Merge pull request #7270 from hashicorp/go_mod_vendor
make go mod manage deps
This commit is contained in:
commit
09d2418552
|
@ -10,3 +10,16 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
- run: make ci
|
||||
check-vendor-vs-mod:
|
||||
docker:
|
||||
- image: circleci/golang:1.12
|
||||
working_directory: /go/src/github.com/hashicorp/packer
|
||||
steps:
|
||||
- checkout
|
||||
- run: make check-vendor-vs-mod
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_check_vendor_vs_module:
|
||||
jobs:
|
||||
- build
|
||||
- check-vendor-vs-mod
|
|
@ -212,22 +212,22 @@ project. After following the steps in "Setting up Go to work on Packer":
|
|||
This way you can push to your fork to create a PR, but the code on disk still
|
||||
lives in the spot where the go cli tools are expecting to find it.
|
||||
|
||||
#### Govendor
|
||||
#### Go modules & go vendor
|
||||
|
||||
If you are submitting a change that requires new or updated dependencies, please
|
||||
include them in `vendor/vendor.json` and in the `vendor/` folder. This helps
|
||||
everything get tested properly in CI.
|
||||
If you are submitting a change that requires new or updated dependencies,
|
||||
please include them in `go.mod`/`go.sum` and in the `vendor/` folder. This
|
||||
helps everything get tested properly in CI.
|
||||
|
||||
Note that you will need to use [govendor](https://github.com/kardianos/govendor)
|
||||
to do this. This step is recommended but not required; if you don't use govendor
|
||||
please indicate in your PR which dependencies have changed and to what versions.
|
||||
Note that you will need to use [go
|
||||
mod](https://github.com/golang/go/wiki/Modules) to do this. This step is
|
||||
recommended but not required.
|
||||
|
||||
Use `govendor fetch <project>` to add dependencies to the project. See
|
||||
[govendor quick start](https://github.com/kardianos/govendor#quick-start-also-see-the-faq)
|
||||
for examples.
|
||||
Use `go get <project>` to add dependencies to the project and `go mod vendor`
|
||||
to make vendored copy of dependencies. See [go mod quick
|
||||
start](https://github.com/golang/go/wiki/Modules#quick-start) for examples.
|
||||
|
||||
Please only apply the minimal vendor changes to get your PR to work. Packer does
|
||||
not attempt to track the latest version for each dependency.
|
||||
Please only apply the minimal vendor changes to get your PR to work. Packer
|
||||
does not attempt to track the latest version for each dependency.
|
||||
|
||||
#### Running Unit Tests
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
env:
|
||||
- USER=travis
|
||||
- USER=travis GO111MODULE=off
|
||||
|
||||
os:
|
||||
- windows
|
||||
|
|
16
Makefile
16
Makefile
|
@ -32,7 +32,7 @@ release: deps test releasebin package ## Build a release build
|
|||
bin: deps ## Build debug/test build
|
||||
@go get github.com/mitchellh/gox
|
||||
@echo "WARN: 'make bin' is for debug / test builds only. Use 'make release' for release builds."
|
||||
@sh -c "$(CURDIR)/scripts/build.sh"
|
||||
@GO111MODULE=off sh -c "$(CURDIR)/scripts/build.sh"
|
||||
|
||||
releasebin: deps
|
||||
@go get github.com/mitchellh/gox
|
||||
|
@ -40,7 +40,7 @@ releasebin: deps
|
|||
echo "ERROR: You must remove prerelease tags from version/version.go prior to release."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@sh -c "$(CURDIR)/scripts/build.sh"
|
||||
@GO111MODULE=off sh -c "$(CURDIR)/scripts/build.sh"
|
||||
|
||||
package:
|
||||
$(if $(VERSION),,@echo 'VERSION= needed to release; Use make package skip compilation'; exit 1)
|
||||
|
@ -50,7 +50,6 @@ deps:
|
|||
@go get golang.org/x/tools/cmd/goimports
|
||||
@go get golang.org/x/tools/cmd/stringer
|
||||
@go get -u github.com/mna/pigeon
|
||||
@go get github.com/kardianos/govendor
|
||||
|
||||
dev: deps ## Build and install a development build
|
||||
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
|
||||
|
@ -110,10 +109,17 @@ testacc: deps generate ## Run acceptance tests
|
|||
PACKER_ACC=1 go test -v $(TEST) $(TESTARGS) -timeout=45m
|
||||
|
||||
testrace: fmt-check mode-check vet ## Test with race detection enabled
|
||||
@go test -race $(TEST) $(TESTARGS) -timeout=3m -p=8
|
||||
@GO111MODULE=off go test -race $(TEST) $(TESTARGS) -timeout=3m -p=8
|
||||
|
||||
check-vendor-vs-mod:
|
||||
@GO111MODULE=on go mod vendor
|
||||
@git diff --exit-code --ignore-space-change --ignore-space-at-eol -- vendor ; if [ $$? -eq 1 ]; then \
|
||||
echo "ERROR: vendor dir is not on par with go modules definition." && \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
updatedeps:
|
||||
@echo "INFO: Packer deps are managed by govendor. See .github/CONTRIBUTING.md"
|
||||
@echo "INFO: Packer deps are managed by go modules. See .github/CONTRIBUTING.md"
|
||||
|
||||
vet: ## Vet Go code
|
||||
@go vet $(VET) ; if [ $$? -eq 1 ]; then \
|
||||
|
|
|
@ -16,6 +16,7 @@ environment:
|
|||
clone_folder: c:\gopath\src\github.com\hashicorp\packer
|
||||
|
||||
install:
|
||||
- set GO111MODULE=off
|
||||
- echo %Path%
|
||||
- go version
|
||||
- go env
|
||||
|
|
|
@ -102,7 +102,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
// set default copy file if we're not giving our own
|
||||
if b.config.CopyFiles == nil {
|
||||
b.config.CopyFiles = make([]string, 0)
|
||||
if !b.config.FromScratch {
|
||||
b.config.CopyFiles = []string{"/etc/resolv.conf"}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,8 @@ func TestBuilderPrepare_CopyFilesNoDefault(t *testing.T) {
|
|||
}
|
||||
|
||||
if len(b.config.CopyFiles) > 0 {
|
||||
t.Errorf("Was expecting no default value for copy_files.")
|
||||
t.Errorf("Was expecting no default value for copy_files. Found %v",
|
||||
b.config.CopyFiles)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/hashicorp/packer/common/iochan"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/mitchellh/iochan"
|
||||
)
|
||||
|
||||
func runAndStream(cmd *exec.Cmd, ui packer.Ui) error {
|
||||
|
@ -40,8 +40,8 @@ func runAndStream(cmd *exec.Cmd, ui packer.Ui) error {
|
|||
|
||||
// Create the channels we'll use for data
|
||||
exitCh := make(chan int, 1)
|
||||
stdoutCh := iochan.DelimReader(stdout_r, '\n')
|
||||
stderrCh := iochan.DelimReader(stderr_r, '\n')
|
||||
stdoutCh := iochan.LineReader(stdout_r)
|
||||
stderrCh := iochan.LineReader(stderr_r)
|
||||
|
||||
// Start the goroutine to watch for the exit
|
||||
go func() {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type Artifact struct {
|
||||
templateID int
|
||||
proxmoxClient *proxmox.Client
|
||||
}
|
||||
|
||||
// Artifact implements packer.Artifact
|
||||
var _ packer.Artifact = &Artifact{}
|
||||
|
||||
func (*Artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (*Artifact) Files() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return strconv.Itoa(a.templateID)
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
return fmt.Sprintf("A template was created: %d", a.templateID)
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Destroy() error {
|
||||
log.Printf("Destroying template: %d", a.templateID)
|
||||
_, err := a.proxmoxClient.DeleteVm(proxmox.NewVmRef(a.templateID))
|
||||
return err
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
)
|
||||
|
||||
type proxmoxDriver struct {
|
||||
client commandTyper
|
||||
vmRef *proxmox.VmRef
|
||||
specialMap map[string]string
|
||||
runeMap map[rune]string
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func NewProxmoxDriver(c commandTyper, vmRef *proxmox.VmRef, interval time.Duration) *proxmoxDriver {
|
||||
// Mappings for packer shorthand to qemu qkeycodes
|
||||
sMap := map[string]string{
|
||||
"spacebar": "spc",
|
||||
"bs": "backspace",
|
||||
"del": "delete",
|
||||
"return": "ret",
|
||||
"enter": "ret",
|
||||
"pageUp": "pgup",
|
||||
"pageDown": "pgdn",
|
||||
}
|
||||
// Mappings for runes that need to be translated to special qkeycodes
|
||||
// Taken from https://github.com/qemu/qemu/blob/master/pc-bios/keymaps/en-us
|
||||
rMap := map[rune]string{
|
||||
// Clean mappings
|
||||
' ': "spc",
|
||||
'.': "dot",
|
||||
',': "comma",
|
||||
';': "semicolon",
|
||||
'*': "asterisk",
|
||||
'-': "minus",
|
||||
'[': "bracket_left",
|
||||
']': "bracket_right",
|
||||
'=': "equal",
|
||||
'\'': "apostrophe",
|
||||
'`': "grave_accent",
|
||||
'/': "slash",
|
||||
'\\': "backslash",
|
||||
|
||||
'!': "shift-1", // "exclam"
|
||||
'@': "shift-2", // "at"
|
||||
'#': "shift-3", // "numbersign"
|
||||
'$': "shift-4", // "dollar"
|
||||
'%': "shift-5", // "percent"
|
||||
'^': "shift-6", // "asciicircum"
|
||||
'&': "shift-7", // "ampersand"
|
||||
'(': "shift-9", // "parenleft"
|
||||
')': "shift-0", // "parenright"
|
||||
'{': "shift-bracket_left", // "braceleft"
|
||||
'}': "shift-bracket_right", // "braceright"
|
||||
'"': "shift-apostrophe", // "quotedbl"
|
||||
'+': "shift-equal", // "plus"
|
||||
'_': "shift-minus", // "underscore"
|
||||
':': "shift-semicolon", // "colon"
|
||||
'<': "shift-comma", // "less" is recognized, but seem to map to '/'?
|
||||
'>': "shift-dot", // "greater"
|
||||
'~': "shift-grave_accent", // "asciitilde"
|
||||
'?': "shift-slash", // "question"
|
||||
'|': "shift-backslash", // "bar"
|
||||
}
|
||||
|
||||
return &proxmoxDriver{
|
||||
client: c,
|
||||
vmRef: vmRef,
|
||||
specialMap: sMap,
|
||||
runeMap: rMap,
|
||||
interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *proxmoxDriver) SendKey(key rune, action bootcommand.KeyAction) error {
|
||||
if special, ok := p.runeMap[key]; ok {
|
||||
return p.send(special)
|
||||
}
|
||||
|
||||
var keys string
|
||||
if unicode.IsUpper(key) {
|
||||
keys = fmt.Sprintf("shift-%c", unicode.ToLower(key))
|
||||
} else {
|
||||
keys = fmt.Sprintf("%c", key)
|
||||
}
|
||||
|
||||
return p.send(keys)
|
||||
}
|
||||
|
||||
func (p *proxmoxDriver) SendSpecial(special string, action bootcommand.KeyAction) error {
|
||||
keys := special
|
||||
if replacement, ok := p.specialMap[special]; ok {
|
||||
keys = replacement
|
||||
}
|
||||
|
||||
return p.send(keys)
|
||||
}
|
||||
|
||||
func (p *proxmoxDriver) send(keys string) error {
|
||||
res, err := p.client.MonitorCmd(p.vmRef, "sendkey "+keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data, ok := res["data"].(string); ok && len(data) > 0 {
|
||||
return fmt.Errorf("failed to send keys: %s", data)
|
||||
}
|
||||
|
||||
time.Sleep(p.interval)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *proxmoxDriver) Flush() error { return nil }
|
|
@ -1,122 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// The unique id for the builder
|
||||
const BuilderId = "proxmox.builder"
|
||||
|
||||
type Builder struct {
|
||||
config Config
|
||||
runner multistep.Runner
|
||||
proxmoxClient *proxmox.Client
|
||||
}
|
||||
|
||||
// Builder implements packer.Builder
|
||||
var _ packer.Builder = &Builder{}
|
||||
|
||||
var pluginVersion = "1.0.0"
|
||||
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||
config, warnings, errs := NewConfig(raws...)
|
||||
if errs != nil {
|
||||
return warnings, errs
|
||||
}
|
||||
b.config = *config
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||
var err error
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: b.config.SkipCertValidation,
|
||||
}
|
||||
b.proxmoxClient, err = proxmox.NewClient(b.config.ProxmoxURL.String(), nil, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = b.proxmoxClient.Login(b.config.Username, b.config.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set up the state
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("proxmoxClient", b.proxmoxClient)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&stepStartVM{},
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
},
|
||||
&stepTypeBootCommand{
|
||||
BootConfig: b.config.BootConfig,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
Host: getVMIP,
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&common.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
},
|
||||
&stepConvertToTemplate{},
|
||||
&stepFinalizeTemplateConfig{},
|
||||
&stepSuccess{},
|
||||
}
|
||||
// Run the steps
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(ctx, state)
|
||||
// If there was an error, return that
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
artifact := &Artifact{
|
||||
templateID: state.Get("template_id").(int),
|
||||
proxmoxClient: b.proxmoxClient,
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func getVMIP(state multistep.StateBag) (string, error) {
|
||||
c := state.Get("proxmoxClient").(*proxmox.Client)
|
||||
vmRef := state.Get("vmRef").(*proxmox.VmRef)
|
||||
|
||||
ifs, err := c.GetVmAgentNetworkInterfaces(vmRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO: Do something smarter here? Allow specifying interface? Or address family?
|
||||
// For now, just go for first non-loopback
|
||||
for _, iface := range ifs {
|
||||
for _, addr := range iface.IPAddresses {
|
||||
if addr.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
return addr.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Found no IP addresses on VM")
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
RawBootKeyInterval string `mapstructure:"boot_key_interval"`
|
||||
BootKeyInterval time.Duration ``
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
ProxmoxURLRaw string `mapstructure:"proxmox_url"`
|
||||
ProxmoxURL *url.URL
|
||||
SkipCertValidation bool `mapstructure:"insecure_skip_tls_verify"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
Node string `mapstructure:"node"`
|
||||
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
VMID int `mapstructure:"vm_id"`
|
||||
|
||||
Memory int `mapstructure:"memory"`
|
||||
Cores int `mapstructure:"cores"`
|
||||
Sockets int `mapstructure:"sockets"`
|
||||
OS string `mapstructure:"os"`
|
||||
NICs []nicConfig `mapstructure:"network_adapters"`
|
||||
Disks []diskConfig `mapstructure:"disks"`
|
||||
ISOFile string `mapstructure:"iso_file"`
|
||||
|
||||
TemplateName string `mapstructure:"template_name"`
|
||||
TemplateDescription string `mapstructure:"template_description"`
|
||||
UnmountISO bool `mapstructure:"unmount_iso"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type nicConfig struct {
|
||||
Model string `mapstructure:"model"`
|
||||
MACAddress string `mapstructure:"mac_address"`
|
||||
Bridge string `mapstructure:"bridge"`
|
||||
VLANTag string `mapstructure:"vlan_tag"`
|
||||
}
|
||||
type diskConfig struct {
|
||||
Type string `mapstructure:"type"`
|
||||
StoragePool string `mapstructure:"storage_pool"`
|
||||
StoragePoolType string `mapstructure:"storage_pool_type"`
|
||||
Size string `mapstructure:"disk_size"`
|
||||
CacheMode string `mapstructure:"cache_mode"`
|
||||
DiskFormat string `mapstructure:"format"`
|
||||
}
|
||||
|
||||
func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||
c := new(Config)
|
||||
|
||||
var md mapstructure.Metadata
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Metadata: &md,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var errs *packer.MultiError
|
||||
|
||||
// Defaults
|
||||
if c.ProxmoxURLRaw == "" {
|
||||
c.ProxmoxURLRaw = os.Getenv("PROXMOX_URL")
|
||||
}
|
||||
if c.Username == "" {
|
||||
c.Username = os.Getenv("PROXMOX_USERNAME")
|
||||
}
|
||||
if c.Password == "" {
|
||||
c.Password = os.Getenv("PROXMOX_PASSWORD")
|
||||
}
|
||||
if c.RawBootKeyInterval == "" {
|
||||
c.RawBootKeyInterval = os.Getenv(common.PackerKeyEnv)
|
||||
}
|
||||
if c.RawBootKeyInterval == "" {
|
||||
c.BootKeyInterval = common.PackerKeyDefault
|
||||
} else {
|
||||
if interval, err := time.ParseDuration(c.RawBootKeyInterval); err == nil {
|
||||
c.BootKeyInterval = interval
|
||||
} else {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Could not parse boot_key_interval: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
if c.VMName == "" {
|
||||
// Default to packer-[time-ordered-uuid]
|
||||
c.VMName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
if c.Memory < 16 {
|
||||
log.Printf("Memory %d is too small, using default: 512", c.Memory)
|
||||
c.Memory = 512
|
||||
}
|
||||
if c.Cores < 1 {
|
||||
log.Printf("Number of cores %d is too small, using default: 1", c.Cores)
|
||||
c.Cores = 1
|
||||
}
|
||||
if c.Sockets < 1 {
|
||||
log.Printf("Number of sockets %d is too small, using default: 1", c.Sockets)
|
||||
c.Sockets = 1
|
||||
}
|
||||
if c.OS == "" {
|
||||
log.Printf("OS not set, using default 'other'")
|
||||
c.OS = "other"
|
||||
}
|
||||
for idx := range c.NICs {
|
||||
if c.NICs[idx].Model == "" {
|
||||
log.Printf("NIC %d model not set, using default 'e1000'", idx)
|
||||
c.NICs[idx].Model = "e1000"
|
||||
}
|
||||
}
|
||||
for idx := range c.Disks {
|
||||
if c.Disks[idx].Type == "" {
|
||||
log.Printf("Disk %d type not set, using default 'scsi'", idx)
|
||||
c.Disks[idx].Type = "scsi"
|
||||
}
|
||||
if c.Disks[idx].Size == "" {
|
||||
log.Printf("Disk %d size not set, using default '20G'", idx)
|
||||
c.Disks[idx].Size = "20G"
|
||||
}
|
||||
if c.Disks[idx].CacheMode == "" {
|
||||
log.Printf("Disk %d cache mode not set, using default 'none'", idx)
|
||||
c.Disks[idx].CacheMode = "none"
|
||||
}
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
|
||||
// Required configurations that will display errors if not set
|
||||
if c.Username == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("username must be specified"))
|
||||
}
|
||||
if c.Password == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("password must be specified"))
|
||||
}
|
||||
if c.ProxmoxURLRaw == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("proxmox_url must be specified"))
|
||||
}
|
||||
if c.ProxmoxURL, err = url.Parse(c.ProxmoxURLRaw); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("Could not parse proxmox_url: %s", err)))
|
||||
}
|
||||
if c.ISOFile == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("iso_file must be specified"))
|
||||
}
|
||||
if c.Node == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("node must be specified"))
|
||||
}
|
||||
for idx := range c.NICs {
|
||||
if c.NICs[idx].Bridge == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("network_adapters[%d].bridge must be specified", idx)))
|
||||
}
|
||||
}
|
||||
for idx := range c.Disks {
|
||||
if c.Disks[idx].StoragePool == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disks[%d].storage_pool must be specified", idx)))
|
||||
}
|
||||
if c.Disks[idx].StoragePoolType == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disks[%d].storage_pool_type must be specified", idx)))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
packer.LogSecretFilter.Set(c.Password)
|
||||
return c, nil, nil
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template"
|
||||
)
|
||||
|
||||
func TestRequiredParameters(t *testing.T) {
|
||||
_, _, err := NewConfig(make(map[string]interface{}))
|
||||
if err == nil {
|
||||
t.Fatal("Expected empty configuration to fail")
|
||||
}
|
||||
errs, ok := err.(*packer.MultiError)
|
||||
if !ok {
|
||||
t.Fatal("Expected errors to be packer.MultiError")
|
||||
}
|
||||
|
||||
required := []string{"username", "password", "proxmox_url", "iso_file", "node", "ssh_username"}
|
||||
for _, param := range required {
|
||||
found := false
|
||||
for _, err := range errs.Errors {
|
||||
if strings.Contains(err.Error(), param) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected error about missing parameter %q", required)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicExampleFromDocsIsValid(t *testing.T) {
|
||||
const config = `{
|
||||
"builders": [
|
||||
{
|
||||
"type": "proxmox",
|
||||
"proxmox_url": "https://my-proxmox.my-domain:8006/api2/json",
|
||||
"insecure_skip_tls_verify": true,
|
||||
"username": "apiuser@pve",
|
||||
"password": "supersecret",
|
||||
|
||||
"node": "my-proxmox",
|
||||
"network_adapters": [
|
||||
{
|
||||
"bridge": "vmbr0"
|
||||
}
|
||||
],
|
||||
"disks": [
|
||||
{
|
||||
"type": "scsi",
|
||||
"disk_size": "5G",
|
||||
"storage_pool": "local-lvm",
|
||||
"storage_pool_type": "lvm"
|
||||
}
|
||||
],
|
||||
|
||||
"iso_file": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso",
|
||||
"http_directory":"config",
|
||||
"boot_wait": "10s",
|
||||
"boot_command": [
|
||||
"<up><tab> ip=dhcp inst.cmdline inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg<enter>"
|
||||
],
|
||||
|
||||
"ssh_username": "root",
|
||||
"ssh_timeout": "15m",
|
||||
"ssh_password": "packer",
|
||||
|
||||
"unmount_iso": true,
|
||||
"template_name": "fedora-29",
|
||||
"template_description": "Fedora 29-1.2, generated on {{ isotime \"2006-01-02T15:04:05Z\" }}"
|
||||
}
|
||||
]
|
||||
}`
|
||||
tpl, err := template.Parse(strings.NewReader(config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b := &Builder{}
|
||||
warn, err := b.Prepare(tpl.Builders["proxmox"].Config)
|
||||
if err != nil {
|
||||
t.Fatal(err, warn)
|
||||
}
|
||||
|
||||
// The example config does not set a number of optional fields. Validate that:
|
||||
// Memory 0 is too small, using default: 512
|
||||
// Number of cores 0 is too small, using default: 1
|
||||
// Number of sockets 0 is too small, using default: 1
|
||||
// OS not set, using default 'other'
|
||||
// NIC 0 model not set, using default 'e1000'
|
||||
// Disk 0 cache mode not set, using default 'none'
|
||||
|
||||
if b.config.Memory != 512 {
|
||||
t.Errorf("Expected Memory to be 512, got %d", b.config.Memory)
|
||||
}
|
||||
if b.config.Cores != 1 {
|
||||
t.Errorf("Expected Cores to be 1, got %d", b.config.Cores)
|
||||
}
|
||||
if b.config.Sockets != 1 {
|
||||
t.Errorf("Expected Sockets to be 1, got %d", b.config.Sockets)
|
||||
}
|
||||
if b.config.OS != "other" {
|
||||
t.Errorf("Expected OS to be 'other', got %s", b.config.OS)
|
||||
}
|
||||
if b.config.NICs[0].Model != "e1000" {
|
||||
t.Errorf("Expected NIC model to be 'e1000', got %s", b.config.NICs[0].Model)
|
||||
}
|
||||
if b.config.Disks[0].CacheMode != "none" {
|
||||
t.Errorf("Expected disk cache mode to be 'none', got %s", b.config.Disks[0].CacheMode)
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// stepConvertToTemplate takes the running VM configured in earlier steps, stops it, and
|
||||
// converts it into a Proxmox template.
|
||||
//
|
||||
// It sets the template_id state which is used for Artifact lookup.
|
||||
type stepConvertToTemplate struct{}
|
||||
|
||||
type templateConverter interface {
|
||||
ShutdownVm(*proxmox.VmRef) (string, error)
|
||||
CreateTemplate(*proxmox.VmRef) error
|
||||
}
|
||||
|
||||
var _ templateConverter = &proxmox.Client{}
|
||||
|
||||
func (s *stepConvertToTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("proxmoxClient").(templateConverter)
|
||||
vmRef := state.Get("vmRef").(*proxmox.VmRef)
|
||||
|
||||
ui.Say("Stopping VM")
|
||||
_, err := client.ShutdownVm(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error converting VM to template, could not stop: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Converting VM to template")
|
||||
err = client.CreateTemplate(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error converting VM to template: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("template_id", vmRef.VmId())
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepConvertToTemplate) Cleanup(state multistep.StateBag) {}
|
|
@ -1,103 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type converterMock struct {
|
||||
shutdownVm func(*proxmox.VmRef) (string, error)
|
||||
createTemplate func(*proxmox.VmRef) error
|
||||
}
|
||||
|
||||
func (m converterMock) ShutdownVm(r *proxmox.VmRef) (string, error) {
|
||||
return m.shutdownVm(r)
|
||||
}
|
||||
func (m converterMock) CreateTemplate(r *proxmox.VmRef) error {
|
||||
return m.createTemplate(r)
|
||||
}
|
||||
|
||||
var _ templateConverter = converterMock{}
|
||||
|
||||
func TestConvertToTemplate(t *testing.T) {
|
||||
cs := []struct {
|
||||
name string
|
||||
shutdownErr error
|
||||
expectCallCreateTemplate bool
|
||||
createTemplateErr error
|
||||
expectedAction multistep.StepAction
|
||||
expectTemplateIdSet bool
|
||||
}{
|
||||
{
|
||||
name: "no errors returns continue and sets template id",
|
||||
expectCallCreateTemplate: true,
|
||||
expectedAction: multistep.ActionContinue,
|
||||
expectTemplateIdSet: true,
|
||||
},
|
||||
{
|
||||
name: "when shutdown fails, don't try to create template and halt",
|
||||
shutdownErr: fmt.Errorf("failed to stop vm"),
|
||||
expectCallCreateTemplate: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
expectTemplateIdSet: false,
|
||||
},
|
||||
{
|
||||
name: "when create template fails, halt",
|
||||
expectCallCreateTemplate: true,
|
||||
createTemplateErr: fmt.Errorf("failed to stop vm"),
|
||||
expectedAction: multistep.ActionHalt,
|
||||
expectTemplateIdSet: false,
|
||||
},
|
||||
}
|
||||
|
||||
const vmid = 123
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
converter := converterMock{
|
||||
shutdownVm: func(r *proxmox.VmRef) (string, error) {
|
||||
if r.VmId() != vmid {
|
||||
t.Errorf("ShutdownVm called with unexpected id, expected %d, got %d", vmid, r.VmId())
|
||||
}
|
||||
return "", c.shutdownErr
|
||||
},
|
||||
createTemplate: func(r *proxmox.VmRef) error {
|
||||
if r.VmId() != vmid {
|
||||
t.Errorf("CreateTemplate called with unexpected id, expected %d, got %d", vmid, r.VmId())
|
||||
}
|
||||
if !c.expectCallCreateTemplate {
|
||||
t.Error("Did not expect CreateTemplate to be called")
|
||||
}
|
||||
|
||||
return c.createTemplateErr
|
||||
},
|
||||
}
|
||||
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packer.TestUi(t))
|
||||
state.Put("vmRef", proxmox.NewVmRef(vmid))
|
||||
state.Put("proxmoxClient", converter)
|
||||
|
||||
step := stepConvertToTemplate{}
|
||||
action := step.Run(context.TODO(), state)
|
||||
if action != c.expectedAction {
|
||||
t.Errorf("Expected action to be %v, got %v", c.expectedAction, action)
|
||||
}
|
||||
|
||||
id, wasSet := state.GetOk("template_id")
|
||||
|
||||
if c.expectTemplateIdSet != wasSet {
|
||||
t.Errorf("Expected template_id state present=%v was present=%v", c.expectTemplateIdSet, wasSet)
|
||||
}
|
||||
|
||||
if c.expectTemplateIdSet && id != vmid {
|
||||
t.Errorf("Expected template_id state to be set to %d, got %v", vmid, id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// stepFinalizeTemplateConfig does any required modifications to the configuration _after_
|
||||
// the VM has been converted into a template, such as updating name and description, or
|
||||
// unmounting the installation ISO.
|
||||
type stepFinalizeTemplateConfig struct{}
|
||||
|
||||
type templateFinalizer interface {
|
||||
GetVmConfig(*proxmox.VmRef) (map[string]interface{}, error)
|
||||
SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
var _ templateFinalizer = &proxmox.Client{}
|
||||
|
||||
func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("proxmoxClient").(templateFinalizer)
|
||||
c := state.Get("config").(*Config)
|
||||
vmRef := state.Get("vmRef").(*proxmox.VmRef)
|
||||
|
||||
changes := make(map[string]interface{})
|
||||
|
||||
if c.TemplateName != "" {
|
||||
changes["name"] = c.TemplateName
|
||||
}
|
||||
|
||||
// During build, the description is "Packer ephemeral build VM", so if no description is
|
||||
// set, we need to clear it
|
||||
changes["description"] = c.TemplateDescription
|
||||
|
||||
if c.UnmountISO {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if vmParams["ide2"] == nil || !strings.HasSuffix(vmParams["ide2"].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, ide2 is not present, or not a cdrom media")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes["ide2"] = "none,media=cdrom"
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error updating template: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepFinalizeTemplateConfig) Cleanup(state multistep.StateBag) {}
|
|
@ -1,151 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type finalizerMock struct {
|
||||
getConfig func() (map[string]interface{}, error)
|
||||
setConfig func(map[string]interface{}) (string, error)
|
||||
}
|
||||
|
||||
func (m finalizerMock) GetVmConfig(*proxmox.VmRef) (map[string]interface{}, error) {
|
||||
return m.getConfig()
|
||||
}
|
||||
func (m finalizerMock) SetVmConfig(vmref *proxmox.VmRef, c map[string]interface{}) (interface{}, error) {
|
||||
return m.setConfig(c)
|
||||
}
|
||||
|
||||
var _ templateFinalizer = finalizerMock{}
|
||||
|
||||
func TestTemplateFinalize(t *testing.T) {
|
||||
cs := []struct {
|
||||
name string
|
||||
builderConfig *Config
|
||||
initialVMConfig map[string]interface{}
|
||||
getConfigErr error
|
||||
expectCallSetConfig bool
|
||||
expectedVMConfig map[string]interface{}
|
||||
setConfigErr error
|
||||
expectedAction multistep.StepAction
|
||||
}{
|
||||
{
|
||||
name: "empty config changes only description",
|
||||
builderConfig: &Config{},
|
||||
initialVMConfig: map[string]interface{}{
|
||||
"name": "dummy",
|
||||
"description": "Packer ephemeral build VM",
|
||||
"ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom",
|
||||
},
|
||||
expectCallSetConfig: true,
|
||||
expectedVMConfig: map[string]interface{}{
|
||||
"name": nil,
|
||||
"description": "",
|
||||
"ide2": nil,
|
||||
},
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "all options",
|
||||
builderConfig: &Config{
|
||||
TemplateName: "my-template",
|
||||
TemplateDescription: "some-description",
|
||||
UnmountISO: true,
|
||||
},
|
||||
initialVMConfig: map[string]interface{}{
|
||||
"name": "dummy",
|
||||
"description": "Packer ephemeral build VM",
|
||||
"ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom",
|
||||
},
|
||||
expectCallSetConfig: true,
|
||||
expectedVMConfig: map[string]interface{}{
|
||||
"name": "my-template",
|
||||
"description": "some-description",
|
||||
"ide2": "none,media=cdrom",
|
||||
},
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "no cd-drive with unmount=true should returns halt",
|
||||
builderConfig: &Config{
|
||||
TemplateName: "my-template",
|
||||
TemplateDescription: "some-description",
|
||||
UnmountISO: true,
|
||||
},
|
||||
initialVMConfig: map[string]interface{}{
|
||||
"name": "dummy",
|
||||
"description": "Packer ephemeral build VM",
|
||||
"ide1": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom",
|
||||
},
|
||||
expectCallSetConfig: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
name: "GetVmConfig error should return halt",
|
||||
builderConfig: &Config{
|
||||
TemplateName: "my-template",
|
||||
TemplateDescription: "some-description",
|
||||
UnmountISO: true,
|
||||
},
|
||||
getConfigErr: fmt.Errorf("some error"),
|
||||
expectCallSetConfig: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
name: "SetVmConfig error should return halt",
|
||||
builderConfig: &Config{
|
||||
TemplateName: "my-template",
|
||||
TemplateDescription: "some-description",
|
||||
UnmountISO: true,
|
||||
},
|
||||
initialVMConfig: map[string]interface{}{
|
||||
"name": "dummy",
|
||||
"description": "Packer ephemeral build VM",
|
||||
"ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom",
|
||||
},
|
||||
expectCallSetConfig: true,
|
||||
setConfigErr: fmt.Errorf("some error"),
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
finalizer := finalizerMock{
|
||||
getConfig: func() (map[string]interface{}, error) {
|
||||
return c.initialVMConfig, c.getConfigErr
|
||||
},
|
||||
setConfig: func(cfg map[string]interface{}) (string, error) {
|
||||
if !c.expectCallSetConfig {
|
||||
t.Error("Did not expect SetVmConfig to be called")
|
||||
}
|
||||
for key, val := range c.expectedVMConfig {
|
||||
if cfg[key] != val {
|
||||
t.Errorf("Expected %q to be %q, got %q", key, val, cfg[key])
|
||||
}
|
||||
}
|
||||
|
||||
return "", c.setConfigErr
|
||||
},
|
||||
}
|
||||
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packer.TestUi(t))
|
||||
state.Put("config", c.builderConfig)
|
||||
state.Put("vmRef", proxmox.NewVmRef(1))
|
||||
state.Put("proxmoxClient", finalizer)
|
||||
|
||||
step := stepFinalizeTemplateConfig{}
|
||||
action := step.Run(context.TODO(), state)
|
||||
if action != c.expectedAction {
|
||||
t.Errorf("Expected action to be %v, got %v", c.expectedAction, action)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// stepStartVM takes the given configuration and starts a VM on the given Proxmox node.
|
||||
//
|
||||
// It sets the vmRef state which is used throughout the later steps to reference the VM
|
||||
// in API calls.
|
||||
type stepStartVM struct{}
|
||||
|
||||
func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("proxmoxClient").(*proxmox.Client)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
ui.Say("Creating VM")
|
||||
config := proxmox.ConfigQemu{
|
||||
Name: c.VMName,
|
||||
Agent: "1",
|
||||
Description: "Packer ephemeral build VM",
|
||||
Memory: c.Memory,
|
||||
QemuCores: c.Cores,
|
||||
QemuSockets: c.Sockets,
|
||||
QemuOs: c.OS,
|
||||
QemuIso: c.ISOFile,
|
||||
QemuNetworks: generateProxmoxNetworkAdapters(c.NICs),
|
||||
QemuDisks: generateProxmoxDisks(c.Disks),
|
||||
}
|
||||
|
||||
if c.VMID == 0 {
|
||||
ui.Say("No VM ID given, getting next free from Proxmox")
|
||||
for n := 0; n < 5; n++ {
|
||||
id, err := proxmox.MaxVmId(client)
|
||||
if err != nil {
|
||||
log.Printf("Error getting max used VM ID: %v (attempt %d/5)", err, n+1)
|
||||
continue
|
||||
}
|
||||
c.VMID = id + 1
|
||||
break
|
||||
}
|
||||
if c.VMID == 0 {
|
||||
err := fmt.Errorf("Failed to get free VM ID")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
vmRef := proxmox.NewVmRef(c.VMID)
|
||||
vmRef.SetNode(c.Node)
|
||||
|
||||
err := config.CreateVm(vmRef, client)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Store the vm id for later
|
||||
state.Put("vmRef", vmRef)
|
||||
|
||||
ui.Say("Starting VM")
|
||||
_, err = client.StartVm(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error starting VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func generateProxmoxNetworkAdapters(nics []nicConfig) proxmox.QemuDevices {
|
||||
devs := make(proxmox.QemuDevices)
|
||||
for idx := range nics {
|
||||
devs[idx] = make(proxmox.QemuDevice)
|
||||
setDeviceParamIfDefined(devs[idx], "model", nics[idx].Model)
|
||||
setDeviceParamIfDefined(devs[idx], "macaddr", nics[idx].MACAddress)
|
||||
setDeviceParamIfDefined(devs[idx], "bridge", nics[idx].Bridge)
|
||||
setDeviceParamIfDefined(devs[idx], "tag", nics[idx].VLANTag)
|
||||
}
|
||||
return devs
|
||||
}
|
||||
func generateProxmoxDisks(disks []diskConfig) proxmox.QemuDevices {
|
||||
devs := make(proxmox.QemuDevices)
|
||||
for idx := range disks {
|
||||
devs[idx] = make(proxmox.QemuDevice)
|
||||
setDeviceParamIfDefined(devs[idx], "type", disks[idx].Type)
|
||||
setDeviceParamIfDefined(devs[idx], "size", disks[idx].Size)
|
||||
setDeviceParamIfDefined(devs[idx], "storage", disks[idx].StoragePool)
|
||||
setDeviceParamIfDefined(devs[idx], "storage_type", disks[idx].StoragePoolType)
|
||||
setDeviceParamIfDefined(devs[idx], "cache", disks[idx].CacheMode)
|
||||
setDeviceParamIfDefined(devs[idx], "format", disks[idx].DiskFormat)
|
||||
}
|
||||
return devs
|
||||
}
|
||||
|
||||
func setDeviceParamIfDefined(dev proxmox.QemuDevice, key, value string) {
|
||||
if value != "" {
|
||||
dev[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
type startedVMCleaner interface {
|
||||
StopVm(*proxmox.VmRef) (string, error)
|
||||
DeleteVm(*proxmox.VmRef) (string, error)
|
||||
}
|
||||
|
||||
var _ startedVMCleaner = &proxmox.Client{}
|
||||
|
||||
func (s *stepStartVM) Cleanup(state multistep.StateBag) {
|
||||
vmRefUntyped, ok := state.GetOk("vmRef")
|
||||
// If not ok, we probably errored out before creating the VM
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
vmRef := vmRefUntyped.(*proxmox.VmRef)
|
||||
|
||||
// The vmRef will actually refer to the created template if everything
|
||||
// finished successfully, so in that case we shouldn't cleanup
|
||||
if _, ok := state.GetOk("success"); ok {
|
||||
return
|
||||
}
|
||||
|
||||
client := state.Get("proxmoxClient").(startedVMCleaner)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// Destroy the server we just created
|
||||
ui.Say("Stopping VM")
|
||||
_, err := client.StopVm(vmRef)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error stop VM. Please stop and delete it manually: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say("Deleting VM")
|
||||
_, err = client.DeleteVm(vmRef)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting VM. Please delete it manually: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type startedVMCleanerMock struct {
|
||||
stopVm func() (string, error)
|
||||
deleteVm func() (string, error)
|
||||
}
|
||||
|
||||
func (m startedVMCleanerMock) StopVm(*proxmox.VmRef) (string, error) {
|
||||
return m.stopVm()
|
||||
}
|
||||
func (m startedVMCleanerMock) DeleteVm(*proxmox.VmRef) (string, error) {
|
||||
return m.deleteVm()
|
||||
}
|
||||
|
||||
var _ startedVMCleaner = &startedVMCleanerMock{}
|
||||
|
||||
func TestCleanupStartVM(t *testing.T) {
|
||||
cs := []struct {
|
||||
name string
|
||||
setVmRef bool
|
||||
setSuccess bool
|
||||
stopVMErr error
|
||||
expectCallStopVM bool
|
||||
deleteVMErr error
|
||||
expectCallDeleteVM bool
|
||||
}{
|
||||
{
|
||||
name: "when vmRef state is not set, nothing should happen",
|
||||
setVmRef: false,
|
||||
expectCallStopVM: false,
|
||||
},
|
||||
{
|
||||
name: "when success state is set, nothing should happen",
|
||||
setVmRef: true,
|
||||
setSuccess: true,
|
||||
expectCallStopVM: false,
|
||||
},
|
||||
{
|
||||
name: "when not successful, vm should be stopped and deleted",
|
||||
setVmRef: true,
|
||||
setSuccess: false,
|
||||
expectCallStopVM: true,
|
||||
expectCallDeleteVM: true,
|
||||
},
|
||||
{
|
||||
name: "if stopping fails, DeleteVm should not be called",
|
||||
setVmRef: true,
|
||||
setSuccess: false,
|
||||
expectCallStopVM: true,
|
||||
stopVMErr: fmt.Errorf("some error"),
|
||||
expectCallDeleteVM: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var stopWasCalled, deleteWasCalled bool
|
||||
|
||||
cleaner := startedVMCleanerMock{
|
||||
stopVm: func() (string, error) {
|
||||
if !c.expectCallStopVM {
|
||||
t.Error("Did not expect StopVm to be called")
|
||||
}
|
||||
|
||||
stopWasCalled = true
|
||||
return "", c.stopVMErr
|
||||
},
|
||||
deleteVm: func() (string, error) {
|
||||
if !c.expectCallDeleteVM {
|
||||
t.Error("Did not expect DeleteVm to be called")
|
||||
}
|
||||
|
||||
deleteWasCalled = true
|
||||
return "", c.deleteVMErr
|
||||
},
|
||||
}
|
||||
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packer.TestUi(t))
|
||||
state.Put("proxmoxClient", cleaner)
|
||||
if c.setVmRef {
|
||||
state.Put("vmRef", proxmox.NewVmRef(1))
|
||||
}
|
||||
if c.setSuccess {
|
||||
state.Put("success", "true")
|
||||
}
|
||||
|
||||
step := stepStartVM{}
|
||||
step.Cleanup(state)
|
||||
|
||||
if c.expectCallStopVM && !stopWasCalled {
|
||||
t.Error("Expected StopVm to be called, but it wasn't")
|
||||
}
|
||||
if c.expectCallDeleteVM && !deleteWasCalled {
|
||||
t.Error("Expected DeleteVm to be called, but it wasn't")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
// stepSuccess runs after the full build has succeeded.
|
||||
//
|
||||
// It sets the success state, which ensures cleanup does not remove the finished template
|
||||
type stepSuccess struct{}
|
||||
|
||||
func (s *stepSuccess) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// We need to ensure stepStartVM.Cleanup doesn't delete the template (no
|
||||
// difference between VMs and templates when deleting)
|
||||
state.Put("success", true)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepSuccess) Cleanup(state multistep.StateBag) {}
|
|
@ -1,119 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
commonhelper "github.com/hashicorp/packer/helper/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// stepTypeBootCommand takes the started VM, and sends the keystrokes required to start
|
||||
// the installation process such that Packer can later reach the VM over SSH/WinRM
|
||||
type stepTypeBootCommand struct {
|
||||
bootcommand.BootConfig
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
}
|
||||
|
||||
type commandTyper interface {
|
||||
MonitorCmd(*proxmox.VmRef, string) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
var _ commandTyper = &proxmox.Client{}
|
||||
|
||||
func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("config").(*Config)
|
||||
client := state.Get("proxmoxClient").(commandTyper)
|
||||
vmRef := state.Get("vmRef").(*proxmox.VmRef)
|
||||
|
||||
if len(s.BootCommand) == 0 {
|
||||
log.Println("No boot command given, skipping")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot", s.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(s.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
httpIP, err := hostIP()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to determine host IP: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
common.SetHTTPIP(httpIP)
|
||||
s.Ctx.Data = &bootCommandTemplateData{
|
||||
HTTPIP: httpIP,
|
||||
HTTPPort: state.Get("http_port").(uint),
|
||||
}
|
||||
|
||||
ui.Say("Typing the boot command")
|
||||
d := NewProxmoxDriver(client, vmRef, c.BootKeyInterval)
|
||||
command, err := interpolate.Render(s.FlatBootCommand(), &s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {
|
||||
commonhelper.RemoveSharedStateFile("ip", "")
|
||||
}
|
||||
|
||||
func hostIP() (string, error) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("No host IP found")
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type commandTyperMock struct {
|
||||
monitorCmd func(*proxmox.VmRef, string) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
func (m commandTyperMock) MonitorCmd(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
|
||||
return m.monitorCmd(ref, cmd)
|
||||
}
|
||||
|
||||
var _ commandTyper = commandTyperMock{}
|
||||
|
||||
func TestTypeBootCommand(t *testing.T) {
|
||||
cs := []struct {
|
||||
name string
|
||||
builderConfig *Config
|
||||
expectCallMonitorCmd bool
|
||||
monitorCmdErr error
|
||||
monitorCmdRet map[string]interface{}
|
||||
expectedKeysSent string
|
||||
expectedAction multistep.StepAction
|
||||
}{
|
||||
{
|
||||
name: "simple boot command is typed",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "hello",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello<enter>world"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "helloretworld",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "merge multiple interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"Hello World 2.0", "foo!bar@baz"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "shift-hellospcshift-worldspc2dot0fooshift-1barshift-2baz",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "without boot command monitorcmd should not be called",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}},
|
||||
expectCallMonitorCmd: false,
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "invalid boot command template function",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}},
|
||||
expectCallMonitorCmd: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
// When proxmox (or Qemu, really) doesn't recognize the keycode we send, we get no error back, but
|
||||
// a map {"data": "invalid parameter: X"}, where X is the keycode.
|
||||
name: "invalid keys sent to proxmox",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"x"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
monitorCmdRet: map[string]interface{}{"data": "invalid parameter: x"},
|
||||
expectedKeysSent: "x",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
name: "error in typing should return halt",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
monitorCmdErr: fmt.Errorf("some error"),
|
||||
expectedKeysSent: "h",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
accumulator := strings.Builder{}
|
||||
typer := commandTyperMock{
|
||||
monitorCmd: func(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
|
||||
if !c.expectCallMonitorCmd {
|
||||
t.Error("Did not expect MonitorCmd to be called")
|
||||
}
|
||||
if !strings.HasPrefix(cmd, "sendkey ") {
|
||||
t.Errorf("Expected all commands to be sendkey, got %s", cmd)
|
||||
}
|
||||
|
||||
accumulator.WriteString(strings.TrimPrefix(cmd, "sendkey "))
|
||||
|
||||
return c.monitorCmdRet, c.monitorCmdErr
|
||||
},
|
||||
}
|
||||
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packer.TestUi(t))
|
||||
state.Put("config", c.builderConfig)
|
||||
state.Put("http_port", uint(0))
|
||||
state.Put("vmRef", proxmox.NewVmRef(1))
|
||||
state.Put("proxmoxClient", typer)
|
||||
|
||||
step := stepTypeBootCommand{
|
||||
c.builderConfig.BootConfig,
|
||||
c.builderConfig.ctx,
|
||||
}
|
||||
action := step.Run(context.TODO(), state)
|
||||
step.Cleanup(state)
|
||||
|
||||
if action != c.expectedAction {
|
||||
t.Errorf("Expected action to be %v, got %v", c.expectedAction, action)
|
||||
}
|
||||
if c.expectedKeysSent != accumulator.String() {
|
||||
t.Errorf("Expected keystrokes to be %q, got %q", c.expectedKeysSent, accumulator.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ import (
|
|||
parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso"
|
||||
parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm"
|
||||
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
|
||||
proxmoxbuilder "github.com/hashicorp/packer/builder/proxmox"
|
||||
qemubuilder "github.com/hashicorp/packer/builder/qemu"
|
||||
scalewaybuilder "github.com/hashicorp/packer/builder/scaleway"
|
||||
tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
|
||||
|
@ -119,7 +118,6 @@ var Builders = map[string]packer.Builder{
|
|||
"parallels-iso": new(parallelsisobuilder.Builder),
|
||||
"parallels-pvm": new(parallelspvmbuilder.Builder),
|
||||
"profitbricks": new(profitbricksbuilder.Builder),
|
||||
"proxmox": new(proxmoxbuilder.Builder),
|
||||
"qemu": new(qemubuilder.Builder),
|
||||
"scaleway": new(scalewaybuilder.Builder),
|
||||
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package iochan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
// LineReader takes an io.Reader and produces the contents of the reader on the
|
||||
// returned channel. Internally bufio.NewScanner is used, io.ScanLines parses
|
||||
// lines and returns them without carriage return. Scan can panic if the split
|
||||
// function returns too many empty tokens without advancing the input.
|
||||
//
|
||||
// The channel will be closed either by reaching the end of the input or an
|
||||
// error.
|
||||
func LineReader(r io.Reader) <-chan string {
|
||||
ch := make(chan string)
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(r)
|
||||
defer close(ch)
|
||||
|
||||
for scanner.Scan() {
|
||||
ch <- scanner.Text()
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package iochan
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLineReader(t *testing.T) {
|
||||
|
||||
data := []string{"foo", "bar", "baz"}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString(strings.Join(data, "\n") + "\n")
|
||||
|
||||
ch := LineReader(buf)
|
||||
|
||||
var result []string
|
||||
expected := data
|
||||
for v := range ch {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Fatalf("unexpected results: %#v", result)
|
||||
}
|
||||
}
|
115
go.mod
115
go.mod
|
@ -3,117 +3,74 @@ module github.com/hashicorp/packer
|
|||
require (
|
||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1
|
||||
github.com/Azure/azure-sdk-for-go v17.3.1+incompatible
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Azure/go-autorest v10.12.0+incompatible
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 // indirect
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290
|
||||
github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 // indirect
|
||||
github.com/Jeffail/gabs v1.1.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.11 // indirect
|
||||
github.com/NYTimes/gziphandler v1.0.1 // indirect
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/SAP/go-hdb v0.13.1 // indirect
|
||||
github.com/SermoDigital/jose v0.9.1 // indirect
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd // indirect
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 // indirect
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6 // indirect
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43
|
||||
github.com/armon/go-metrics v0.0.0-20180713145231-3c58d8115a78 // indirect
|
||||
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.16.24
|
||||
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909 // indirect
|
||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/cenkalti/backoff v2.1.0+incompatible // indirect
|
||||
github.com/cheggaaa/pb v1.0.27
|
||||
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible // indirect
|
||||
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
|
||||
github.com/creack/goselect v0.0.0-20180210034346-528c74964609 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f // indirect
|
||||
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/digitalocean/godo v1.7.3
|
||||
github.com/digitalocean/godo v1.11.1
|
||||
github.com/dnaeon/go-vcr v1.0.0 // indirect
|
||||
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.3.3 // indirect
|
||||
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22 // indirect
|
||||
github.com/docker/docker v0.0.0-20180422163414-57142e89befe // indirect
|
||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/go-ini/ini v1.25.4
|
||||
github.com/go-ldap/ldap v2.5.1+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.4.1 // indirect
|
||||
github.com/go-test/deep v1.0.1 // indirect
|
||||
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b // indirect
|
||||
github.com/gofrs/flock v0.7.0
|
||||
github.com/gogo/protobuf v1.2.0 // indirect
|
||||
github.com/gofrs/flock v0.7.1
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
||||
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d
|
||||
github.com/google/uuid v1.0.0
|
||||
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06
|
||||
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
|
||||
github.com/hashicorp/consul v0.0.0-20180807174550-3e6313bebbf0
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce
|
||||
github.com/hashicorp/consul v1.4.0
|
||||
github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
github.com/hashicorp/go-getter v1.2.0
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f // indirect
|
||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
||||
github.com/hashicorp/go-memdb v0.0.0-20181108192425-032f93b25bec // indirect
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
|
||||
github.com/hashicorp/go-plugin v0.0.0-20181030172320-54b6ff97d818 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect
|
||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
||||
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0
|
||||
github.com/hashicorp/go-uuid v1.0.1
|
||||
github.com/hashicorp/go-version v1.1.0
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 // indirect
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect
|
||||
github.com/hashicorp/memberlist v0.1.0 // indirect
|
||||
github.com/hashicorp/serf v0.0.0-20180530155958-984a73625de3 // indirect
|
||||
github.com/hashicorp/vault v0.0.0-20180724215049-b9adaf9c6959
|
||||
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/serf v0.8.2 // indirect
|
||||
github.com/hashicorp/vault v1.1.0
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
|
||||
github.com/hetznercloud/hcloud-go v1.12.0
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775
|
||||
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect
|
||||
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564
|
||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
|
||||
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a // indirect
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77 // indirect
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817 // indirect
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f // indirect
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 // indirect
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 // indirect
|
||||
github.com/lib/pq v1.0.0 // indirect
|
||||
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c // indirect
|
||||
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
|
||||
github.com/masterzen/simplexml v0.0.0-20140219194429-95ba30457eb1 // indirect
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
|
||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
||||
github.com/mattn/go-tty v0.0.0-20190402035014-76a2065f1a95
|
||||
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007
|
||||
github.com/miekg/dns v1.1.1 // indirect
|
||||
github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/cli v1.0.0
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7
|
||||
github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||
github.com/mitchellh/iochan v1.0.1-0.20190408094311-e9f5309a3061
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc
|
||||
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||
|
@ -125,52 +82,36 @@ require (
|
|||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||
github.com/onsi/ginkgo v1.7.0 // indirect
|
||||
github.com/onsi/gomega v1.4.3 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v0.1.1 // indirect
|
||||
github.com/oracle/oci-go-sdk v1.8.0
|
||||
github.com/ory/dockertest v3.3.2+incompatible // indirect
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pierrec/lz4 v0.0.0-20160112163551-383c0d87b5dd
|
||||
github.com/pierrec/xxHash v0.0.0-20160112165351-5a004441f897 // indirect
|
||||
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca
|
||||
github.com/posener/complete v0.0.0-20170908125245-88e59760adad
|
||||
github.com/posener/complete v1.1.1
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
||||
github.com/prometheus/client_golang v0.9.2 // indirect
|
||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 // indirect
|
||||
github.com/rwtodd/Go.Sed v0.0.0-20170507045331-d6d5d585814e
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect
|
||||
github.com/satori/go.uuid v0.0.0-20170321230731-5bf94b69c6b6 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
github.com/sirupsen/logrus v0.0.0-20180315010703-90150a8ed11b // indirect
|
||||
github.com/sirupsen/logrus v1.2.0 // indirect
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346
|
||||
github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
|
||||
github.com/ulikunitz/xz v0.5.5
|
||||
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311
|
||||
github.com/xanzy/go-cloudstack v2.4.1+incompatible
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6
|
||||
google.golang.org/api v0.1.0
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/h2non/gock.v1 v1.0.12 // indirect
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 // indirect
|
||||
gopkg.in/ldap.v2 v2.5.1 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
|
284
go.sum
284
go.sum
|
@ -1,4 +1,3 @@
|
|||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||
|
@ -12,8 +11,6 @@ github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2px
|
|||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
|
||||
github.com/Azure/azure-sdk-for-go v17.3.1+incompatible h1:9Nzge8xxnYm5lVRkvTpG1odiDN0fYDorQwVEaVfg1+g=
|
||||
github.com/Azure/azure-sdk-for-go v17.3.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v10.12.0+incompatible h1:6YphwUK+oXbzvCc1fd5VrnxCekwzDkpA7gUEbci2MvI=
|
||||
github.com/Azure/go-autorest v10.12.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA=
|
||||
|
@ -21,22 +18,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzS
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290 h1:K9I21XUHNbYD3GNMmJBN0UKJCpdP+glftwNZ7Bo8kqY=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 h1:dmc/C8bpE5VkQn65PNbbyACDC8xw8Hpp/NEurdPmQDQ=
|
||||
github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
|
||||
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/NYTimes/gziphandler v1.0.1 h1:iLrQrdwjDd52kHDA5op2UBJFjmOb9g+7scBan4RN8F0=
|
||||
github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591 h1:/P9HCl71+Eh6vDbKNyRu+rpIIR70UCZWNOGexVV3e6k=
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591/go.mod h1:EHGzQGbwozJBj/4qj3WGrTJ0FqjgOTOxLQ0VNWvPn08=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/SAP/go-hdb v0.13.1 h1:BuZlUZtqbF/oVSQ8Vp+/+wOtcBLh55zwMV7XnvYcz8g=
|
||||
github.com/SAP/go-hdb v0.13.1/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
|
||||
github.com/SermoDigital/jose v0.9.1 h1:atYaHPD3lPICcbK1owly3aPm0iaJGSGPi0WD4vLznv8=
|
||||
github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f h1:jI4DIE5Vf4oRaHfthB0oRhU+yuYuoOTurDzwAlskP00=
|
||||
|
@ -50,74 +33,47 @@ github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6 h1:uZuxRZCz65cG1o
|
|||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc=
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
||||
github.com/armon/go-metrics v0.0.0-20180713145231-3c58d8115a78 h1:mdRSArcFLfW0VoL34LZAKSz6LkkK4jFxVx2xYavACMg=
|
||||
github.com/armon/go-metrics v0.0.0-20180713145231-3c58d8115a78/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 h1:MBXhrxjNkjdqJysfNbKMMPFNXlz6EzpOnPcsoYBeD3E=
|
||||
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.16.24 h1:I/A3Hwbgs3IEAP6v1bFpHKXiT7wZDoToX9cb00nxZnM=
|
||||
github.com/aws/aws-sdk-go v1.16.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909 h1:mUVWHQ4tjVv86uJhxSbYqwdz4o+Imcl6HoZtoaqC3zM=
|
||||
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28=
|
||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/cenkalti/backoff v2.1.0+incompatible h1:FIRvWBZrzS4YC7NT5cOuZjexzFvIr+Dbi6aD1cZaNBk=
|
||||
github.com/cenkalti/backoff v2.1.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible h1:KsuY3ogbxgVv3FNhbLUoT+SE9znoWEUIuChSIT4HukI=
|
||||
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/goselect v0.0.0-20180210034346-528c74964609 h1:FSxXMd2wCHj6GqgBdo4UtVA9R2aieIDvSniepqyOppU=
|
||||
github.com/creack/goselect v0.0.0-20180210034346-528c74964609/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=
|
||||
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/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978 h1:oyfbRmu7YnytN4bXhvJPY1HPgUL52j5AxtgGDU0bMVs=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/godo v1.7.3 h1:0tFPilFBDsVSiAKF8hyzj/MAmRYxNc5MGhAiYZYVrpo=
|
||||
github.com/digitalocean/godo v1.7.3/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
|
||||
github.com/digitalocean/godo v1.11.1 h1:OsTh37YFKk+g6DnAOrkXJ9oDArTkRx5UTkBJ2EWAO38=
|
||||
github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
|
||||
github.com/dnaeon/go-vcr v1.0.0 h1:1QZ+ahihvRvppcJnFvuoHAdnZTf1PqKjO4Ftr1cfQTo=
|
||||
github.com/dnaeon/go-vcr v1.0.0/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5 h1:DwY2bFs8p+xf2WaQewx2hnGdjYR5K4UAaxcNPyKkTek=
|
||||
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22 h1:RqZJa9Ohzpyr5OADCKz/8t0vtdD2vczMdjqCbvTYz7o=
|
||||
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
|
||||
github.com/docker/docker v0.0.0-20180422163414-57142e89befe h1:VW8TnWi0CZgg7oCv0wH6evNwkzcJg/emnw4HrVIWws4=
|
||||
github.com/docker/docker v0.0.0-20180422163414-57142e89befe/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
||||
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
|
@ -125,19 +81,9 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
|
||||
github.com/go-ldap/ldap v2.5.1+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
|
||||
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b h1:dnUw9Ih14dCKzbtZxm+pwQRYIb+9ypiwtZgsCQN4zmg=
|
||||
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
||||
github.com/gofrs/flock v0.7.0 h1:pGFUjl501gafK9HBt1VGL1KCOd/YhIooID+xgyJCf3g=
|
||||
github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
|
||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
|
@ -145,12 +91,11 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
|||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
|
@ -158,8 +103,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
|||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
|
||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d h1:rXQlD9GXkjA/PQZhmEaF/8Pj/sJfdZJK7GJG0gkS8I0=
|
||||
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
|
||||
|
@ -173,71 +117,55 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 h1:JIM+OacoOJRU30xpjMf8sulYqjr0ViA3WDrTX6j/yDI=
|
||||
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/consul v0.0.0-20180807174550-3e6313bebbf0 h1:vh/G7ew4mi/FcKNrRk4x1N1fWYzYB5/AaSHr9W+wrF4=
|
||||
github.com/hashicorp/consul v0.0.0-20180807174550-3e6313bebbf0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
|
||||
github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v1.2.0 h1:E05bVPilzyh2yXgT6srn7WEkfMZaH+LuX9tDJw/4kaE=
|
||||
github.com/hashicorp/go-getter v1.2.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa h1:0nA8i+6Rwqaq9xlpmVxxTwk6rxiEhX+E6Wh4vPNHiS8=
|
||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=
|
||||
github.com/hashicorp/go-memdb v0.0.0-20181108192425-032f93b25bec h1:A1nDk9UOKWPTQh5YcCnbwNbqj23e5pggf4HxGBulhr8=
|
||||
github.com/hashicorp/go-memdb v0.0.0-20181108192425-032f93b25bec/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE=
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c h1:BTAbnbegUIMB6xmQCwWE8yRzbA4XSpnZY5hvRJC188I=
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19 h1:gb61U/o4ZJ6TRYvZqJUKYidIhJOEAvNyVMesryROxAY=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20181030172320-54b6ff97d818 h1:wA1XRGBHMdpio0gcKUtnmkwSIlCAG1qzpsr+iPyi3Y8=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20181030172320-54b6ff97d818/go.mod h1:Ft7ju2vWzhO0ETMKUVo12XmXmII6eSUS4rsPTkY/siA=
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4319q2q7XKn0MQbi8p37hsJ+9Xo8e6yojA73JVxk=
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
|
||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0 h1:YnYV/qHLtjZmhFnWaSsNqVAUhxCuPKRe5wDGpOKYL/U=
|
||||
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU1hzH8=
|
||||
github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
|
||||
github.com/hashicorp/serf v0.0.0-20180530155958-984a73625de3 h1:NUr1hG6WO9sI1x8ofSimmpqfJ+rEHiHP/PLEA33rcfQ=
|
||||
github.com/hashicorp/serf v0.0.0-20180530155958-984a73625de3/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
|
||||
github.com/hashicorp/vault v0.0.0-20180724215049-b9adaf9c6959 h1:PyVs2CB7Hxgl1kU0tWAVpagLK3N+MC0eeyKmG+L5VPI=
|
||||
github.com/hashicorp/vault v0.0.0-20180724215049-b9adaf9c6959/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
||||
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e h1:2Hwd2Yi0/qjAC6ujOu6WBVXAak9Snuw0LTYdZkqIdKM=
|
||||
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/vault v1.1.0 h1:v79NUgO5xCZnXVzUkIqFOXtP8YhpnHAi1fk3eo9cuOE=
|
||||
github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hetznercloud/hcloud-go v1.12.0 h1:ugZO8a8ADekqSWi7xWlcs6pxr4QE0tw5VnyjXcL5n28=
|
||||
github.com/hetznercloud/hcloud-go v1.12.0/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIoIQ5nFoOmwEHPDsqng8d0dtKj3lCnQCwGvtxXc=
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775/go.mod h1:R9rU87RxxmcD3DkspW9JqGBXiJyg5MA+WNCtJrBtnXs=
|
||||
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee h1:AQ/QmCk6x8ECPpf2pkPtA4lyncEEBbs8VFnVXPYKhIs=
|
||||
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
|
@ -248,8 +176,6 @@ github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpR
|
|||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
|
||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a h1:X/UFlwD2/UV0RCy+8ITi4DmxJwk83YUH7bXwkJIHHMo=
|
||||
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77 h1:rJnR80lkojFgjdg/oQPhbZoY8t8uM51XMz8DrJrjabk=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
|
@ -259,6 +185,8 @@ github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f h1:UD9YLTi2aBhdOOT
|
|||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec h1:PYqF3Tiz2W2Ag0ezyDhAobWDWlrFv7U+qct4spLeDBM=
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 h1:YUrU1/jxRqnt0PSrKj1Uj/wEjk/fjnE80QFfi2Zlj7Q=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8PwwdtnarXqLagOX0b/TbZx2zLMqEg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
|
@ -269,46 +197,37 @@ 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c h1:N7uWGS2fTwH/4BwxbHiJZNAFTSJ5yPU0emHsQWvkxEY=
|
||||
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c h1:FMUOnVGy8nWk1cvlMCAoftRItQGMxI0vzJ3dQjeZTCE=
|
||||
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c/go.mod h1:mf8fjOu33zCqxUjuiU3I8S1lJMyEAlH+0F2+M5xl3hE=
|
||||
github.com/masterzen/simplexml v0.0.0-20140219194429-95ba30457eb1 h1:cLEbk5d4t8CDqmQtCMc2lk91cflxOrj31k9LTIabPoA=
|
||||
github.com/masterzen/simplexml v0.0.0-20140219194429-95ba30457eb1/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939 h1:cRFHA33ER97Xy5jmjS519OXCS/yE3AT3zdbQAg0Z53g=
|
||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-tty v0.0.0-20190402035014-76a2065f1a95 h1:zyW7ieMaS0tXk1KgXxFgQ+HAMmxYpwe7MiAzE4C/rkE=
|
||||
github.com/mattn/go-tty v0.0.0-20190402035014-76a2065f1a95/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007 h1:xjZxmVDmDZoEsl2gV0qD0pyBH+wXmJIZd27wsNFphJk=
|
||||
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o=
|
||||
github.com/miekg/dns v1.1.1/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8 h1:ZPmOsqSxGDUqFp0ErYpJDIfaBRBGvQv0qDPr37kZBrk=
|
||||
github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8/go.mod h1:oGumspjLm2kTyiT1QMGpFqRlmxnKHfCvhZEVnx+5UeE=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7 h1:PXPMDtfqV+rZJshQHOiwUFqlqErXaAcuWy+/ZmyRfNc=
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7/go.mod h1:g7SZj7ABpStq3tM4zqHiVEG5un/DZ1+qJJKO7qx1EvU=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/iochan v0.0.0-20150529224432-87b45ffd0e95 h1:aHWVygBsLb+Kls/35B3tevL1hvDxZ0UklPA0BmhqTEk=
|
||||
github.com/mitchellh/iochan v0.0.0-20150529224432-87b45ffd0e95/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/iochan v1.0.1-0.20190408094311-e9f5309a3061 h1:BSEloc+wp5WA/fu0jw5HBWOfoKLdvpqi38ZP22eNemg=
|
||||
github.com/mitchellh/iochan v1.0.1-0.20190408094311-e9f5309a3061/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc h1:5T6hzGUO5OrL6MdYXYoLQtRWJDDgjdlOVBn9mIqGY1g=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557 h1:w1QuuAA2km2Hax+EPamrq5ZRBeaNv2vsjvgB4an0zoU=
|
||||
|
@ -327,8 +246,6 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
|
|||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b h1:LGItPaClbzopugAomw5VFKnG3h1dUr9QW5KOU+m8gu0=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
|
@ -336,57 +253,36 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
|||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs=
|
||||
github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ory/dockertest v3.3.2+incompatible h1:uO+NcwH6GuFof/Uz8yzjNi1g0sGT5SLAJbdBvD8bUYc=
|
||||
github.com/ory/dockertest v3.3.2+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pierrec/lz4 v0.0.0-20160112163551-383c0d87b5dd h1:0EXhZlgCFcAZjhD6f6yqIINBBBym9seHr6AkvAWUgN8=
|
||||
github.com/pierrec/lz4 v0.0.0-20160112163551-383c0d87b5dd/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/xxHash v0.0.0-20160112165351-5a004441f897 h1:jp3jc/PyyTrTKjJJ6rWnhTbmo7tGgBFyG9AL5FIrO1I=
|
||||
github.com/pierrec/xxHash v0.0.0-20160112165351-5a004441f897/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I=
|
||||
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e h1:+RHxT/gm0O3UF7nLJbdNzAmULvCFt4XfXHWzh3XI/zs=
|
||||
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca h1:k8gsErq3rkcbAyCnpOycQsbw88NjCHk7L3KfBZKhQDQ=
|
||||
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
|
||||
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/posener/complete v0.0.0-20170908125245-88e59760adad h1:jTlrcNQXxCpT8NU0MG1aiUkfAiTlm+W6KPScysqjvdA=
|
||||
github.com/posener/complete v0.0.0-20170908125245-88e59760adad/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible h1:ZoVHH6voxW9Onzo6z2yLtocVoN6mBocyDoqoyAMHokE=
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible/go.mod h1:T3/WrziK7fYH3C8ilAFAHe99R452/IzIG3YYkqaOFeQ=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 h1:4qPms2txLWMLXKzqlnYSulKRS4cS9aYgPtAEpUelQok=
|
||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/rwtodd/Go.Sed v0.0.0-20170507045331-d6d5d585814e h1:lN+IKs+Jb9uwDOMO4VJZzH9vOjjist0THR5s9akp+Ss=
|
||||
github.com/rwtodd/Go.Sed v0.0.0-20170507045331-d6d5d585814e/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v0.0.0-20170321230731-5bf94b69c6b6 h1:7PFDlUz5/yGp8w21PuZGgEfN7ZF7OXvgAIDjwih82Gs=
|
||||
github.com/satori/go.uuid v0.0.0-20170321230731-5bf94b69c6b6/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 h1:DaqC32ZwOuO4ctgg9qAdKnlQxwFPkKmCOEqwSNwYy7c=
|
||||
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70/go.mod h1:XjlXWPd6VONhsRSEuzGkV8mzRpH7ou1cdLV7IKJk96s=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
|
@ -414,23 +310,24 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l
|
|||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v0.0.0-20180315010703-90150a8ed11b h1:m1cItYkpJSJ3ZKPEV/XKbZTcCaJHY1qF+R9GisLZ4kw=
|
||||
github.com/sirupsen/logrus v0.0.0-20180315010703-90150a8ed11b/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346 h1:a014AaXz7AISMePv8xKRffUZZkr5z2XmSDf41gRV3+A=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4=
|
||||
github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
|
@ -443,14 +340,19 @@ go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
|
|||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -458,21 +360,26 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsq
|
|||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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 h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6 h1:MXtOG7w2ND9qNCUZSDBGll/SpVIq7ftozR9I8/JGBHY=
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
|
@ -487,13 +394,11 @@ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+
|
|||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
@ -504,36 +409,25 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
|
|||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/h2non/gock.v1 v1.0.12 h1:o3JJqe+h7R9Ay6LtMeFrKz1WnokrJDrNpDQs9KGqVn8=
|
||||
gopkg.in/h2non/gock.v1 v1.0.12/go.mod h1:KHI4Z1sxDW6P4N3DfTWSEza07YpkQP7KJBfglRMEjKY=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 h1:H6trpavCIuipdInWrab8l34Mf+GGVfphniHostMdMaQ=
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516/go.mod h1:d3R+NllX3X5e0zlG1Rful3uLvsGC/Q3OHut5464DEQw=
|
||||
gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
|
||||
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38VS1jM=
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/mitchellh/iochan"
|
||||
"github.com/hashicorp/packer/common/iochan"
|
||||
)
|
||||
|
||||
// CmdDisconnect is a sentinel value to indicate a RemoteCmd
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/mitchellh/iochan"
|
||||
"github.com/hashicorp/packer/common/iochan"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package compress
|
|||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -273,8 +272,8 @@ func makeBGZFWriter(output io.WriteCloser, compressionLevel int) (io.WriteCloser
|
|||
|
||||
func makeLZ4Writer(output io.WriteCloser, compressionLevel int) (io.WriteCloser, error) {
|
||||
lzwriter := lz4.NewWriter(output)
|
||||
if compressionLevel > gzip.DefaultCompression {
|
||||
lzwriter.Header.HighCompression = true
|
||||
if compressionLevel > 0 {
|
||||
lzwriter.Header.CompressionLevel = compressionLevel
|
||||
}
|
||||
return lzwriter, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
|
@ -1,12 +0,0 @@
|
|||
# go-ansiterm
|
||||
|
||||
This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
|
||||
|
||||
For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
|
||||
|
||||
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
|
||||
|
||||
See parser_test.go for examples exercising the state machine and generating appropriate function calls.
|
||||
|
||||
-----
|
||||
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.
|
|
@ -1,188 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
const LogEnv = "DEBUG_TERMINAL"
|
||||
|
||||
// ANSI constants
|
||||
// References:
|
||||
// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm
|
||||
// -- http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
|
||||
// -- http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// -- http://vt100.net/emu/dec_ansi_parser
|
||||
// -- http://vt100.net/emu/vt500_parser.svg
|
||||
// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
// -- http://www.inwap.com/pdp10/ansicode.txt
|
||||
const (
|
||||
// ECMA-48 Set Graphics Rendition
|
||||
// Note:
|
||||
// -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved
|
||||
// -- Fonts could possibly be supported via SetCurrentConsoleFontEx
|
||||
// -- Windows does not expose the per-window cursor (i.e., caret) blink times
|
||||
ANSI_SGR_RESET = 0
|
||||
ANSI_SGR_BOLD = 1
|
||||
ANSI_SGR_DIM = 2
|
||||
_ANSI_SGR_ITALIC = 3
|
||||
ANSI_SGR_UNDERLINE = 4
|
||||
_ANSI_SGR_BLINKSLOW = 5
|
||||
_ANSI_SGR_BLINKFAST = 6
|
||||
ANSI_SGR_REVERSE = 7
|
||||
_ANSI_SGR_INVISIBLE = 8
|
||||
_ANSI_SGR_LINETHROUGH = 9
|
||||
_ANSI_SGR_FONT_00 = 10
|
||||
_ANSI_SGR_FONT_01 = 11
|
||||
_ANSI_SGR_FONT_02 = 12
|
||||
_ANSI_SGR_FONT_03 = 13
|
||||
_ANSI_SGR_FONT_04 = 14
|
||||
_ANSI_SGR_FONT_05 = 15
|
||||
_ANSI_SGR_FONT_06 = 16
|
||||
_ANSI_SGR_FONT_07 = 17
|
||||
_ANSI_SGR_FONT_08 = 18
|
||||
_ANSI_SGR_FONT_09 = 19
|
||||
_ANSI_SGR_FONT_10 = 20
|
||||
_ANSI_SGR_DOUBLEUNDERLINE = 21
|
||||
ANSI_SGR_BOLD_DIM_OFF = 22
|
||||
_ANSI_SGR_ITALIC_OFF = 23
|
||||
ANSI_SGR_UNDERLINE_OFF = 24
|
||||
_ANSI_SGR_BLINK_OFF = 25
|
||||
_ANSI_SGR_RESERVED_00 = 26
|
||||
ANSI_SGR_REVERSE_OFF = 27
|
||||
_ANSI_SGR_INVISIBLE_OFF = 28
|
||||
_ANSI_SGR_LINETHROUGH_OFF = 29
|
||||
ANSI_SGR_FOREGROUND_BLACK = 30
|
||||
ANSI_SGR_FOREGROUND_RED = 31
|
||||
ANSI_SGR_FOREGROUND_GREEN = 32
|
||||
ANSI_SGR_FOREGROUND_YELLOW = 33
|
||||
ANSI_SGR_FOREGROUND_BLUE = 34
|
||||
ANSI_SGR_FOREGROUND_MAGENTA = 35
|
||||
ANSI_SGR_FOREGROUND_CYAN = 36
|
||||
ANSI_SGR_FOREGROUND_WHITE = 37
|
||||
_ANSI_SGR_RESERVED_01 = 38
|
||||
ANSI_SGR_FOREGROUND_DEFAULT = 39
|
||||
ANSI_SGR_BACKGROUND_BLACK = 40
|
||||
ANSI_SGR_BACKGROUND_RED = 41
|
||||
ANSI_SGR_BACKGROUND_GREEN = 42
|
||||
ANSI_SGR_BACKGROUND_YELLOW = 43
|
||||
ANSI_SGR_BACKGROUND_BLUE = 44
|
||||
ANSI_SGR_BACKGROUND_MAGENTA = 45
|
||||
ANSI_SGR_BACKGROUND_CYAN = 46
|
||||
ANSI_SGR_BACKGROUND_WHITE = 47
|
||||
_ANSI_SGR_RESERVED_02 = 48
|
||||
ANSI_SGR_BACKGROUND_DEFAULT = 49
|
||||
// 50 - 65: Unsupported
|
||||
|
||||
ANSI_MAX_CMD_LENGTH = 4096
|
||||
|
||||
MAX_INPUT_EVENTS = 128
|
||||
DEFAULT_WIDTH = 80
|
||||
DEFAULT_HEIGHT = 24
|
||||
|
||||
ANSI_BEL = 0x07
|
||||
ANSI_BACKSPACE = 0x08
|
||||
ANSI_TAB = 0x09
|
||||
ANSI_LINE_FEED = 0x0A
|
||||
ANSI_VERTICAL_TAB = 0x0B
|
||||
ANSI_FORM_FEED = 0x0C
|
||||
ANSI_CARRIAGE_RETURN = 0x0D
|
||||
ANSI_ESCAPE_PRIMARY = 0x1B
|
||||
ANSI_ESCAPE_SECONDARY = 0x5B
|
||||
ANSI_OSC_STRING_ENTRY = 0x5D
|
||||
ANSI_COMMAND_FIRST = 0x40
|
||||
ANSI_COMMAND_LAST = 0x7E
|
||||
DCS_ENTRY = 0x90
|
||||
CSI_ENTRY = 0x9B
|
||||
OSC_STRING = 0x9D
|
||||
ANSI_PARAMETER_SEP = ";"
|
||||
ANSI_CMD_G0 = '('
|
||||
ANSI_CMD_G1 = ')'
|
||||
ANSI_CMD_G2 = '*'
|
||||
ANSI_CMD_G3 = '+'
|
||||
ANSI_CMD_DECPNM = '>'
|
||||
ANSI_CMD_DECPAM = '='
|
||||
ANSI_CMD_OSC = ']'
|
||||
ANSI_CMD_STR_TERM = '\\'
|
||||
|
||||
KEY_CONTROL_PARAM_2 = ";2"
|
||||
KEY_CONTROL_PARAM_3 = ";3"
|
||||
KEY_CONTROL_PARAM_4 = ";4"
|
||||
KEY_CONTROL_PARAM_5 = ";5"
|
||||
KEY_CONTROL_PARAM_6 = ";6"
|
||||
KEY_CONTROL_PARAM_7 = ";7"
|
||||
KEY_CONTROL_PARAM_8 = ";8"
|
||||
KEY_ESC_CSI = "\x1B["
|
||||
KEY_ESC_N = "\x1BN"
|
||||
KEY_ESC_O = "\x1BO"
|
||||
|
||||
FILL_CHARACTER = ' '
|
||||
)
|
||||
|
||||
func getByteRange(start byte, end byte) []byte {
|
||||
bytes := make([]byte, 0, 32)
|
||||
for i := start; i <= end; i++ {
|
||||
bytes = append(bytes, byte(i))
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
var toGroundBytes = getToGroundBytes()
|
||||
var executors = getExecuteBytes()
|
||||
|
||||
// SPACE 20+A0 hex Always and everywhere a blank space
|
||||
// Intermediate 20-2F hex !"#$%&'()*+,-./
|
||||
var intermeds = getByteRange(0x20, 0x2F)
|
||||
|
||||
// Parameters 30-3F hex 0123456789:;<=>?
|
||||
// CSI Parameters 30-39, 3B hex 0123456789;
|
||||
var csiParams = getByteRange(0x30, 0x3F)
|
||||
|
||||
var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...)
|
||||
|
||||
// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
var upperCase = getByteRange(0x40, 0x5F)
|
||||
|
||||
// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
|
||||
var lowerCase = getByteRange(0x60, 0x7E)
|
||||
|
||||
// Alphabetics 40-7E hex (all of upper and lower case)
|
||||
var alphabetics = append(upperCase, lowerCase...)
|
||||
|
||||
var printables = getByteRange(0x20, 0x7F)
|
||||
|
||||
var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E)
|
||||
var escapeToGroundBytes = getEscapeToGroundBytes()
|
||||
|
||||
// See http://www.vt100.net/emu/vt500_parser.png for description of the complex
|
||||
// byte ranges below
|
||||
|
||||
func getEscapeToGroundBytes() []byte {
|
||||
escapeToGroundBytes := getByteRange(0x30, 0x4F)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x59)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5A)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5C)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...)
|
||||
return escapeToGroundBytes
|
||||
}
|
||||
|
||||
func getExecuteBytes() []byte {
|
||||
executeBytes := getByteRange(0x00, 0x17)
|
||||
executeBytes = append(executeBytes, 0x19)
|
||||
executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...)
|
||||
return executeBytes
|
||||
}
|
||||
|
||||
func getToGroundBytes() []byte {
|
||||
groundBytes := []byte{0x18}
|
||||
groundBytes = append(groundBytes, 0x1A)
|
||||
groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...)
|
||||
groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...)
|
||||
groundBytes = append(groundBytes, 0x99)
|
||||
groundBytes = append(groundBytes, 0x9A)
|
||||
groundBytes = append(groundBytes, 0x9C)
|
||||
return groundBytes
|
||||
}
|
||||
|
||||
// Delete 7F hex Always and everywhere ignored
|
||||
// C1 Control 80-9F hex 32 additional control characters
|
||||
// G1 Displayable A1-FE hex 94 additional displayable characters
|
||||
// Special A0+FF hex Same as SPACE and DELETE
|
|
@ -1,7 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type ansiContext struct {
|
||||
currentChar byte
|
||||
paramBuffer []byte
|
||||
interBuffer []byte
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type csiEntryState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Handle(b byte) (s state, e error) {
|
||||
csiState.parser.logf("CsiEntry::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
return csiState.parser.csiParam, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Transition(s state) error {
|
||||
csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
case csiState.parser.csiParam:
|
||||
switch {
|
||||
case sliceContains(csiParams, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectParam()
|
||||
case sliceContains(intermeds, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectInter()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Enter() error {
|
||||
csiState.parser.clear()
|
||||
return nil
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type csiParamState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Handle(b byte) (s state, e error) {
|
||||
csiState.parser.logf("CsiParam::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
csiState.parser.collectParam()
|
||||
return csiState, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Transition(s state) error {
|
||||
csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type escapeIntermediateState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
|
||||
escState.parser.logf("escapeIntermediateState::Handle %#x", b)
|
||||
nextState, err := escState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(intermeds, b):
|
||||
return escState, escState.parser.collectInter()
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case sliceContains(escapeIntermediateToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
}
|
||||
|
||||
return escState, nil
|
||||
}
|
||||
|
||||
func (escState escapeIntermediateState) Transition(s state) error {
|
||||
escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
|
||||
escState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case escState.parser.ground:
|
||||
return escState.parser.escDispatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type escapeState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (escState escapeState) Handle(b byte) (s state, e error) {
|
||||
escState.parser.logf("escapeState::Handle %#x", b)
|
||||
nextState, err := escState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case b == ANSI_ESCAPE_SECONDARY:
|
||||
return escState.parser.csiEntry, nil
|
||||
case b == ANSI_OSC_STRING_ENTRY:
|
||||
return escState.parser.oscString, nil
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case sliceContains(escapeToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
case sliceContains(intermeds, b):
|
||||
return escState.parser.escapeIntermediate, nil
|
||||
}
|
||||
|
||||
return escState, nil
|
||||
}
|
||||
|
||||
func (escState escapeState) Transition(s state) error {
|
||||
escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name())
|
||||
escState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case escState.parser.ground:
|
||||
return escState.parser.escDispatch()
|
||||
case escState.parser.escapeIntermediate:
|
||||
return escState.parser.collectInter()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (escState escapeState) Enter() error {
|
||||
escState.parser.clear()
|
||||
return nil
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type AnsiEventHandler interface {
|
||||
// Print
|
||||
Print(b byte) error
|
||||
|
||||
// Execute C0 commands
|
||||
Execute(b byte) error
|
||||
|
||||
// CUrsor Up
|
||||
CUU(int) error
|
||||
|
||||
// CUrsor Down
|
||||
CUD(int) error
|
||||
|
||||
// CUrsor Forward
|
||||
CUF(int) error
|
||||
|
||||
// CUrsor Backward
|
||||
CUB(int) error
|
||||
|
||||
// Cursor to Next Line
|
||||
CNL(int) error
|
||||
|
||||
// Cursor to Previous Line
|
||||
CPL(int) error
|
||||
|
||||
// Cursor Horizontal position Absolute
|
||||
CHA(int) error
|
||||
|
||||
// Vertical line Position Absolute
|
||||
VPA(int) error
|
||||
|
||||
// CUrsor Position
|
||||
CUP(int, int) error
|
||||
|
||||
// Horizontal and Vertical Position (depends on PUM)
|
||||
HVP(int, int) error
|
||||
|
||||
// Text Cursor Enable Mode
|
||||
DECTCEM(bool) error
|
||||
|
||||
// Origin Mode
|
||||
DECOM(bool) error
|
||||
|
||||
// 132 Column Mode
|
||||
DECCOLM(bool) error
|
||||
|
||||
// Erase in Display
|
||||
ED(int) error
|
||||
|
||||
// Erase in Line
|
||||
EL(int) error
|
||||
|
||||
// Insert Line
|
||||
IL(int) error
|
||||
|
||||
// Delete Line
|
||||
DL(int) error
|
||||
|
||||
// Insert Character
|
||||
ICH(int) error
|
||||
|
||||
// Delete Character
|
||||
DCH(int) error
|
||||
|
||||
// Set Graphics Rendition
|
||||
SGR([]int) error
|
||||
|
||||
// Pan Down
|
||||
SU(int) error
|
||||
|
||||
// Pan Up
|
||||
SD(int) error
|
||||
|
||||
// Device Attributes
|
||||
DA([]string) error
|
||||
|
||||
// Set Top and Bottom Margins
|
||||
DECSTBM(int, int) error
|
||||
|
||||
// Index
|
||||
IND() error
|
||||
|
||||
// Reverse Index
|
||||
RI() error
|
||||
|
||||
// Flush updates from previous commands
|
||||
Flush() error
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type groundState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (gs groundState) Handle(b byte) (s state, e error) {
|
||||
gs.parser.context.currentChar = b
|
||||
|
||||
nextState, err := gs.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(printables, b):
|
||||
return gs, gs.parser.print()
|
||||
|
||||
case sliceContains(executors, b):
|
||||
return gs, gs.parser.execute()
|
||||
}
|
||||
|
||||
return gs, nil
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type oscStringState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (oscState oscStringState) Handle(b byte) (s state, e error) {
|
||||
oscState.parser.logf("OscString::Handle %#x", b)
|
||||
nextState, err := oscState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isOscStringTerminator(b):
|
||||
return oscState.parser.ground, nil
|
||||
}
|
||||
|
||||
return oscState, nil
|
||||
}
|
||||
|
||||
// See below for OSC string terminators for linux
|
||||
// http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
func isOscStringTerminator(b byte) bool {
|
||||
|
||||
if b == ANSI_BEL || b == 0x5C {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type AnsiParser struct {
|
||||
currState state
|
||||
eventHandler AnsiEventHandler
|
||||
context *ansiContext
|
||||
csiEntry state
|
||||
csiParam state
|
||||
dcsEntry state
|
||||
escape state
|
||||
escapeIntermediate state
|
||||
error state
|
||||
ground state
|
||||
oscString state
|
||||
stateMap []state
|
||||
|
||||
logf func(string, ...interface{})
|
||||
}
|
||||
|
||||
type Option func(*AnsiParser)
|
||||
|
||||
func WithLogf(f func(string, ...interface{})) Option {
|
||||
return func(ap *AnsiParser) {
|
||||
ap.logf = f
|
||||
}
|
||||
}
|
||||
|
||||
func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser {
|
||||
ap := &AnsiParser{
|
||||
eventHandler: evtHandler,
|
||||
context: &ansiContext{},
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(ap)
|
||||
}
|
||||
|
||||
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
|
||||
logFile, _ := os.Create("ansiParser.log")
|
||||
logger := log.New(logFile, "", log.LstdFlags)
|
||||
if ap.logf != nil {
|
||||
l := ap.logf
|
||||
ap.logf = func(s string, v ...interface{}) {
|
||||
l(s, v...)
|
||||
logger.Printf(s, v...)
|
||||
}
|
||||
} else {
|
||||
ap.logf = logger.Printf
|
||||
}
|
||||
}
|
||||
|
||||
if ap.logf == nil {
|
||||
ap.logf = func(string, ...interface{}) {}
|
||||
}
|
||||
|
||||
ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}}
|
||||
ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}}
|
||||
ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}}
|
||||
ap.escape = escapeState{baseState{name: "Escape", parser: ap}}
|
||||
ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}}
|
||||
ap.error = errorState{baseState{name: "Error", parser: ap}}
|
||||
ap.ground = groundState{baseState{name: "Ground", parser: ap}}
|
||||
ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}}
|
||||
|
||||
ap.stateMap = []state{
|
||||
ap.csiEntry,
|
||||
ap.csiParam,
|
||||
ap.dcsEntry,
|
||||
ap.escape,
|
||||
ap.escapeIntermediate,
|
||||
ap.error,
|
||||
ap.ground,
|
||||
ap.oscString,
|
||||
}
|
||||
|
||||
ap.currState = getState(initialState, ap.stateMap)
|
||||
|
||||
ap.logf("CreateParser: parser %p", ap)
|
||||
return ap
|
||||
}
|
||||
|
||||
func getState(name string, states []state) state {
|
||||
for _, el := range states {
|
||||
if el.Name() == name {
|
||||
return el
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) Parse(bytes []byte) (int, error) {
|
||||
for i, b := range bytes {
|
||||
if err := ap.handle(b); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
return len(bytes), ap.eventHandler.Flush()
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) handle(b byte) error {
|
||||
ap.context.currentChar = b
|
||||
newState, err := ap.currState.Handle(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if newState == nil {
|
||||
ap.logf("WARNING: newState is nil")
|
||||
return errors.New("New state of 'nil' is invalid.")
|
||||
}
|
||||
|
||||
if newState != ap.currState {
|
||||
if err := ap.changeState(newState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) changeState(newState state) error {
|
||||
ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
|
||||
|
||||
// Exit old state
|
||||
if err := ap.currState.Exit(); err != nil {
|
||||
ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform transition action
|
||||
if err := ap.currState.Transition(newState); err != nil {
|
||||
ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Enter new state
|
||||
if err := newState.Enter(); err != nil {
|
||||
ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
ap.currState = newState
|
||||
return nil
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func parseParams(bytes []byte) ([]string, error) {
|
||||
paramBuff := make([]byte, 0, 0)
|
||||
params := []string{}
|
||||
|
||||
for _, v := range bytes {
|
||||
if v == ';' {
|
||||
if len(paramBuff) > 0 {
|
||||
// Completed parameter, append it to the list
|
||||
s := string(paramBuff)
|
||||
params = append(params, s)
|
||||
paramBuff = make([]byte, 0, 0)
|
||||
}
|
||||
} else {
|
||||
paramBuff = append(paramBuff, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Last parameter may not be terminated with ';'
|
||||
if len(paramBuff) > 0 {
|
||||
s := string(paramBuff)
|
||||
params = append(params, s)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func parseCmd(context ansiContext) (string, error) {
|
||||
return string(context.currentChar), nil
|
||||
}
|
||||
|
||||
func getInt(params []string, dflt int) int {
|
||||
i := getInts(params, 1, dflt)[0]
|
||||
return i
|
||||
}
|
||||
|
||||
func getInts(params []string, minCount int, dflt int) []int {
|
||||
ints := []int{}
|
||||
|
||||
for _, v := range params {
|
||||
i, _ := strconv.Atoi(v)
|
||||
// Zero is mapped to the default value in VT100.
|
||||
if i == 0 {
|
||||
i = dflt
|
||||
}
|
||||
ints = append(ints, i)
|
||||
}
|
||||
|
||||
if len(ints) < minCount {
|
||||
remaining := minCount - len(ints)
|
||||
for i := 0; i < remaining; i++ {
|
||||
ints = append(ints, dflt)
|
||||
}
|
||||
}
|
||||
|
||||
return ints
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) modeDispatch(param string, set bool) error {
|
||||
switch param {
|
||||
case "?3":
|
||||
return ap.eventHandler.DECCOLM(set)
|
||||
case "?6":
|
||||
return ap.eventHandler.DECOM(set)
|
||||
case "?25":
|
||||
return ap.eventHandler.DECTCEM(set)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) hDispatch(params []string) error {
|
||||
if len(params) == 1 {
|
||||
return ap.modeDispatch(params[0], true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) lDispatch(params []string) error {
|
||||
if len(params) == 1 {
|
||||
return ap.modeDispatch(params[0], false)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEraseParam(params []string) int {
|
||||
param := getInt(params, 0)
|
||||
if param < 0 || 3 < param {
|
||||
param = 0
|
||||
}
|
||||
|
||||
return param
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
func (ap *AnsiParser) collectParam() error {
|
||||
currChar := ap.context.currentChar
|
||||
ap.logf("collectParam %#x", currChar)
|
||||
ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) collectInter() error {
|
||||
currChar := ap.context.currentChar
|
||||
ap.logf("collectInter %#x", currChar)
|
||||
ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) escDispatch() error {
|
||||
cmd, _ := parseCmd(*ap.context)
|
||||
intermeds := ap.context.interBuffer
|
||||
ap.logf("escDispatch currentChar: %#x", ap.context.currentChar)
|
||||
ap.logf("escDispatch: %v(%v)", cmd, intermeds)
|
||||
|
||||
switch cmd {
|
||||
case "D": // IND
|
||||
return ap.eventHandler.IND()
|
||||
case "E": // NEL, equivalent to CRLF
|
||||
err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN)
|
||||
if err == nil {
|
||||
err = ap.eventHandler.Execute(ANSI_LINE_FEED)
|
||||
}
|
||||
return err
|
||||
case "M": // RI
|
||||
return ap.eventHandler.RI()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) csiDispatch() error {
|
||||
cmd, _ := parseCmd(*ap.context)
|
||||
params, _ := parseParams(ap.context.paramBuffer)
|
||||
ap.logf("Parsed params: %v with length: %d", params, len(params))
|
||||
|
||||
ap.logf("csiDispatch: %v(%v)", cmd, params)
|
||||
|
||||
switch cmd {
|
||||
case "@":
|
||||
return ap.eventHandler.ICH(getInt(params, 1))
|
||||
case "A":
|
||||
return ap.eventHandler.CUU(getInt(params, 1))
|
||||
case "B":
|
||||
return ap.eventHandler.CUD(getInt(params, 1))
|
||||
case "C":
|
||||
return ap.eventHandler.CUF(getInt(params, 1))
|
||||
case "D":
|
||||
return ap.eventHandler.CUB(getInt(params, 1))
|
||||
case "E":
|
||||
return ap.eventHandler.CNL(getInt(params, 1))
|
||||
case "F":
|
||||
return ap.eventHandler.CPL(getInt(params, 1))
|
||||
case "G":
|
||||
return ap.eventHandler.CHA(getInt(params, 1))
|
||||
case "H":
|
||||
ints := getInts(params, 2, 1)
|
||||
x, y := ints[0], ints[1]
|
||||
return ap.eventHandler.CUP(x, y)
|
||||
case "J":
|
||||
param := getEraseParam(params)
|
||||
return ap.eventHandler.ED(param)
|
||||
case "K":
|
||||
param := getEraseParam(params)
|
||||
return ap.eventHandler.EL(param)
|
||||
case "L":
|
||||
return ap.eventHandler.IL(getInt(params, 1))
|
||||
case "M":
|
||||
return ap.eventHandler.DL(getInt(params, 1))
|
||||
case "P":
|
||||
return ap.eventHandler.DCH(getInt(params, 1))
|
||||
case "S":
|
||||
return ap.eventHandler.SU(getInt(params, 1))
|
||||
case "T":
|
||||
return ap.eventHandler.SD(getInt(params, 1))
|
||||
case "c":
|
||||
return ap.eventHandler.DA(params)
|
||||
case "d":
|
||||
return ap.eventHandler.VPA(getInt(params, 1))
|
||||
case "f":
|
||||
ints := getInts(params, 2, 1)
|
||||
x, y := ints[0], ints[1]
|
||||
return ap.eventHandler.HVP(x, y)
|
||||
case "h":
|
||||
return ap.hDispatch(params)
|
||||
case "l":
|
||||
return ap.lDispatch(params)
|
||||
case "m":
|
||||
return ap.eventHandler.SGR(getInts(params, 1, 0))
|
||||
case "r":
|
||||
ints := getInts(params, 2, 1)
|
||||
top, bottom := ints[0], ints[1]
|
||||
return ap.eventHandler.DECSTBM(top, bottom)
|
||||
default:
|
||||
ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context)
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) print() error {
|
||||
return ap.eventHandler.Print(ap.context.currentChar)
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) clear() error {
|
||||
ap.context = &ansiContext{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) execute() error {
|
||||
return ap.eventHandler.Execute(ap.context.currentChar)
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
type stateID int
|
||||
|
||||
type state interface {
|
||||
Enter() error
|
||||
Exit() error
|
||||
Handle(byte) (state, error)
|
||||
Name() string
|
||||
Transition(state) error
|
||||
}
|
||||
|
||||
type baseState struct {
|
||||
name string
|
||||
parser *AnsiParser
|
||||
}
|
||||
|
||||
func (base baseState) Enter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (base baseState) Exit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (base baseState) Handle(b byte) (s state, e error) {
|
||||
|
||||
switch {
|
||||
case b == CSI_ENTRY:
|
||||
return base.parser.csiEntry, nil
|
||||
case b == DCS_ENTRY:
|
||||
return base.parser.dcsEntry, nil
|
||||
case b == ANSI_ESCAPE_PRIMARY:
|
||||
return base.parser.escape, nil
|
||||
case b == OSC_STRING:
|
||||
return base.parser.oscString, nil
|
||||
case sliceContains(toGroundBytes, b):
|
||||
return base.parser.ground, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (base baseState) Name() string {
|
||||
return base.name
|
||||
}
|
||||
|
||||
func (base baseState) Transition(s state) error {
|
||||
if s == base.parser.ground {
|
||||
execBytes := []byte{0x18}
|
||||
execBytes = append(execBytes, 0x1A)
|
||||
execBytes = append(execBytes, getByteRange(0x80, 0x8F)...)
|
||||
execBytes = append(execBytes, getByteRange(0x91, 0x97)...)
|
||||
execBytes = append(execBytes, 0x99)
|
||||
execBytes = append(execBytes, 0x9A)
|
||||
|
||||
if sliceContains(execBytes, base.parser.context.currentChar) {
|
||||
return base.parser.execute()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type dcsEntryState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
type errorState struct {
|
||||
baseState
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package ansiterm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func sliceContains(bytes []byte, b byte) bool {
|
||||
for _, v := range bytes {
|
||||
if v == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func convertBytesToInteger(bytes []byte) int {
|
||||
s := string(bytes)
|
||||
i, _ := strconv.Atoi(s)
|
||||
return i
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Azure/go-ansiterm"
|
||||
)
|
||||
|
||||
// Windows keyboard constants
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx.
|
||||
const (
|
||||
VK_PRIOR = 0x21 // PAGE UP key
|
||||
VK_NEXT = 0x22 // PAGE DOWN key
|
||||
VK_END = 0x23 // END key
|
||||
VK_HOME = 0x24 // HOME key
|
||||
VK_LEFT = 0x25 // LEFT ARROW key
|
||||
VK_UP = 0x26 // UP ARROW key
|
||||
VK_RIGHT = 0x27 // RIGHT ARROW key
|
||||
VK_DOWN = 0x28 // DOWN ARROW key
|
||||
VK_SELECT = 0x29 // SELECT key
|
||||
VK_PRINT = 0x2A // PRINT key
|
||||
VK_EXECUTE = 0x2B // EXECUTE key
|
||||
VK_SNAPSHOT = 0x2C // PRINT SCREEN key
|
||||
VK_INSERT = 0x2D // INS key
|
||||
VK_DELETE = 0x2E // DEL key
|
||||
VK_HELP = 0x2F // HELP key
|
||||
VK_F1 = 0x70 // F1 key
|
||||
VK_F2 = 0x71 // F2 key
|
||||
VK_F3 = 0x72 // F3 key
|
||||
VK_F4 = 0x73 // F4 key
|
||||
VK_F5 = 0x74 // F5 key
|
||||
VK_F6 = 0x75 // F6 key
|
||||
VK_F7 = 0x76 // F7 key
|
||||
VK_F8 = 0x77 // F8 key
|
||||
VK_F9 = 0x78 // F9 key
|
||||
VK_F10 = 0x79 // F10 key
|
||||
VK_F11 = 0x7A // F11 key
|
||||
VK_F12 = 0x7B // F12 key
|
||||
|
||||
RIGHT_ALT_PRESSED = 0x0001
|
||||
LEFT_ALT_PRESSED = 0x0002
|
||||
RIGHT_CTRL_PRESSED = 0x0004
|
||||
LEFT_CTRL_PRESSED = 0x0008
|
||||
SHIFT_PRESSED = 0x0010
|
||||
NUMLOCK_ON = 0x0020
|
||||
SCROLLLOCK_ON = 0x0040
|
||||
CAPSLOCK_ON = 0x0080
|
||||
ENHANCED_KEY = 0x0100
|
||||
)
|
||||
|
||||
type ansiCommand struct {
|
||||
CommandBytes []byte
|
||||
Command string
|
||||
Parameters []string
|
||||
IsSpecial bool
|
||||
}
|
||||
|
||||
func newAnsiCommand(command []byte) *ansiCommand {
|
||||
|
||||
if isCharacterSelectionCmdChar(command[1]) {
|
||||
// Is Character Set Selection commands
|
||||
return &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command),
|
||||
IsSpecial: true,
|
||||
}
|
||||
}
|
||||
|
||||
// last char is command character
|
||||
lastCharIndex := len(command) - 1
|
||||
|
||||
ac := &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command[lastCharIndex]),
|
||||
IsSpecial: false,
|
||||
}
|
||||
|
||||
// more than a single escape
|
||||
if lastCharIndex != 0 {
|
||||
start := 1
|
||||
// skip if double char escape sequence
|
||||
if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY {
|
||||
start++
|
||||
}
|
||||
// convert this to GetNextParam method
|
||||
ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP)
|
||||
}
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 {
|
||||
if index < 0 || index >= len(ac.Parameters) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
param, err := strconv.ParseInt(ac.Parameters[index], 10, 16)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return int16(param)
|
||||
}
|
||||
|
||||
func (ac *ansiCommand) String() string {
|
||||
return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
|
||||
bytesToHex(ac.CommandBytes),
|
||||
ac.Command,
|
||||
strings.Join(ac.Parameters, "\",\""))
|
||||
}
|
||||
|
||||
// isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands.
|
||||
// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html.
|
||||
func isAnsiCommandChar(b byte) bool {
|
||||
switch {
|
||||
case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY:
|
||||
return true
|
||||
case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM:
|
||||
// non-CSI escape sequence terminator
|
||||
return true
|
||||
case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL:
|
||||
// String escape sequence terminator
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isXtermOscSequence(command []byte, current byte) bool {
|
||||
return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL)
|
||||
}
|
||||
|
||||
func isCharacterSelectionCmdChar(b byte) bool {
|
||||
return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3)
|
||||
}
|
||||
|
||||
// bytesToHex converts a slice of bytes to a human-readable string.
|
||||
func bytesToHex(b []byte) string {
|
||||
hex := make([]string, len(b))
|
||||
for i, ch := range b {
|
||||
hex[i] = fmt.Sprintf("%X", ch)
|
||||
}
|
||||
return strings.Join(hex, "")
|
||||
}
|
||||
|
||||
// ensureInRange adjusts the passed value, if necessary, to ensure it is within
|
||||
// the passed min / max range.
|
||||
func ensureInRange(n int16, min int16, max int16) int16 {
|
||||
if n < min {
|
||||
return min
|
||||
} else if n > max {
|
||||
return max
|
||||
} else {
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
func GetStdFile(nFile int) (*os.File, uintptr) {
|
||||
var file *os.File
|
||||
switch nFile {
|
||||
case syscall.STD_INPUT_HANDLE:
|
||||
file = os.Stdin
|
||||
case syscall.STD_OUTPUT_HANDLE:
|
||||
file = os.Stdout
|
||||
case syscall.STD_ERROR_HANDLE:
|
||||
file = os.Stderr
|
||||
default:
|
||||
panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile))
|
||||
}
|
||||
|
||||
fd, err := syscall.GetStdHandle(nFile)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err))
|
||||
}
|
||||
|
||||
return file, uintptr(fd)
|
||||
}
|
|
@ -1,327 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//===========================================================================================================
|
||||
// IMPORTANT NOTE:
|
||||
//
|
||||
// The methods below make extensive use of the "unsafe" package to obtain the required pointers.
|
||||
// Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
|
||||
// variables) the pointers reference *before* the API completes.
|
||||
//
|
||||
// As a result, in those cases, the code must hint that the variables remain in active by invoking the
|
||||
// dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
|
||||
// require unsafe pointers.
|
||||
//
|
||||
// If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
|
||||
// the garbage collector the variables remain in use if:
|
||||
//
|
||||
// -- The value is not a pointer (e.g., int32, struct)
|
||||
// -- The value is not referenced by the method after passing the pointer to Windows
|
||||
//
|
||||
// See http://golang.org/doc/go1.3.
|
||||
//===========================================================================================================
|
||||
|
||||
var (
|
||||
kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo")
|
||||
setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo")
|
||||
setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition")
|
||||
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
|
||||
scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
|
||||
setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
|
||||
setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo")
|
||||
writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW")
|
||||
readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW")
|
||||
waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject")
|
||||
)
|
||||
|
||||
// Windows Console constants
|
||||
const (
|
||||
// Console modes
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
ENABLE_LINE_INPUT = 0x0002
|
||||
ENABLE_ECHO_INPUT = 0x0004
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_INSERT_MODE = 0x0020
|
||||
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||
ENABLE_EXTENDED_FLAGS = 0x0080
|
||||
ENABLE_AUTO_POSITION = 0x0100
|
||||
ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
|
||||
|
||||
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||
DISABLE_NEWLINE_AUTO_RETURN = 0x0008
|
||||
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
|
||||
|
||||
// Character attributes
|
||||
// Note:
|
||||
// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
|
||||
// Clearing all foreground or background colors results in black; setting all creates white.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
|
||||
FOREGROUND_BLUE uint16 = 0x0001
|
||||
FOREGROUND_GREEN uint16 = 0x0002
|
||||
FOREGROUND_RED uint16 = 0x0004
|
||||
FOREGROUND_INTENSITY uint16 = 0x0008
|
||||
FOREGROUND_MASK uint16 = 0x000F
|
||||
|
||||
BACKGROUND_BLUE uint16 = 0x0010
|
||||
BACKGROUND_GREEN uint16 = 0x0020
|
||||
BACKGROUND_RED uint16 = 0x0040
|
||||
BACKGROUND_INTENSITY uint16 = 0x0080
|
||||
BACKGROUND_MASK uint16 = 0x00F0
|
||||
|
||||
COMMON_LVB_MASK uint16 = 0xFF00
|
||||
COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
|
||||
COMMON_LVB_UNDERSCORE uint16 = 0x8000
|
||||
|
||||
// Input event types
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
KEY_EVENT = 0x0001
|
||||
MOUSE_EVENT = 0x0002
|
||||
WINDOW_BUFFER_SIZE_EVENT = 0x0004
|
||||
MENU_EVENT = 0x0008
|
||||
FOCUS_EVENT = 0x0010
|
||||
|
||||
// WaitForSingleObject return codes
|
||||
WAIT_ABANDONED = 0x00000080
|
||||
WAIT_FAILED = 0xFFFFFFFF
|
||||
WAIT_SIGNALED = 0x0000000
|
||||
WAIT_TIMEOUT = 0x00000102
|
||||
|
||||
// WaitForSingleObject wait duration
|
||||
WAIT_INFINITE = 0xFFFFFFFF
|
||||
WAIT_ONE_SECOND = 1000
|
||||
WAIT_HALF_SECOND = 500
|
||||
WAIT_QUARTER_SECOND = 250
|
||||
)
|
||||
|
||||
// Windows API Console types
|
||||
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
|
||||
// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
|
||||
type (
|
||||
CHAR_INFO struct {
|
||||
UnicodeChar uint16
|
||||
Attributes uint16
|
||||
}
|
||||
|
||||
CONSOLE_CURSOR_INFO struct {
|
||||
Size uint32
|
||||
Visible int32
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||
Size COORD
|
||||
CursorPosition COORD
|
||||
Attributes uint16
|
||||
Window SMALL_RECT
|
||||
MaximumWindowSize COORD
|
||||
}
|
||||
|
||||
COORD struct {
|
||||
X int16
|
||||
Y int16
|
||||
}
|
||||
|
||||
SMALL_RECT struct {
|
||||
Left int16
|
||||
Top int16
|
||||
Right int16
|
||||
Bottom int16
|
||||
}
|
||||
|
||||
// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
INPUT_RECORD struct {
|
||||
EventType uint16
|
||||
KeyEvent KEY_EVENT_RECORD
|
||||
}
|
||||
|
||||
KEY_EVENT_RECORD struct {
|
||||
KeyDown int32
|
||||
RepeatCount uint16
|
||||
VirtualKeyCode uint16
|
||||
VirtualScanCode uint16
|
||||
UnicodeChar uint16
|
||||
ControlKeyState uint32
|
||||
}
|
||||
|
||||
WINDOW_BUFFER_SIZE struct {
|
||||
Size COORD
|
||||
}
|
||||
)
|
||||
|
||||
// boolToBOOL converts a Go bool into a Windows int32.
|
||||
func boolToBOOL(f bool) int32 {
|
||||
if f {
|
||||
return int32(1)
|
||||
} else {
|
||||
return int32(0)
|
||||
}
|
||||
}
|
||||
|
||||
// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
|
||||
func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
|
||||
r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleCursorInfo sets the size and visiblity of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
|
||||
func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
|
||||
r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleCursorPosition location of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
|
||||
func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
|
||||
r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleMode gets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
|
||||
func GetConsoleMode(handle uintptr) (mode uint32, err error) {
|
||||
err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
|
||||
return mode, err
|
||||
}
|
||||
|
||||
// SetConsoleMode sets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
func SetConsoleMode(handle uintptr, mode uint32) error {
|
||||
r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
|
||||
use(mode)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
|
||||
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
info := CONSOLE_SCREEN_BUFFER_INFO{}
|
||||
err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
|
||||
r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
|
||||
use(scrollRect)
|
||||
use(clipRect)
|
||||
use(destOrigin)
|
||||
use(char)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleScreenBufferSize sets the size of the console screen buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
|
||||
func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
|
||||
r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleTextAttribute sets the attributes of characters written to the
|
||||
// console screen buffer by the WriteFile or WriteConsole function.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
|
||||
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
|
||||
r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
|
||||
use(attribute)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
|
||||
// Note that the size and location must be within and no larger than the backing console screen buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
|
||||
func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
|
||||
r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
|
||||
use(isAbsolute)
|
||||
use(rect)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
|
||||
func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
|
||||
r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
|
||||
use(buffer)
|
||||
use(bufferSize)
|
||||
use(bufferCoord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// ReadConsoleInput reads (and removes) data from the console input buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
|
||||
func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
|
||||
r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
|
||||
use(buffer)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// WaitForSingleObject waits for the passed handle to be signaled.
|
||||
// It returns true if the handle was signaled; false otherwise.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
|
||||
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
|
||||
r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
|
||||
switch r1 {
|
||||
case WAIT_ABANDONED, WAIT_TIMEOUT:
|
||||
return false, nil
|
||||
case WAIT_SIGNALED:
|
||||
return true, nil
|
||||
}
|
||||
use(msWait)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// String helpers
|
||||
func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
|
||||
return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
|
||||
}
|
||||
|
||||
func (coord COORD) String() string {
|
||||
return fmt.Sprintf("%v,%v", coord.X, coord.Y)
|
||||
}
|
||||
|
||||
func (rect SMALL_RECT) String() string {
|
||||
return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
|
||||
}
|
||||
|
||||
// checkError evaluates the results of a Windows API call and returns the error if it failed.
|
||||
func checkError(r1, r2 uintptr, err error) error {
|
||||
// Windows APIs return non-zero to indicate success
|
||||
if r1 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the error if provided, otherwise default to EINVAL
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
|
||||
// coordToPointer converts a COORD into a uintptr (by fooling the type system).
|
||||
func coordToPointer(c COORD) uintptr {
|
||||
// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
|
||||
return uintptr(*((*uint32)(unsafe.Pointer(&c))))
|
||||
}
|
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
func use(p interface{}) {}
|
|
@ -1,100 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import "github.com/Azure/go-ansiterm"
|
||||
|
||||
const (
|
||||
FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
)
|
||||
|
||||
// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
|
||||
// request represented by the passed ANSI mode.
|
||||
func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) {
|
||||
switch ansiMode {
|
||||
|
||||
// Mode styles
|
||||
case ansiterm.ANSI_SGR_BOLD:
|
||||
windowsMode = windowsMode | FOREGROUND_INTENSITY
|
||||
|
||||
case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF:
|
||||
windowsMode &^= FOREGROUND_INTENSITY
|
||||
|
||||
case ansiterm.ANSI_SGR_UNDERLINE:
|
||||
windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
|
||||
|
||||
case ansiterm.ANSI_SGR_REVERSE:
|
||||
inverted = true
|
||||
|
||||
case ansiterm.ANSI_SGR_REVERSE_OFF:
|
||||
inverted = false
|
||||
|
||||
case ansiterm.ANSI_SGR_UNDERLINE_OFF:
|
||||
windowsMode &^= COMMON_LVB_UNDERSCORE
|
||||
|
||||
// Foreground colors
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_BLACK:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_RED:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_GREEN:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_YELLOW:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_BLUE:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_CYAN:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_WHITE:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
|
||||
// Background colors
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT:
|
||||
// Black with no intensity
|
||||
windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_BLACK:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_RED:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_GREEN:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_YELLOW:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_BLUE:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_CYAN:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_WHITE:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
}
|
||||
|
||||
return windowsMode, inverted
|
||||
}
|
||||
|
||||
// invertAttributes inverts the foreground and background colors of a Windows attributes value
|
||||
func invertAttributes(windowsMode uint16) uint16 {
|
||||
return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
const (
|
||||
horizontal = iota
|
||||
vertical
|
||||
)
|
||||
|
||||
func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
|
||||
if h.originMode {
|
||||
sr := h.effectiveSr(info.Window)
|
||||
return SMALL_RECT{
|
||||
Top: sr.top,
|
||||
Bottom: sr.bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
} else {
|
||||
return SMALL_RECT{
|
||||
Top: info.Window.Top,
|
||||
Bottom: info.Window.Bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setCursorPosition sets the cursor to the specified position, bounded to the screen size
|
||||
func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error {
|
||||
position.X = ensureInRange(position.X, window.Left, window.Right)
|
||||
position.Y = ensureInRange(position.Y, window.Top, window.Bottom)
|
||||
err := SetConsoleCursorPosition(h.fd, position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("Cursor position set: (%d, %d)", position.X, position.Y)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error {
|
||||
return h.moveCursor(vertical, param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error {
|
||||
return h.moveCursor(horizontal, param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
switch moveMode {
|
||||
case horizontal:
|
||||
position.X += int16(param)
|
||||
case vertical:
|
||||
position.Y += int16(param)
|
||||
}
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = 0
|
||||
position.Y += int16(param)
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = int16(param) - 1
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import "github.com/Azure/go-ansiterm"
|
||||
|
||||
func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error {
|
||||
// Ignore an invalid (negative area) request
|
||||
if toCoord.Y < fromCoord.Y {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
var coordStart = COORD{}
|
||||
var coordEnd = COORD{}
|
||||
|
||||
xCurrent, yCurrent := fromCoord.X, fromCoord.Y
|
||||
xEnd, yEnd := toCoord.X, toCoord.Y
|
||||
|
||||
// Clear any partial initial line
|
||||
if xCurrent > 0 {
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yCurrent
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xCurrent = 0
|
||||
yCurrent += 1
|
||||
}
|
||||
|
||||
// Clear intervening rectangular section
|
||||
if yCurrent < yEnd {
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yEnd-1
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xCurrent = 0
|
||||
yCurrent = yEnd
|
||||
}
|
||||
|
||||
// Clear remaining partial ending line
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yEnd
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error {
|
||||
region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X}
|
||||
width := toCoord.X - fromCoord.X + 1
|
||||
height := toCoord.Y - fromCoord.Y + 1
|
||||
size := uint32(width) * uint32(height)
|
||||
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
buffer := make([]CHAR_INFO, size)
|
||||
|
||||
char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes}
|
||||
for i := 0; i < int(size); i++ {
|
||||
buffer[i] = char
|
||||
}
|
||||
|
||||
err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
// effectiveSr gets the current effective scroll region in buffer coordinates
|
||||
func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
|
||||
top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom)
|
||||
bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom)
|
||||
if top >= bottom {
|
||||
top = window.Top
|
||||
bottom = window.Bottom
|
||||
}
|
||||
return scrollRegion{top: top, bottom: bottom}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) scrollUp(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr := h.effectiveSr(info.Window)
|
||||
return h.scroll(param, sr, info)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) scrollDown(param int) error {
|
||||
return h.scrollUp(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) deleteLines(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
start := info.CursorPosition.Y
|
||||
sr := h.effectiveSr(info.Window)
|
||||
// Lines cannot be inserted or deleted outside the scrolling region.
|
||||
if start >= sr.top && start <= sr.bottom {
|
||||
sr.top = start
|
||||
return h.scroll(param, sr, info)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) insertLines(param int) error {
|
||||
return h.deleteLines(-param)
|
||||
}
|
||||
|
||||
// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
|
||||
func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
|
||||
h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
|
||||
h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
|
||||
|
||||
// Copy from and clip to the scroll region (full buffer width)
|
||||
scrollRect := SMALL_RECT{
|
||||
Top: sr.top,
|
||||
Bottom: sr.bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
|
||||
// Origin to which area should be copied
|
||||
destOrigin := COORD{
|
||||
X: 0,
|
||||
Y: sr.top - int16(param),
|
||||
}
|
||||
|
||||
char := CHAR_INFO{
|
||||
UnicodeChar: ' ',
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) deleteCharacters(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.scrollLine(param, info.CursorPosition, info)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) insertCharacters(param int) error {
|
||||
return h.deleteCharacters(-param)
|
||||
}
|
||||
|
||||
// scrollLine scrolls a line horizontally starting at the provided position by a number of columns.
|
||||
func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error {
|
||||
// Copy from and clip to the scroll region (full buffer width)
|
||||
scrollRect := SMALL_RECT{
|
||||
Top: position.Y,
|
||||
Bottom: position.Y,
|
||||
Left: position.X,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
|
||||
// Origin to which area should be copied
|
||||
destOrigin := COORD{
|
||||
X: position.X - int16(columns),
|
||||
Y: position.Y,
|
||||
}
|
||||
|
||||
char := CHAR_INFO{
|
||||
UnicodeChar: ' ',
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
// AddInRange increments a value by the passed quantity while ensuring the values
|
||||
// always remain within the supplied min / max range.
|
||||
func addInRange(n int16, increment int16, min int16, max int16) int16 {
|
||||
return ensureInRange(n+increment, min, max)
|
||||
}
|
|
@ -1,743 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/Azure/go-ansiterm"
|
||||
)
|
||||
|
||||
type windowsAnsiEventHandler struct {
|
||||
fd uintptr
|
||||
file *os.File
|
||||
infoReset *CONSOLE_SCREEN_BUFFER_INFO
|
||||
sr scrollRegion
|
||||
buffer bytes.Buffer
|
||||
attributes uint16
|
||||
inverted bool
|
||||
wrapNext bool
|
||||
drewMarginByte bool
|
||||
originMode bool
|
||||
marginByte byte
|
||||
curInfo *CONSOLE_SCREEN_BUFFER_INFO
|
||||
curPos COORD
|
||||
logf func(string, ...interface{})
|
||||
}
|
||||
|
||||
type Option func(*windowsAnsiEventHandler)
|
||||
|
||||
func WithLogf(f func(string, ...interface{})) Option {
|
||||
return func(w *windowsAnsiEventHandler) {
|
||||
w.logf = f
|
||||
}
|
||||
}
|
||||
|
||||
func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler {
|
||||
infoReset, err := GetConsoleScreenBufferInfo(fd)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
h := &windowsAnsiEventHandler{
|
||||
fd: fd,
|
||||
file: file,
|
||||
infoReset: infoReset,
|
||||
attributes: infoReset.Attributes,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(h)
|
||||
}
|
||||
|
||||
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
|
||||
logFile, _ := os.Create("winEventHandler.log")
|
||||
logger := log.New(logFile, "", log.LstdFlags)
|
||||
if h.logf != nil {
|
||||
l := h.logf
|
||||
h.logf = func(s string, v ...interface{}) {
|
||||
l(s, v...)
|
||||
logger.Printf(s, v...)
|
||||
}
|
||||
} else {
|
||||
h.logf = logger.Printf
|
||||
}
|
||||
}
|
||||
|
||||
if h.logf == nil {
|
||||
h.logf = func(string, ...interface{}) {}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
type scrollRegion struct {
|
||||
top int16
|
||||
bottom int16
|
||||
}
|
||||
|
||||
// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
|
||||
// current cursor position and scroll region settings, in which case it returns
|
||||
// true. If no special handling is necessary, then it does nothing and returns
|
||||
// false.
|
||||
//
|
||||
// In the false case, the caller should ensure that a carriage return
|
||||
// and line feed are inserted or that the text is otherwise wrapped.
|
||||
func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sr := h.effectiveSr(info.Window)
|
||||
if pos.Y == sr.bottom {
|
||||
// Scrolling is necessary. Let Windows automatically scroll if the scrolling region
|
||||
// is the full window.
|
||||
if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom {
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
h.updatePos(pos)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A custom scroll region is active. Scroll the window manually to simulate
|
||||
// the LF.
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
h.logf("Simulating LF inside scroll region")
|
||||
if err := h.scrollUp(1); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
} else if pos.Y < info.Window.Bottom {
|
||||
// Let Windows handle the LF.
|
||||
pos.Y++
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
}
|
||||
h.updatePos(pos)
|
||||
return false, nil
|
||||
} else {
|
||||
// The cursor is at the bottom of the screen but outside the scroll
|
||||
// region. Skip the LF.
|
||||
h.logf("Simulating LF outside scroll region")
|
||||
if includeCR {
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
pos.X = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// executeLF executes a LF without a CR.
|
||||
func (h *windowsAnsiEventHandler) executeLF() error {
|
||||
handled, err := h.simulateLF(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !handled {
|
||||
// Windows LF will reset the cursor column position. Write the LF
|
||||
// and restore the cursor position.
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
|
||||
if pos.X != 0 {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("Resetting cursor position for LF without CR")
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Print(b byte) error {
|
||||
if h.wrapNext {
|
||||
h.buffer.WriteByte(h.marginByte)
|
||||
h.clearWrap()
|
||||
if _, err := h.simulateLF(true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X == info.Size.X-1 {
|
||||
h.wrapNext = true
|
||||
h.marginByte = b
|
||||
} else {
|
||||
pos.X++
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Execute(b byte) error {
|
||||
switch b {
|
||||
case ansiterm.ANSI_TAB:
|
||||
h.logf("Execute(TAB)")
|
||||
// Move to the next tab stop, but preserve auto-wrap if already set.
|
||||
if !h.wrapNext {
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pos.X = (pos.X + 8) - pos.X%8
|
||||
if pos.X >= info.Size.X {
|
||||
pos.X = info.Size.X - 1
|
||||
}
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_BEL:
|
||||
h.buffer.WriteByte(ansiterm.ANSI_BEL)
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_BACKSPACE:
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X > 0 {
|
||||
pos.X--
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE)
|
||||
}
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED:
|
||||
// Treat as true LF.
|
||||
return h.executeLF()
|
||||
|
||||
case ansiterm.ANSI_LINE_FEED:
|
||||
// Simulate a CR and LF for now since there is no way in go-ansiterm
|
||||
// to tell if the LF should include CR (and more things break when it's
|
||||
// missing than when it's incorrectly added).
|
||||
handled, err := h.simulateLF(true)
|
||||
if handled || err != nil {
|
||||
return err
|
||||
}
|
||||
return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
|
||||
|
||||
case ansiterm.ANSI_CARRIAGE_RETURN:
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X != 0 {
|
||||
pos.X = 0
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN)
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUU(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CUU: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorVertical(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUD(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CUD: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorVertical(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUF(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CUF: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorHorizontal(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUB(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CUB: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorHorizontal(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CNL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CNL: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorLine(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CPL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CPL: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorLine(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CHA(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CHA: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorColumn(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) VPA(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("VPA: [[%d]]", param)
|
||||
h.clearWrap()
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
window := h.getCursorWindow(info)
|
||||
position := info.CursorPosition
|
||||
position.Y = window.Top + int16(param) - 1
|
||||
return h.setCursorPosition(position, window)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("CUP: [[%d %d]]", row, col)
|
||||
h.clearWrap()
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
window := h.getCursorWindow(info)
|
||||
position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1}
|
||||
return h.setCursorPosition(position, window)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("HVP: [[%d %d]]", row, col)
|
||||
h.clearWrap()
|
||||
return h.CUP(row, col)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
|
||||
h.clearWrap()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)})
|
||||
h.clearWrap()
|
||||
h.originMode = enable
|
||||
return h.CUP(1, 1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
|
||||
h.clearWrap()
|
||||
if err := h.ED(2); err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetWidth := int16(80)
|
||||
if use132 {
|
||||
targetWidth = 132
|
||||
}
|
||||
if info.Size.X < targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
h.logf("set buffer failed: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
window := info.Window
|
||||
window.Left = 0
|
||||
window.Right = targetWidth - 1
|
||||
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
|
||||
h.logf("set window failed: %v", err)
|
||||
return err
|
||||
}
|
||||
if info.Size.X > targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
h.logf("set buffer failed: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return SetConsoleCursorPosition(h.fd, COORD{0, 0})
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) ED(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("ED: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
|
||||
// [J -- Erases from the cursor to the end of the screen, including the cursor position.
|
||||
// [1J -- Erases from the beginning of the screen to the cursor, including the cursor position.
|
||||
// [2J -- Erases the complete display. The cursor does not move.
|
||||
// Notes:
|
||||
// -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var start COORD
|
||||
var end COORD
|
||||
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
|
||||
case 1:
|
||||
start = COORD{0, 0}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{0, 0}
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
}
|
||||
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the whole buffer was cleared, move the window to the top while preserving
|
||||
// the window-relative cursor position.
|
||||
if param == 2 {
|
||||
pos := info.CursorPosition
|
||||
window := info.Window
|
||||
pos.Y -= window.Top
|
||||
window.Bottom -= window.Top
|
||||
window.Top = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) EL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("EL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
|
||||
// [K -- Erases from the cursor to the end of the line, including the cursor position.
|
||||
// [1K -- Erases from the beginning of the line to the cursor, including the cursor position.
|
||||
// [2K -- Erases the complete line.
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var start COORD
|
||||
var end COORD
|
||||
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
|
||||
case 1:
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
}
|
||||
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) IL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("IL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.insertLines(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.deleteLines(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) ICH(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("ICH: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.insertCharacters(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DCH(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DCH: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.deleteCharacters(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SGR(params []int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
strings := []string{}
|
||||
for _, v := range params {
|
||||
strings = append(strings, strconv.Itoa(v))
|
||||
}
|
||||
|
||||
h.logf("SGR: [%v]", strings)
|
||||
|
||||
if len(params) <= 0 {
|
||||
h.attributes = h.infoReset.Attributes
|
||||
h.inverted = false
|
||||
} else {
|
||||
for _, attr := range params {
|
||||
|
||||
if attr == ansiterm.ANSI_SGR_RESET {
|
||||
h.attributes = h.infoReset.Attributes
|
||||
h.inverted = false
|
||||
continue
|
||||
}
|
||||
|
||||
h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr))
|
||||
}
|
||||
}
|
||||
|
||||
attributes := h.attributes
|
||||
if h.inverted {
|
||||
attributes = invertAttributes(attributes)
|
||||
}
|
||||
err := SetConsoleTextAttribute(h.fd, attributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SU(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("SU: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.scrollUp(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SD(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("SD: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.scrollDown(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DA(params []string) error {
|
||||
h.logf("DA: [%v]", params)
|
||||
// DA cannot be implemented because it must send data on the VT100 input stream,
|
||||
// which is not available to go-ansiterm.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("DECSTBM: [%d, %d]", top, bottom)
|
||||
|
||||
// Windows is 0 indexed, Linux is 1 indexed
|
||||
h.sr.top = int16(top - 1)
|
||||
h.sr.bottom = int16(bottom - 1)
|
||||
|
||||
// This command also moves the cursor to the origin.
|
||||
h.clearWrap()
|
||||
return h.CUP(1, 1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) RI() error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.logf("RI: []")
|
||||
h.clearWrap()
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr := h.effectiveSr(info.Window)
|
||||
if info.CursorPosition.Y == sr.top {
|
||||
return h.scrollDown(1)
|
||||
}
|
||||
|
||||
return h.moveCursorVertical(-1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) IND() error {
|
||||
h.logf("IND: []")
|
||||
return h.executeLF()
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Flush() error {
|
||||
h.curInfo = nil
|
||||
if h.buffer.Len() > 0 {
|
||||
h.logf("Flush: [%s]", h.buffer.Bytes())
|
||||
if _, err := h.buffer.WriteTo(h.file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if h.wrapNext && !h.drewMarginByte {
|
||||
h.logf("Flush: drawing margin byte '%c'", h.marginByte)
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}}
|
||||
size := COORD{1, 1}
|
||||
position := COORD{0, 0}
|
||||
region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
|
||||
if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil {
|
||||
return err
|
||||
}
|
||||
h.drewMarginByte = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cacheConsoleInfo ensures that the current console screen information has been queried
|
||||
// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
|
||||
func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
if h.curInfo == nil {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return COORD{}, nil, err
|
||||
}
|
||||
h.curInfo = info
|
||||
h.curPos = info.CursorPosition
|
||||
}
|
||||
return h.curPos, h.curInfo, nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) updatePos(pos COORD) {
|
||||
if h.curInfo == nil {
|
||||
panic("failed to call getCurrentInfo before calling updatePos")
|
||||
}
|
||||
h.curPos = pos
|
||||
}
|
||||
|
||||
// clearWrap clears the state where the cursor is in the margin
|
||||
// waiting for the next character before wrapping the line. This must
|
||||
// be done before most operations that act on the cursor.
|
||||
func (h *windowsAnsiEventHandler) clearWrap() {
|
||||
h.wrapNext = false
|
||||
h.drewMarginByte = false
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
before_script:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
|
||||
go:
|
||||
- 1.10.x
|
||||
- master
|
||||
|
||||
script:
|
||||
- test -z "$(gofmt -s -l . | tee /dev/stderr)"
|
||||
- test -z "$(golint ./... | tee /dev/stderr)"
|
||||
- go vet ./...
|
||||
- go build -v ./...
|
||||
- go test -v ./...
|
|
@ -0,0 +1,16 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
|
||||
before_install:
|
||||
- go get -u github.com/ChrisTrenkamp/goxpath
|
||||
- go get -u github.com/ChrisTrenkamp/goxpath/cmd/goxpath
|
||||
- go get -u github.com/wadey/gocovmerge
|
||||
|
||||
script:
|
||||
- go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {}
|
||||
- gocovmerge `ls *.coverprofile` > coverage.txt
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
0
vendor/github.com/ChrisTrenkamp/goxpath/coverage.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/ChrisTrenkamp/goxpath/coverage.sh
generated
vendored
Executable file → Normal file
|
@ -1,597 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
// inspired by https://github.com/Telmate/vagrant-proxmox/blob/master/lib/vagrant-proxmox/proxmox/connection.rb
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TaskTimeout - default async task call timeout in seconds
|
||||
const TaskTimeout = 300
|
||||
|
||||
// TaskStatusCheckInterval - time between async checks in seconds
|
||||
const TaskStatusCheckInterval = 2
|
||||
|
||||
const exitStatusSuccess = "OK"
|
||||
|
||||
// Client - URL, user and password to specifc Proxmox node
|
||||
type Client struct {
|
||||
session *Session
|
||||
ApiUrl string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// VmRef - virtual machine ref parts
|
||||
// map[type:qemu node:proxmox1-xx id:qemu/132 diskread:5.57424738e+08 disk:0 netin:5.9297450593e+10 mem:3.3235968e+09 uptime:1.4567097e+07 vmid:132 template:0 maxcpu:2 netout:6.053310416e+09 maxdisk:3.4359738368e+10 maxmem:8.592031744e+09 diskwrite:1.49663619584e+12 status:running cpu:0.00386980694947209 name:appt-app1-dev.xxx.xx]
|
||||
type VmRef struct {
|
||||
vmId int
|
||||
node string
|
||||
vmType string
|
||||
}
|
||||
|
||||
func (vmr *VmRef) SetNode(node string) {
|
||||
vmr.node = node
|
||||
return
|
||||
}
|
||||
|
||||
func (vmr *VmRef) SetVmType(vmType string) {
|
||||
vmr.vmType = vmType
|
||||
return
|
||||
}
|
||||
|
||||
func (vmr *VmRef) VmId() int {
|
||||
return vmr.vmId
|
||||
}
|
||||
|
||||
func (vmr *VmRef) Node() string {
|
||||
return vmr.node
|
||||
}
|
||||
|
||||
func NewVmRef(vmId int) (vmr *VmRef) {
|
||||
vmr = &VmRef{vmId: vmId, node: "", vmType: ""}
|
||||
return
|
||||
}
|
||||
|
||||
func NewClient(apiUrl string, hclient *http.Client, tls *tls.Config) (client *Client, err error) {
|
||||
var sess *Session
|
||||
sess, err = NewSession(apiUrl, hclient, tls)
|
||||
if err == nil {
|
||||
client = &Client{session: sess, ApiUrl: apiUrl}
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
|
||||
func (c *Client) Login(username string, password string) (err error) {
|
||||
c.Username = username
|
||||
c.Password = password
|
||||
return c.session.Login(username, password)
|
||||
}
|
||||
|
||||
func (c *Client) GetJsonRetryable(url string, data *map[string]interface{}, tries int) error {
|
||||
var statErr error
|
||||
for ii := 0; ii < tries; ii++ {
|
||||
_, statErr = c.session.GetJSON(url, nil, nil, data)
|
||||
if statErr == nil {
|
||||
return nil
|
||||
}
|
||||
// if statErr != io.ErrUnexpectedEOF { // don't give up on ErrUnexpectedEOF
|
||||
// return statErr
|
||||
// }
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
return statErr
|
||||
}
|
||||
|
||||
func (c *Client) GetNodeList() (list map[string]interface{}, err error) {
|
||||
err = c.GetJsonRetryable("/nodes", &list, 3)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) GetVmList() (list map[string]interface{}, err error) {
|
||||
err = c.GetJsonRetryable("/cluster/resources?type=vm", &list, 3)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) CheckVmRef(vmr *VmRef) (err error) {
|
||||
if vmr.node == "" || vmr.vmType == "" {
|
||||
_, err = c.GetVmInfo(vmr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) GetVmInfo(vmr *VmRef) (vmInfo map[string]interface{}, err error) {
|
||||
resp, err := c.GetVmList()
|
||||
vms := resp["data"].([]interface{})
|
||||
for vmii := range vms {
|
||||
vm := vms[vmii].(map[string]interface{})
|
||||
if int(vm["vmid"].(float64)) == vmr.vmId {
|
||||
vmInfo = vm
|
||||
vmr.node = vmInfo["node"].(string)
|
||||
vmr.vmType = vmInfo["type"].(string)
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("Vm '%d' not found", vmr.vmId))
|
||||
}
|
||||
|
||||
func (c *Client) GetVmRefByName(vmName string) (vmr *VmRef, err error) {
|
||||
resp, err := c.GetVmList()
|
||||
vms := resp["data"].([]interface{})
|
||||
for vmii := range vms {
|
||||
vm := vms[vmii].(map[string]interface{})
|
||||
if vm["name"] != nil && vm["name"].(string) == vmName {
|
||||
vmr = NewVmRef(int(vm["vmid"].(float64)))
|
||||
vmr.node = vm["node"].(string)
|
||||
vmr.vmType = vm["type"].(string)
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("Vm '%s' not found", vmName))
|
||||
}
|
||||
|
||||
func (c *Client) GetVmState(vmr *VmRef) (vmState map[string]interface{}, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/status/current", vmr.node, vmr.vmType, vmr.vmId)
|
||||
err = c.GetJsonRetryable(url, &data, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data["data"] == nil {
|
||||
return nil, errors.New("Vm STATE not readable")
|
||||
}
|
||||
vmState = data["data"].(map[string]interface{})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) GetVmConfig(vmr *VmRef) (vmConfig map[string]interface{}, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
|
||||
err = c.GetJsonRetryable(url, &data, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data["data"] == nil {
|
||||
return nil, errors.New("Vm CONFIG not readable")
|
||||
}
|
||||
vmConfig = data["data"].(map[string]interface{})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) GetVmSpiceProxy(vmr *VmRef) (vmSpiceProxy map[string]interface{}, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/spiceproxy", vmr.node, vmr.vmType, vmr.vmId)
|
||||
_, err = c.session.PostJSON(url, nil, nil, nil, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data["data"] == nil {
|
||||
return nil, errors.New("Vm SpiceProxy not readable")
|
||||
}
|
||||
vmSpiceProxy = data["data"].(map[string]interface{})
|
||||
return
|
||||
}
|
||||
|
||||
type AgentNetworkInterface struct {
|
||||
MACAddress string
|
||||
IPAddresses []net.IP
|
||||
Name string
|
||||
Statistics map[string]int64
|
||||
}
|
||||
|
||||
func (a *AgentNetworkInterface) UnmarshalJSON(b []byte) error {
|
||||
var intermediate struct {
|
||||
HardwareAddress string `json:"hardware-address"`
|
||||
IPAddresses []struct {
|
||||
IPAddress string `json:"ip-address"`
|
||||
IPAddressType string `json:"ip-address-type"`
|
||||
Prefix int `json:"prefix"`
|
||||
} `json:"ip-addresses"`
|
||||
Name string `json:"name"`
|
||||
Statistics map[string]int64 `json:"statistics"`
|
||||
}
|
||||
err := json.Unmarshal(b, &intermediate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.IPAddresses = make([]net.IP, len(intermediate.IPAddresses))
|
||||
for idx, ip := range intermediate.IPAddresses {
|
||||
a.IPAddresses[idx] = net.ParseIP(ip.IPAddress)
|
||||
if a.IPAddresses[idx] == nil {
|
||||
return fmt.Errorf("Could not parse %s as IP", ip.IPAddress)
|
||||
}
|
||||
}
|
||||
a.MACAddress = intermediate.HardwareAddress
|
||||
a.Name = intermediate.Name
|
||||
a.Statistics = intermediate.Statistics
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) GetVmAgentNetworkInterfaces(vmr *VmRef) ([]AgentNetworkInterface, error) {
|
||||
var ifs []AgentNetworkInterface
|
||||
err := c.doAgentGet(vmr, "network-get-interfaces", &ifs)
|
||||
return ifs, err
|
||||
}
|
||||
|
||||
func (c *Client) doAgentGet(vmr *VmRef, command string, output interface{}) error {
|
||||
err := c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/agent/%s", vmr.node, vmr.vmType, vmr.vmId, command)
|
||||
resp, err := c.session.Get(url, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return TypedResponse(resp, output)
|
||||
}
|
||||
|
||||
func (c *Client) CreateTemplate(vmr *VmRef) error {
|
||||
err := c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/template", vmr.node, vmr.vmType, vmr.vmId)
|
||||
_, err = c.session.Post(url, nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) MonitorCmd(vmr *VmRef, command string) (monitorRes map[string]interface{}, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqbody := ParamsToBody(map[string]interface{}{"command": command})
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/monitor", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
monitorRes, err = ResponseJSON(resp)
|
||||
return
|
||||
}
|
||||
|
||||
// WaitForCompletion - poll the API for task completion
|
||||
func (c *Client) WaitForCompletion(taskResponse map[string]interface{}) (waitExitStatus string, err error) {
|
||||
if taskResponse["errors"] != nil {
|
||||
errJSON, _ := json.MarshalIndent(taskResponse["errors"], "", " ")
|
||||
return string(errJSON), errors.New("Error reponse")
|
||||
}
|
||||
if taskResponse["data"] == nil {
|
||||
return "", nil
|
||||
}
|
||||
waited := 0
|
||||
taskUpid := taskResponse["data"].(string)
|
||||
for waited < TaskTimeout {
|
||||
exitStatus, statErr := c.GetTaskExitstatus(taskUpid)
|
||||
if statErr != nil {
|
||||
if statErr != io.ErrUnexpectedEOF { // don't give up on ErrUnexpectedEOF
|
||||
return "", statErr
|
||||
}
|
||||
}
|
||||
if exitStatus != nil {
|
||||
waitExitStatus = exitStatus.(string)
|
||||
return
|
||||
}
|
||||
time.Sleep(TaskStatusCheckInterval * time.Second)
|
||||
waited = waited + TaskStatusCheckInterval
|
||||
}
|
||||
return "", errors.New("Wait timeout for:" + taskUpid)
|
||||
}
|
||||
|
||||
var rxTaskNode = regexp.MustCompile("UPID:(.*?):")
|
||||
|
||||
func (c *Client) GetTaskExitstatus(taskUpid string) (exitStatus interface{}, err error) {
|
||||
node := rxTaskNode.FindStringSubmatch(taskUpid)[1]
|
||||
url := fmt.Sprintf("/nodes/%s/tasks/%s/status", node, taskUpid)
|
||||
var data map[string]interface{}
|
||||
_, err = c.session.GetJSON(url, nil, nil, &data)
|
||||
if err == nil {
|
||||
exitStatus = data["data"].(map[string]interface{})["exitstatus"]
|
||||
}
|
||||
if exitStatus != nil && exitStatus != exitStatusSuccess {
|
||||
err = errors.New(exitStatus.(string))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) StatusChangeVm(vmr *VmRef, setStatus string) (exitStatus string, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/status/%s", vmr.node, vmr.vmType, vmr.vmId, setStatus)
|
||||
var taskResponse map[string]interface{}
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err = c.session.PostJSON(url, nil, nil, nil, &taskResponse)
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
if exitStatus == "" {
|
||||
time.Sleep(TaskStatusCheckInterval * time.Second)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) StartVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "start")
|
||||
}
|
||||
|
||||
func (c *Client) StopVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "stop")
|
||||
}
|
||||
|
||||
func (c *Client) ShutdownVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "shutdown")
|
||||
}
|
||||
|
||||
func (c *Client) ResetVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "reset")
|
||||
}
|
||||
|
||||
func (c *Client) SuspendVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "suspend")
|
||||
}
|
||||
|
||||
func (c *Client) ResumeVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
return c.StatusChangeVm(vmr, "resume")
|
||||
}
|
||||
|
||||
func (c *Client) DeleteVm(vmr *VmRef) (exitStatus string, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d", vmr.node, vmr.vmType, vmr.vmId)
|
||||
var taskResponse map[string]interface{}
|
||||
_, err = c.session.RequestJSON("DELETE", url, nil, nil, nil, &taskResponse)
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) CreateQemuVm(node string, vmParams map[string]interface{}) (exitStatus string, err error) {
|
||||
// Create VM disks first to ensure disks names.
|
||||
createdDisks, createdDisksErr := c.createVMDisks(node, vmParams)
|
||||
if createdDisksErr != nil {
|
||||
return "", createdDisksErr
|
||||
}
|
||||
|
||||
// Then create the VM itself.
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/qemu", node)
|
||||
var resp *http.Response
|
||||
resp, err = c.session.Post(url, nil, nil, &reqbody)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
exitStatus = string(b)
|
||||
return
|
||||
}
|
||||
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
// Delete VM disks if the VM didn't create.
|
||||
if exitStatus != "OK" {
|
||||
deleteDisksErr := c.DeleteVMDisks(node, createdDisks)
|
||||
if deleteDisksErr != nil {
|
||||
return "", deleteDisksErr
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) CloneQemuVm(vmr *VmRef, vmParams map[string]interface{}) (exitStatus string, err error) {
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/qemu/%d/clone", vmr.node, vmr.vmId)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) RollbackQemuVm(vmr *VmRef, snapshot string) (exitStatus string, err error) {
|
||||
err = c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/snapshot/%s/rollback", vmr.node, vmr.vmType, vmr.vmId, snapshot)
|
||||
var taskResponse map[string]interface{}
|
||||
_, err = c.session.PostJSON(url, nil, nil, nil, &taskResponse)
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// SetVmConfig - send config options
|
||||
func (c *Client) SetVmConfig(vmr *VmRef, vmParams map[string]interface{}) (exitStatus interface{}, err error) {
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitStatus interface{}, err error) {
|
||||
// PUT
|
||||
//disk:virtio0
|
||||
//size:+2G
|
||||
if disk == "" {
|
||||
disk = "virtio0"
|
||||
}
|
||||
size := fmt.Sprintf("+%dG", moreSizeGB)
|
||||
reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "size": size})
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/resize", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Put(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetNextID - Get next free VMID
|
||||
func (c *Client) GetNextID(currentID int) (nextID int, err error) {
|
||||
var data map[string]interface{}
|
||||
var url string
|
||||
if currentID >= 100 {
|
||||
url = fmt.Sprintf("/cluster/nextid?vmid=%d", currentID)
|
||||
} else {
|
||||
url = "/cluster/nextid"
|
||||
}
|
||||
_, err = c.session.GetJSON(url, nil, nil, &data)
|
||||
if err == nil {
|
||||
if data["errors"] != nil {
|
||||
if currentID >= 100 {
|
||||
return c.GetNextID(currentID + 1)
|
||||
} else {
|
||||
return -1, errors.New("error using /cluster/nextid")
|
||||
}
|
||||
}
|
||||
nextID, err = strconv.Atoi(data["data"].(string))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateVMDisk - Create single disk for VM on host node.
|
||||
func (c *Client) CreateVMDisk(
|
||||
nodeName string,
|
||||
storageName string,
|
||||
fullDiskName string,
|
||||
diskParams map[string]interface{},
|
||||
) error {
|
||||
|
||||
reqbody := ParamsToBody(diskParams)
|
||||
url := fmt.Sprintf("/nodes/%s/storage/%s/content", nodeName, storageName)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if diskName, containsData := taskResponse["data"]; !containsData || diskName != fullDiskName {
|
||||
return errors.New(fmt.Sprintf("Cannot create VM disk %s", fullDiskName))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createVMDisks - Make disks parameters and create all VM disks on host node.
|
||||
func (c *Client) createVMDisks(
|
||||
node string,
|
||||
vmParams map[string]interface{},
|
||||
) (disks []string, err error) {
|
||||
var createdDisks []string
|
||||
vmID := vmParams["vmid"].(int)
|
||||
for deviceName, deviceConf := range vmParams {
|
||||
rxStorageModels := `(ide|sata|scsi|virtio)\d+`
|
||||
if matched, _ := regexp.MatchString(rxStorageModels, deviceName); matched {
|
||||
deviceConfMap := ParseConf(deviceConf.(string), ",", "=")
|
||||
// This if condition to differentiate between `disk` and `cdrom`.
|
||||
if media, containsFile := deviceConfMap["media"]; containsFile && media == "disk" {
|
||||
fullDiskName := deviceConfMap["file"].(string)
|
||||
storageName, volumeName := getStorageAndVolumeName(fullDiskName, ":")
|
||||
diskParams := map[string]interface{}{
|
||||
"vmid": vmID,
|
||||
"filename": volumeName,
|
||||
"size": deviceConfMap["size"],
|
||||
}
|
||||
err := c.CreateVMDisk(node, storageName, fullDiskName, diskParams)
|
||||
if err != nil {
|
||||
return createdDisks, err
|
||||
} else {
|
||||
createdDisks = append(createdDisks, fullDiskName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createdDisks, nil
|
||||
}
|
||||
|
||||
// DeleteVMDisks - Delete VM disks from host node.
|
||||
// By default the VM disks are deteled when the VM is deleted,
|
||||
// so mainly this is used to delete the disks in case VM creation didn't complete.
|
||||
func (c *Client) DeleteVMDisks(
|
||||
node string,
|
||||
disks []string,
|
||||
) error {
|
||||
for _, fullDiskName := range disks {
|
||||
storageName, volumeName := getStorageAndVolumeName(fullDiskName, ":")
|
||||
url := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", node, storageName, volumeName)
|
||||
_, err := c.session.Post(url, nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getStorageAndVolumeName - Extract disk storage and disk volume, since disk name is saved
|
||||
// in Proxmox with its storage.
|
||||
func getStorageAndVolumeName(
|
||||
fullDiskName string,
|
||||
separator string,
|
||||
) (storageName string, diskName string) {
|
||||
storageAndVolumeName := strings.Split(fullDiskName, separator)
|
||||
storageName, volumeName := storageAndVolumeName[0], storageAndVolumeName[1]
|
||||
|
||||
// when disk type is dir, volumeName is `file=local:100/vm-100-disk-0.raw`
|
||||
re := regexp.MustCompile(`\d+/(?P<filename>\S+.\S+)`)
|
||||
match := re.FindStringSubmatch(volumeName)
|
||||
if len(match) == 2 {
|
||||
volumeName = match[1]
|
||||
}
|
||||
|
||||
return storageName, volumeName
|
||||
}
|
|
@ -1,706 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
QemuDevices map[int]map[string]interface{}
|
||||
QemuDevice map[string]interface{}
|
||||
QemuDeviceParam []string
|
||||
)
|
||||
|
||||
// ConfigQemu - Proxmox API QEMU options
|
||||
type ConfigQemu struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
Onboot bool `json:"onboot"`
|
||||
Agent string `json:"agent"`
|
||||
Memory int `json:"memory"`
|
||||
QemuOs string `json:"os"`
|
||||
QemuCores int `json:"cores"`
|
||||
QemuSockets int `json:"sockets"`
|
||||
QemuIso string `json:"iso"`
|
||||
FullClone *int `json:"fullclone"`
|
||||
QemuDisks QemuDevices `json:"disk"`
|
||||
QemuNetworks QemuDevices `json:"network"`
|
||||
|
||||
// Deprecated single disk.
|
||||
DiskSize float64 `json:"diskGB"`
|
||||
Storage string `json:"storage"`
|
||||
StorageType string `json:"storageType"` // virtio|scsi (cloud-init defaults to scsi)
|
||||
|
||||
// Deprecated single nic.
|
||||
QemuNicModel string `json:"nic"`
|
||||
QemuBrige string `json:"bridge"`
|
||||
QemuVlanTag int `json:"vlan"`
|
||||
QemuMacAddr string `json:"mac"`
|
||||
|
||||
// cloud-init options
|
||||
CIuser string `json:"ciuser"`
|
||||
CIpassword string `json:"cipassword"`
|
||||
|
||||
Searchdomain string `json:"searchdomain"`
|
||||
Nameserver string `json:"nameserver"`
|
||||
Sshkeys string `json:"sshkeys"`
|
||||
|
||||
// arrays are hard, support 2 interfaces for now
|
||||
Ipconfig0 string `json:"ipconfig0"`
|
||||
Ipconfig1 string `json:"ipconfig1"`
|
||||
}
|
||||
|
||||
// CreateVm - Tell Proxmox API to make the VM
|
||||
func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
|
||||
if config.HasCloudInit() {
|
||||
return errors.New("Cloud-init parameters only supported on clones or updates")
|
||||
}
|
||||
vmr.SetVmType("qemu")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"vmid": vmr.vmId,
|
||||
"name": config.Name,
|
||||
"onboot": config.Onboot,
|
||||
"agent": config.Agent,
|
||||
"ide2": config.QemuIso + ",media=cdrom",
|
||||
"ostype": config.QemuOs,
|
||||
"sockets": config.QemuSockets,
|
||||
"cores": config.QemuCores,
|
||||
"cpu": "host",
|
||||
"memory": config.Memory,
|
||||
"description": config.Description,
|
||||
}
|
||||
|
||||
// Create disks config.
|
||||
config.CreateQemuDisksParams(vmr.vmId, params, false)
|
||||
|
||||
// Create networks config.
|
||||
config.CreateQemuNetworksParams(vmr.vmId, params)
|
||||
|
||||
exitStatus, err := client.CreateQemuVm(vmr.node, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating VM: %v, error status: %s (params: %v)", err, exitStatus, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasCloudInit - are there cloud-init options?
|
||||
func (config ConfigQemu) HasCloudInit() bool {
|
||||
return config.CIuser != "" ||
|
||||
config.CIpassword != "" ||
|
||||
config.Searchdomain != "" ||
|
||||
config.Nameserver != "" ||
|
||||
config.Sshkeys != "" ||
|
||||
config.Ipconfig0 != "" ||
|
||||
config.Ipconfig1 != ""
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
CloneVm
|
||||
Example: Request
|
||||
|
||||
nodes/proxmox1-xx/qemu/1012/clone
|
||||
|
||||
newid:145
|
||||
name:tf-clone1
|
||||
target:proxmox1-xx
|
||||
full:1
|
||||
storage:xxx
|
||||
|
||||
*/
|
||||
func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (err error) {
|
||||
vmr.SetVmType("qemu")
|
||||
fullclone := "1"
|
||||
if config.FullClone != nil {
|
||||
fullclone = strconv.Itoa(*config.FullClone)
|
||||
}
|
||||
storage := config.Storage
|
||||
if disk0Storage, ok := config.QemuDisks[0]["storage"].(string); ok && len(disk0Storage) > 0 {
|
||||
storage = disk0Storage
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"newid": vmr.vmId,
|
||||
"target": vmr.node,
|
||||
"name": config.Name,
|
||||
"storage": storage,
|
||||
"full": fullclone,
|
||||
}
|
||||
_, err = client.CloneQemuVm(sourceVmr, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return config.UpdateConfig(vmr, client)
|
||||
}
|
||||
|
||||
func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
||||
configParams := map[string]interface{}{
|
||||
"name": config.Name,
|
||||
"description": config.Description,
|
||||
"onboot": config.Onboot,
|
||||
"agent": config.Agent,
|
||||
"sockets": config.QemuSockets,
|
||||
"cores": config.QemuCores,
|
||||
"memory": config.Memory,
|
||||
}
|
||||
|
||||
// Create disks config.
|
||||
config.CreateQemuDisksParams(vmr.vmId, configParams, true)
|
||||
|
||||
// Create networks config.
|
||||
config.CreateQemuNetworksParams(vmr.vmId, configParams)
|
||||
|
||||
// cloud-init options
|
||||
if config.CIuser != "" {
|
||||
configParams["ciuser"] = config.CIuser
|
||||
}
|
||||
if config.CIpassword != "" {
|
||||
configParams["cipassword"] = config.CIpassword
|
||||
}
|
||||
if config.Searchdomain != "" {
|
||||
configParams["searchdomain"] = config.Searchdomain
|
||||
}
|
||||
if config.Nameserver != "" {
|
||||
configParams["nameserver"] = config.Nameserver
|
||||
}
|
||||
if config.Sshkeys != "" {
|
||||
sshkeyEnc := url.PathEscape(config.Sshkeys + "\n")
|
||||
sshkeyEnc = strings.Replace(sshkeyEnc, "+", "%2B", -1)
|
||||
sshkeyEnc = strings.Replace(sshkeyEnc, "@", "%40", -1)
|
||||
sshkeyEnc = strings.Replace(sshkeyEnc, "=", "%3D", -1)
|
||||
configParams["sshkeys"] = sshkeyEnc
|
||||
}
|
||||
if config.Ipconfig0 != "" {
|
||||
configParams["ipconfig0"] = config.Ipconfig0
|
||||
}
|
||||
if config.Ipconfig1 != "" {
|
||||
configParams["ipconfig1"] = config.Ipconfig1
|
||||
}
|
||||
_, err = client.SetVmConfig(vmr, configParams)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
|
||||
config = &ConfigQemu{QemuVlanTag: -1}
|
||||
err = json.NewDecoder(io).Decode(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
log.Println(config)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
rxIso = regexp.MustCompile(`(.*?),media`)
|
||||
rxDeviceID = regexp.MustCompile(`\d+`)
|
||||
rxDiskName = regexp.MustCompile(`(virtio|scsi)\d+`)
|
||||
rxDiskType = regexp.MustCompile(`\D+`)
|
||||
rxNicName = regexp.MustCompile(`net\d+`)
|
||||
)
|
||||
|
||||
func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
|
||||
var vmConfig map[string]interface{}
|
||||
for ii := 0; ii < 3; ii++ {
|
||||
vmConfig, err = client.GetVmConfig(vmr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
// this can happen:
|
||||
// {"data":{"lock":"clone","digest":"eb54fb9d9f120ba0c3bdf694f73b10002c375c38","description":" qmclone temporary file\n"}})
|
||||
if vmConfig["lock"] == nil {
|
||||
break
|
||||
} else {
|
||||
time.Sleep(8 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
if vmConfig["lock"] != nil {
|
||||
return nil, errors.New("vm locked, could not obtain config")
|
||||
}
|
||||
|
||||
// vmConfig Sample: map[ cpu:host
|
||||
// net0:virtio=62:DF:XX:XX:XX:XX,bridge=vmbr0
|
||||
// ide2:local:iso/xxx-xx.iso,media=cdrom memory:2048
|
||||
// smbios1:uuid=8b3bf833-aad8-4545-xxx-xxxxxxx digest:aa6ce5xxxxx1b9ce33e4aaeff564d4 sockets:1
|
||||
// name:terraform-ubuntu1404-template bootdisk:virtio0
|
||||
// virtio0:ProxmoxxxxISCSI:vm-1014-disk-2,size=4G
|
||||
// description:Base image
|
||||
// cores:2 ostype:l26
|
||||
|
||||
name := ""
|
||||
if _, isSet := vmConfig["name"]; isSet {
|
||||
name = vmConfig["name"].(string)
|
||||
}
|
||||
description := ""
|
||||
if _, isSet := vmConfig["description"]; isSet {
|
||||
description = vmConfig["description"].(string)
|
||||
}
|
||||
onboot := true
|
||||
if _, isSet := vmConfig["onboot"]; isSet {
|
||||
onboot = Itob(int(vmConfig["onboot"].(float64)))
|
||||
}
|
||||
agent := "1"
|
||||
if _, isSet := vmConfig["agent"]; isSet {
|
||||
agent = vmConfig["agent"].(string)
|
||||
}
|
||||
ostype := "other"
|
||||
if _, isSet := vmConfig["ostype"]; isSet {
|
||||
ostype = vmConfig["ostype"].(string)
|
||||
}
|
||||
memory := 0.0
|
||||
if _, isSet := vmConfig["memory"]; isSet {
|
||||
memory = vmConfig["memory"].(float64)
|
||||
}
|
||||
cores := 1.0
|
||||
if _, isSet := vmConfig["cores"]; isSet {
|
||||
cores = vmConfig["cores"].(float64)
|
||||
}
|
||||
sockets := 1.0
|
||||
if _, isSet := vmConfig["sockets"]; isSet {
|
||||
sockets = vmConfig["sockets"].(float64)
|
||||
}
|
||||
config = &ConfigQemu{
|
||||
Name: name,
|
||||
Description: strings.TrimSpace(description),
|
||||
Onboot: onboot,
|
||||
Agent: agent,
|
||||
QemuOs: ostype,
|
||||
Memory: int(memory),
|
||||
QemuCores: int(cores),
|
||||
QemuSockets: int(sockets),
|
||||
QemuVlanTag: -1,
|
||||
QemuDisks: QemuDevices{},
|
||||
QemuNetworks: QemuDevices{},
|
||||
}
|
||||
|
||||
if vmConfig["ide2"] != nil {
|
||||
isoMatch := rxIso.FindStringSubmatch(vmConfig["ide2"].(string))
|
||||
config.QemuIso = isoMatch[1]
|
||||
}
|
||||
|
||||
if _, isSet := vmConfig["ciuser"]; isSet {
|
||||
config.CIuser = vmConfig["ciuser"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["cipassword"]; isSet {
|
||||
config.CIpassword = vmConfig["cipassword"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["searchdomain"]; isSet {
|
||||
config.Searchdomain = vmConfig["searchdomain"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["sshkeys"]; isSet {
|
||||
config.Sshkeys, _ = url.PathUnescape(vmConfig["sshkeys"].(string))
|
||||
}
|
||||
if _, isSet := vmConfig["ipconfig0"]; isSet {
|
||||
config.Ipconfig0 = vmConfig["ipconfig0"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["ipconfig1"]; isSet {
|
||||
config.Ipconfig1 = vmConfig["ipconfig1"].(string)
|
||||
}
|
||||
|
||||
// Add disks.
|
||||
diskNames := []string{}
|
||||
|
||||
for k, _ := range vmConfig {
|
||||
if diskName := rxDiskName.FindStringSubmatch(k); len(diskName) > 0 {
|
||||
diskNames = append(diskNames, diskName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, diskName := range diskNames {
|
||||
diskConfStr := vmConfig[diskName]
|
||||
diskConfList := strings.Split(diskConfStr.(string), ",")
|
||||
|
||||
//
|
||||
id := rxDeviceID.FindStringSubmatch(diskName)
|
||||
diskID, _ := strconv.Atoi(id[0])
|
||||
diskType := rxDiskType.FindStringSubmatch(diskName)[0]
|
||||
storageName, fileName := ParseSubConf(diskConfList[0], ":")
|
||||
|
||||
//
|
||||
diskConfMap := QemuDevice{
|
||||
"type": diskType,
|
||||
"storage": storageName,
|
||||
"file": fileName,
|
||||
}
|
||||
|
||||
// Add rest of device config.
|
||||
diskConfMap.readDeviceConfig(diskConfList[1:])
|
||||
|
||||
// And device config to disks map.
|
||||
if len(diskConfMap) > 0 {
|
||||
config.QemuDisks[diskID] = diskConfMap
|
||||
}
|
||||
}
|
||||
|
||||
// Add networks.
|
||||
nicNameRe := regexp.MustCompile(`net\d+`)
|
||||
nicNames := []string{}
|
||||
|
||||
for k, _ := range vmConfig {
|
||||
if nicName := nicNameRe.FindStringSubmatch(k); len(nicName) > 0 {
|
||||
nicNames = append(nicNames, nicName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, nicName := range nicNames {
|
||||
nicConfStr := vmConfig[nicName]
|
||||
nicConfList := strings.Split(nicConfStr.(string), ",")
|
||||
|
||||
//
|
||||
id := rxDeviceID.FindStringSubmatch(nicName)
|
||||
nicID, _ := strconv.Atoi(id[0])
|
||||
model, macaddr := ParseSubConf(nicConfList[0], "=")
|
||||
|
||||
// Add model and MAC address.
|
||||
nicConfMap := QemuDevice{
|
||||
"model": model,
|
||||
"macaddr": macaddr,
|
||||
}
|
||||
|
||||
// Add rest of device config.
|
||||
nicConfMap.readDeviceConfig(nicConfList[1:])
|
||||
|
||||
// And device config to networks.
|
||||
if len(nicConfMap) > 0 {
|
||||
config.QemuNetworks[nicID] = nicConfMap
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Useful waiting for ISO install to complete
|
||||
func WaitForShutdown(vmr *VmRef, client *Client) (err error) {
|
||||
for ii := 0; ii < 100; ii++ {
|
||||
vmState, err := client.GetVmState(vmr)
|
||||
if err != nil {
|
||||
log.Print("Wait error:")
|
||||
log.Println(err)
|
||||
} else if vmState["status"] == "stopped" {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
return errors.New("Not shutdown within wait time")
|
||||
}
|
||||
|
||||
// This is because proxmox create/config API won't let us make usernet devices
|
||||
func SshForwardUsernet(vmr *VmRef, client *Client) (sshPort string, err error) {
|
||||
vmState, err := client.GetVmState(vmr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if vmState["status"] == "stopped" {
|
||||
return "", errors.New("VM must be running first")
|
||||
}
|
||||
sshPort = strconv.Itoa(vmr.VmId() + 22000)
|
||||
_, err = client.MonitorCmd(vmr, "netdev_add user,id=net1,hostfwd=tcp::"+sshPort+"-:22")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = client.MonitorCmd(vmr, "device_add virtio-net-pci,id=net1,netdev=net1,addr=0x13")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// device_del net1
|
||||
// netdev_del net1
|
||||
func RemoveSshForwardUsernet(vmr *VmRef, client *Client) (err error) {
|
||||
vmState, err := client.GetVmState(vmr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vmState["status"] == "stopped" {
|
||||
return errors.New("VM must be running first")
|
||||
}
|
||||
_, err = client.MonitorCmd(vmr, "device_del net1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.MonitorCmd(vmr, "netdev_del net1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MaxVmId(client *Client) (max int, err error) {
|
||||
resp, err := client.GetVmList()
|
||||
vms := resp["data"].([]interface{})
|
||||
max = 0
|
||||
for vmii := range vms {
|
||||
vm := vms[vmii].(map[string]interface{})
|
||||
vmid := int(vm["vmid"].(float64))
|
||||
if vmid > max {
|
||||
max = vmid
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SendKeysString(vmr *VmRef, client *Client, keys string) (err error) {
|
||||
vmState, err := client.GetVmState(vmr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vmState["status"] == "stopped" {
|
||||
return errors.New("VM must be running first")
|
||||
}
|
||||
for _, r := range keys {
|
||||
c := string(r)
|
||||
lower := strings.ToLower(c)
|
||||
if c != lower {
|
||||
c = "shift-" + lower
|
||||
} else {
|
||||
switch c {
|
||||
case "!":
|
||||
c = "shift-1"
|
||||
case "@":
|
||||
c = "shift-2"
|
||||
case "#":
|
||||
c = "shift-3"
|
||||
case "$":
|
||||
c = "shift-4"
|
||||
case "%%":
|
||||
c = "shift-5"
|
||||
case "^":
|
||||
c = "shift-6"
|
||||
case "&":
|
||||
c = "shift-7"
|
||||
case "*":
|
||||
c = "shift-8"
|
||||
case "(":
|
||||
c = "shift-9"
|
||||
case ")":
|
||||
c = "shift-0"
|
||||
case "_":
|
||||
c = "shift-minus"
|
||||
case "+":
|
||||
c = "shift-equal"
|
||||
case " ":
|
||||
c = "spc"
|
||||
case "/":
|
||||
c = "slash"
|
||||
case "\\":
|
||||
c = "backslash"
|
||||
case ",":
|
||||
c = "comma"
|
||||
case "-":
|
||||
c = "minus"
|
||||
case "=":
|
||||
c = "equal"
|
||||
case ".":
|
||||
c = "dot"
|
||||
case "?":
|
||||
c = "shift-slash"
|
||||
}
|
||||
}
|
||||
_, err = client.MonitorCmd(vmr, "sendkey "+c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(100)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create parameters for each Nic device.
|
||||
func (c ConfigQemu) CreateQemuNetworksParams(vmID int, params map[string]interface{}) error {
|
||||
|
||||
// For backward compatibility.
|
||||
if len(c.QemuNetworks) == 0 && len(c.QemuNicModel) > 0 {
|
||||
deprecatedStyleMap := QemuDevice{
|
||||
"model": c.QemuNicModel,
|
||||
"bridge": c.QemuBrige,
|
||||
"macaddr": c.QemuMacAddr,
|
||||
}
|
||||
|
||||
if c.QemuVlanTag > 0 {
|
||||
deprecatedStyleMap["tag"] = strconv.Itoa(c.QemuVlanTag)
|
||||
}
|
||||
|
||||
c.QemuNetworks[0] = deprecatedStyleMap
|
||||
}
|
||||
|
||||
// For new style with multi net device.
|
||||
for nicID, nicConfMap := range c.QemuNetworks {
|
||||
|
||||
nicConfParam := QemuDeviceParam{}
|
||||
|
||||
// Set Nic name.
|
||||
qemuNicName := "net" + strconv.Itoa(nicID)
|
||||
|
||||
// Set Mac address.
|
||||
if nicConfMap["macaddr"] == nil || nicConfMap["macaddr"].(string) == "" {
|
||||
// Generate Mac based on VmID and NicID so it will be the same always.
|
||||
macaddr := make(net.HardwareAddr, 6)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Read(macaddr)
|
||||
macaddr[0] = (macaddr[0] | 2) & 0xfe // fix from github issue #18
|
||||
macAddrUppr := strings.ToUpper(fmt.Sprintf("%v", macaddr))
|
||||
// use model=mac format for older proxmox compatability
|
||||
macAddr := fmt.Sprintf("%v=%v", nicConfMap["model"], macAddrUppr)
|
||||
|
||||
// Add Mac to source map so it will be returned. (useful for some use case like Terraform)
|
||||
nicConfMap["macaddr"] = macAddrUppr
|
||||
// and also add it to the parameters which will be sent to Proxmox API.
|
||||
nicConfParam = append(nicConfParam, macAddr)
|
||||
} else {
|
||||
macAddr := fmt.Sprintf("%v=%v", nicConfMap["model"], nicConfMap["macaddr"].(string))
|
||||
nicConfParam = append(nicConfParam, macAddr)
|
||||
}
|
||||
|
||||
// Set bridge if not nat.
|
||||
if nicConfMap["bridge"].(string) != "nat" {
|
||||
bridge := fmt.Sprintf("bridge=%v", nicConfMap["bridge"])
|
||||
nicConfParam = append(nicConfParam, bridge)
|
||||
}
|
||||
|
||||
// Keys that are not used as real/direct conf.
|
||||
ignoredKeys := []string{"id", "bridge", "macaddr", "model"}
|
||||
|
||||
// Rest of config.
|
||||
nicConfParam = nicConfParam.createDeviceParam(nicConfMap, ignoredKeys)
|
||||
|
||||
// Add nic to Qemu prams.
|
||||
params[qemuNicName] = strings.Join(nicConfParam, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create parameters for each disk.
|
||||
func (c ConfigQemu) CreateQemuDisksParams(
|
||||
vmID int,
|
||||
params map[string]interface{},
|
||||
cloned bool,
|
||||
) error {
|
||||
|
||||
// For backward compatibility.
|
||||
if len(c.QemuDisks) == 0 && len(c.Storage) > 0 {
|
||||
|
||||
dType := c.StorageType
|
||||
if dType == "" {
|
||||
if c.HasCloudInit() {
|
||||
dType = "scsi"
|
||||
} else {
|
||||
dType = "virtio"
|
||||
}
|
||||
}
|
||||
deprecatedStyleMap := QemuDevice{
|
||||
"type": dType,
|
||||
"storage": c.Storage,
|
||||
"size": c.DiskSize,
|
||||
"storage_type": "lvm", // default old style
|
||||
"cache": "none", // default old value
|
||||
}
|
||||
|
||||
c.QemuDisks[0] = deprecatedStyleMap
|
||||
}
|
||||
|
||||
// For new style with multi disk device.
|
||||
for diskID, diskConfMap := range c.QemuDisks {
|
||||
|
||||
// skip the first disk for clones (may not always be right, but a template probably has at least 1 disk)
|
||||
if diskID == 0 && cloned {
|
||||
continue
|
||||
}
|
||||
diskConfParam := QemuDeviceParam{
|
||||
"media=disk",
|
||||
}
|
||||
|
||||
// Device name.
|
||||
deviceType := diskConfMap["type"].(string)
|
||||
qemuDiskName := deviceType + strconv.Itoa(diskID)
|
||||
|
||||
// Set disk storage.
|
||||
// Disk size.
|
||||
diskSizeGB := fmt.Sprintf("size=%v", diskConfMap["size"])
|
||||
diskConfParam = append(diskConfParam, diskSizeGB)
|
||||
|
||||
// Disk name.
|
||||
var diskFile string
|
||||
// Currently ZFS local, LVM, and Directory are considered.
|
||||
// Other formats are not verified, but could be added if they're needed.
|
||||
rxStorageTypes := `(zfspool|lvm)`
|
||||
storageType := diskConfMap["storage_type"].(string)
|
||||
if matched, _ := regexp.MatchString(rxStorageTypes, storageType); matched {
|
||||
diskFile = fmt.Sprintf("file=%v:vm-%v-disk-%v", diskConfMap["storage"], vmID, diskID)
|
||||
} else {
|
||||
diskFile = fmt.Sprintf("file=%v:%v/vm-%v-disk-%v.%v", diskConfMap["storage"], vmID, vmID, diskID, diskConfMap["format"])
|
||||
}
|
||||
diskConfParam = append(diskConfParam, diskFile)
|
||||
|
||||
// Set cache if not none (default).
|
||||
if diskConfMap["cache"].(string) != "none" {
|
||||
diskCache := fmt.Sprintf("cache=%v", diskConfMap["cache"])
|
||||
diskConfParam = append(diskConfParam, diskCache)
|
||||
}
|
||||
|
||||
// Keys that are not used as real/direct conf.
|
||||
ignoredKeys := []string{"id", "type", "storage", "storage_type", "size", "cache"}
|
||||
|
||||
// Rest of config.
|
||||
diskConfParam = diskConfParam.createDeviceParam(diskConfMap, ignoredKeys)
|
||||
|
||||
// Add back to Qemu prams.
|
||||
params[qemuDiskName] = strings.Join(diskConfParam, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the parameters for each device that will be sent to Proxmox API.
|
||||
func (p QemuDeviceParam) createDeviceParam(
|
||||
deviceConfMap QemuDevice,
|
||||
ignoredKeys []string,
|
||||
) QemuDeviceParam {
|
||||
|
||||
for key, value := range deviceConfMap {
|
||||
if ignored := inArray(ignoredKeys, key); !ignored {
|
||||
var confValue interface{}
|
||||
if bValue, ok := value.(bool); ok && bValue {
|
||||
confValue = "1"
|
||||
} else if sValue, ok := value.(string); ok && len(sValue) > 0 {
|
||||
confValue = sValue
|
||||
} else if iValue, ok := value.(int); ok && iValue > 0 {
|
||||
confValue = iValue
|
||||
}
|
||||
if confValue != nil {
|
||||
deviceConf := fmt.Sprintf("%v=%v", key, confValue)
|
||||
p = append(p, deviceConf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// readDeviceConfig - get standard sub-conf strings where `key=value` and update conf map.
|
||||
func (confMap QemuDevice) readDeviceConfig(confList []string) error {
|
||||
// Add device config.
|
||||
for _, conf := range confList {
|
||||
key, value := ParseSubConf(conf, "=")
|
||||
confMap[key] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ConfigQemu) String() string {
|
||||
jsConf, _ := json.Marshal(c)
|
||||
return string(jsConf)
|
||||
}
|
|
@ -1,319 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
// inspired by https://github.com/openstack/golang-client/blob/master/openstack/session.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var Debug = new(bool)
|
||||
|
||||
type Response struct {
|
||||
Resp *http.Response
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
httpClient *http.Client
|
||||
ApiUrl string
|
||||
AuthTicket string
|
||||
CsrfToken string
|
||||
Headers http.Header
|
||||
}
|
||||
|
||||
func NewSession(apiUrl string, hclient *http.Client, tls *tls.Config) (session *Session, err error) {
|
||||
if hclient == nil {
|
||||
// Only build a transport if we're also building the client
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls,
|
||||
DisableCompression: true,
|
||||
}
|
||||
hclient = &http.Client{Transport: tr}
|
||||
}
|
||||
session = &Session{
|
||||
httpClient: hclient,
|
||||
ApiUrl: apiUrl,
|
||||
AuthTicket: "",
|
||||
CsrfToken: "",
|
||||
Headers: http.Header{},
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func ParamsToBody(params map[string]interface{}) (body []byte) {
|
||||
vals := url.Values{}
|
||||
for k, intrV := range params {
|
||||
var v string
|
||||
switch intrV.(type) {
|
||||
// Convert true/false bool to 1/0 string where Proxmox API can understand it.
|
||||
case bool:
|
||||
if intrV.(bool) {
|
||||
v = "1"
|
||||
} else {
|
||||
v = "0"
|
||||
}
|
||||
default:
|
||||
v = fmt.Sprintf("%v", intrV)
|
||||
}
|
||||
vals.Set(k, v)
|
||||
}
|
||||
body = bytes.NewBufferString(vals.Encode()).Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func decodeResponse(resp *http.Response, v interface{}) error {
|
||||
if resp.Body == nil {
|
||||
return nil
|
||||
}
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading response body: %s", err)
|
||||
}
|
||||
if err = json.Unmarshal(rbody, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ResponseJSON(resp *http.Response) (jbody map[string]interface{}, err error) {
|
||||
err = decodeResponse(resp, &jbody)
|
||||
return jbody, err
|
||||
}
|
||||
|
||||
func TypedResponse(resp *http.Response, v interface{}) error {
|
||||
var intermediate struct {
|
||||
Data struct {
|
||||
Result json.RawMessage `json:"result"`
|
||||
} `json:"data"`
|
||||
}
|
||||
err := decodeResponse(resp, &intermediate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading response envelope: %v", err)
|
||||
}
|
||||
if err = json.Unmarshal(intermediate.Data.Result, v); err != nil {
|
||||
return fmt.Errorf("error unmarshalling result %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Login(username string, password string) (err error) {
|
||||
reqbody := ParamsToBody(map[string]interface{}{"username": username, "password": password})
|
||||
olddebug := *Debug
|
||||
*Debug = false // don't share passwords in debug log
|
||||
resp, err := s.Post("/access/ticket", nil, nil, &reqbody)
|
||||
*Debug = olddebug
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil {
|
||||
return errors.New("Login error reading response")
|
||||
}
|
||||
dr, _ := httputil.DumpResponse(resp, true)
|
||||
jbody, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if jbody == nil || jbody["data"] == nil {
|
||||
return fmt.Errorf("Invalid login response:\n-----\n%s\n-----", dr)
|
||||
}
|
||||
dat := jbody["data"].(map[string]interface{})
|
||||
s.AuthTicket = dat["ticket"].(string)
|
||||
s.CsrfToken = dat["CSRFPreventionToken"].(string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) NewRequest(method, url string, headers *http.Header, body io.Reader) (req *http.Request, err error) {
|
||||
req, err = http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if headers != nil {
|
||||
req.Header = *headers
|
||||
}
|
||||
if s.AuthTicket != "" {
|
||||
req.Header.Add("Cookie", "PVEAuthCookie="+s.AuthTicket)
|
||||
req.Header.Add("CSRFPreventionToken", s.CsrfToken)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Session) Do(req *http.Request) (*http.Response, error) {
|
||||
// Add session headers
|
||||
for k := range s.Headers {
|
||||
req.Header.Set(k, s.Headers.Get(k))
|
||||
}
|
||||
|
||||
if *Debug {
|
||||
d, _ := httputil.DumpRequestOut(req, true)
|
||||
log.Printf(">>>>>>>>>> REQUEST:\n", string(d))
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *Debug {
|
||||
dr, _ := httputil.DumpResponse(resp, true)
|
||||
log.Printf("<<<<<<<<<< RESULT:\n", string(dr))
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
return resp, errors.New(resp.Status)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Perform a simple get to an endpoint
|
||||
func (s *Session) Request(
|
||||
method string,
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte,
|
||||
) (resp *http.Response, err error) {
|
||||
// add params to url here
|
||||
url = s.ApiUrl + url
|
||||
if params != nil {
|
||||
url = url + "?" + params.Encode()
|
||||
}
|
||||
|
||||
// Get the body if one is present
|
||||
var buf io.Reader
|
||||
if body != nil {
|
||||
buf = bytes.NewReader(*body)
|
||||
}
|
||||
|
||||
req, err := s.NewRequest(method, url, headers, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
return s.Do(req)
|
||||
}
|
||||
|
||||
// Perform a simple get to an endpoint and unmarshall returned JSON
|
||||
func (s *Session) RequestJSON(
|
||||
method string,
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body interface{},
|
||||
responseContainer interface{},
|
||||
) (resp *http.Response, err error) {
|
||||
var bodyjson []byte
|
||||
if body != nil {
|
||||
bodyjson, err = json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// if headers == nil {
|
||||
// headers = &http.Header{}
|
||||
// headers.Add("Content-Type", "application/json")
|
||||
// }
|
||||
|
||||
resp, err = s.Request(method, url, params, headers, &bodyjson)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// err = util.CheckHTTPResponseStatusCode(resp)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return resp, errors.New("error reading response body")
|
||||
}
|
||||
if err = json.Unmarshal(rbody, &responseContainer); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *Session) Delete(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
) (resp *http.Response, err error) {
|
||||
return s.Request("DELETE", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Get(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
) (resp *http.Response, err error) {
|
||||
return s.Request("GET", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) GetJSON(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
responseContainer interface{},
|
||||
) (resp *http.Response, err error) {
|
||||
return s.RequestJSON("GET", url, params, headers, nil, responseContainer)
|
||||
}
|
||||
|
||||
func (s *Session) Head(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
) (resp *http.Response, err error) {
|
||||
return s.Request("HEAD", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Post(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte,
|
||||
) (resp *http.Response, err error) {
|
||||
if headers == nil {
|
||||
headers = &http.Header{}
|
||||
headers.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
return s.Request("POST", url, params, headers, body)
|
||||
}
|
||||
|
||||
func (s *Session) PostJSON(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body interface{},
|
||||
responseContainer interface{},
|
||||
) (resp *http.Response, err error) {
|
||||
return s.RequestJSON("POST", url, params, headers, body, responseContainer)
|
||||
}
|
||||
|
||||
func (s *Session) Put(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte,
|
||||
) (resp *http.Response, err error) {
|
||||
if headers == nil {
|
||||
headers = &http.Header{}
|
||||
headers.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
return s.Request("PUT", url, params, headers, body)
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func inArray(arr []string, str string) bool {
|
||||
for _, elem := range arr {
|
||||
if elem == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func Itob(i int) bool {
|
||||
if i == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ParseSubConf - Parse standard sub-conf strings `key=value`.
|
||||
func ParseSubConf(
|
||||
element string,
|
||||
separator string,
|
||||
) (key string, value interface{}) {
|
||||
if strings.Contains(element, separator) {
|
||||
conf := strings.Split(element, separator)
|
||||
key, value := conf[0], conf[1]
|
||||
var interValue interface{}
|
||||
|
||||
// Make sure to add value in right type,
|
||||
// because all subconfig are returned as strings from Proxmox API.
|
||||
if iValue, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
interValue = int(iValue)
|
||||
} else if bValue, err := strconv.ParseBool(value); err == nil {
|
||||
interValue = bValue
|
||||
} else {
|
||||
interValue = value
|
||||
}
|
||||
return key, interValue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseConf - Parse standard device conf string `key1=val1,key2=val2`.
|
||||
func ParseConf(
|
||||
kvString string,
|
||||
confSeparator string,
|
||||
subConfSeparator string,
|
||||
) QemuDevice {
|
||||
var confMap = QemuDevice{}
|
||||
confList := strings.Split(kvString, confSeparator)
|
||||
for _, item := range confList {
|
||||
key, value := ParseSubConf(item, subConfSeparator)
|
||||
confMap[key] = value
|
||||
}
|
||||
return confMap
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
# vscode
|
||||
.vscode
|
||||
debug
|
||||
*.test
|
||||
|
||||
./build
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
|
@ -0,0 +1,12 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
|
||||
install:
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
|
@ -0,0 +1,25 @@
|
|||
*.received.*
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
|
@ -0,0 +1,16 @@
|
|||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
|
||||
install:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
|
||||
script:
|
||||
- test -z "$(go fmt -s -l -w ./... | tee /dev/stderr)"
|
||||
- golint -set_exit_status ./...
|
||||
- go vet ./...
|
||||
- go build -v ./...
|
||||
- go test -v ./...
|
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
/metrics.out
|
|
@ -0,0 +1,22 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
|
@ -0,0 +1,3 @@
|
|||
language: go
|
||||
go:
|
||||
- tip
|
|
@ -0,0 +1 @@
|
|||
module github.com/armon/go-radix
|
|
@ -44,13 +44,13 @@ func (n *node) addEdge(e edge) {
|
|||
n.edges.Sort()
|
||||
}
|
||||
|
||||
func (n *node) replaceEdge(e edge) {
|
||||
func (n *node) updateEdge(label byte, node *node) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= e.label
|
||||
return n.edges[i].label >= label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == e.label {
|
||||
n.edges[idx].node = e.node
|
||||
if idx < num && n.edges[idx].label == label {
|
||||
n.edges[idx].node = node
|
||||
return
|
||||
}
|
||||
panic("replacing missing edge")
|
||||
|
@ -198,10 +198,7 @@ func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) {
|
|||
child := &node{
|
||||
prefix: search[:commonPrefix],
|
||||
}
|
||||
parent.replaceEdge(edge{
|
||||
label: search[0],
|
||||
node: child,
|
||||
})
|
||||
parent.updateEdge(search[0], child)
|
||||
|
||||
// Restore the existing node
|
||||
child.addEdge(edge{
|
||||
|
@ -292,6 +289,53 @@ DELETE:
|
|||
return leaf.val, true
|
||||
}
|
||||
|
||||
// DeletePrefix is used to delete the subtree under a prefix
|
||||
// Returns how many nodes were deleted
|
||||
// Use this to delete large subtrees efficiently
|
||||
func (t *Tree) DeletePrefix(s string) int {
|
||||
return t.deletePrefix(nil, t.root, s)
|
||||
}
|
||||
|
||||
// delete does a recursive deletion
|
||||
func (t *Tree) deletePrefix(parent, n *node, prefix string) int {
|
||||
// Check for key exhaustion
|
||||
if len(prefix) == 0 {
|
||||
// Remove the leaf node
|
||||
subTreeSize := 0
|
||||
//recursively walk from all edges of the node to be deleted
|
||||
recursiveWalk(n, func(s string, v interface{}) bool {
|
||||
subTreeSize++
|
||||
return false
|
||||
})
|
||||
if n.isLeaf() {
|
||||
n.leaf = nil
|
||||
}
|
||||
n.edges = nil // deletes the entire subtree
|
||||
|
||||
// Check if we should merge the parent's other child
|
||||
if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() {
|
||||
parent.mergeChild()
|
||||
}
|
||||
t.size -= subTreeSize
|
||||
return subTreeSize
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
label := prefix[0]
|
||||
child := n.getEdge(label)
|
||||
if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if len(child.prefix) > len(prefix) {
|
||||
prefix = prefix[len(prefix):]
|
||||
} else {
|
||||
prefix = prefix[len(child.prefix):]
|
||||
}
|
||||
return t.deletePrefix(n, child, prefix)
|
||||
}
|
||||
|
||||
func (n *node) mergeChild() {
|
||||
e := n.edges[0]
|
||||
child := e.node
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
Original version Copyright © 2010 Fazlul Shahriar <fshahriar@gmail.com>. Newer
|
||||
portions Copyright © 2014 Blake Gentry <blakesgentry@gmail.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
|
@ -0,0 +1,2 @@
|
|||
example/example
|
||||
example/example.exe
|
|
@ -1,6 +1,9 @@
|
|||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 <copyright holders>
|
||||
Copyright (c) 2017 Blake Gentry
|
||||
|
||||
This license applies to the non-Windows portions of this library. The Windows
|
||||
portion maintains its own Apache 2.0 license.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -9,13 +12,13 @@ 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 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.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,18 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
password, err := speakeasy.Ask("Please enter a password: ")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Password result: %q\n", password)
|
||||
fmt.Printf("Password len: %d\n", len(password))
|
||||
}
|
|
@ -14,16 +14,18 @@ func Ask(prompt string) (password string, err error) {
|
|||
return FAsk(os.Stdout, prompt)
|
||||
}
|
||||
|
||||
// Same as the Ask function, except it is possible to specify the file to write
|
||||
// the prompt to.
|
||||
func FAsk(file *os.File, prompt string) (password string, err error) {
|
||||
if prompt != "" {
|
||||
fmt.Fprint(file, prompt) // Display the prompt.
|
||||
// FAsk is the same as Ask, except it is possible to specify the file to write
|
||||
// the prompt to. If 'nil' is passed as the writer, no prompt will be written.
|
||||
func FAsk(wr io.Writer, prompt string) (password string, err error) {
|
||||
if wr != nil && prompt != "" {
|
||||
fmt.Fprint(wr, prompt) // Display the prompt.
|
||||
}
|
||||
password, err = getPassword()
|
||||
|
||||
// Carriage return after the user input.
|
||||
fmt.Fprintln(file, "")
|
||||
if wr != nil {
|
||||
fmt.Fprintln(wr, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Original code is based on code by RogerV in the golang-nuts thread:
|
||||
// https://groups.google.com/group/golang-nuts/browse_thread/thread/40cc41e9d9fc9247
|
||||
|
||||
// +build darwin freebsd linux netbsd openbsd solaris
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package speakeasy
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package speakeasy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
@ -12,18 +11,17 @@ import (
|
|||
const ENABLE_ECHO_INPUT = 0x0004
|
||||
|
||||
func getPassword() (password string, err error) {
|
||||
hStdin := syscall.Handle(os.Stdin.Fd())
|
||||
var oldMode uint32
|
||||
|
||||
err = syscall.GetConsoleMode(hStdin, &oldMode)
|
||||
err = syscall.GetConsoleMode(syscall.Stdin, &oldMode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var newMode uint32 = (oldMode &^ ENABLE_ECHO_INPUT)
|
||||
|
||||
err = setConsoleMode(hStdin, newMode)
|
||||
defer setConsoleMode(hStdin, oldMode)
|
||||
err = setConsoleMode(syscall.Stdin, newMode)
|
||||
defer setConsoleMode(syscall.Stdin, oldMode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
go-select*
|
||||
goselect*
|
||||
example-*
|
||||
example/example
|
0
vendor/github.com/creack/goselect/test_crosscompile.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/creack/goselect/test_crosscompile.sh
generated
vendored
Executable file → Normal file
|
@ -2,7 +2,7 @@ ISC License
|
|||
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
|
@ -34,80 +36,49 @@ const (
|
|||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
type flag uintptr
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
|
@ -119,34 +90,56 @@ func init() {
|
|||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
|
@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
|||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
|
|
|
@ -35,16 +35,16 @@ var (
|
|||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
|
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
|||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
|
|
|
@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
|||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
|
|
|
@ -69,7 +69,6 @@ func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, s
|
|||
client.Init(endpoint, version, accessKeyId, accessKeySecret)
|
||||
client.serviceCode = serviceCode
|
||||
client.regionID = regionID
|
||||
client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret, client.securityToken)
|
||||
}
|
||||
|
||||
// Intialize client object when all properties are ready
|
||||
|
@ -86,7 +85,6 @@ func (client *Client) InitClient() *Client {
|
|||
TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second}
|
||||
client.httpClient = &http.Client{Transport: t}
|
||||
}
|
||||
client.setEndpointByLocation(client.regionID, client.serviceCode, client.AccessKeyId, client.AccessKeySecret, client.securityToken)
|
||||
return client
|
||||
}
|
||||
|
||||
|
@ -95,9 +93,17 @@ func (client *Client) NewInitForAssumeRole(endpoint, version, accessKeyId, acces
|
|||
client.securityToken = securityToken
|
||||
}
|
||||
|
||||
//getLocationEndpoint
|
||||
func (client *Client) getEndpointByLocation() string {
|
||||
locationClient := NewLocationClient(client.AccessKeyId, client.AccessKeySecret, client.securityToken)
|
||||
locationClient.SetDebug(true)
|
||||
return locationClient.DescribeOpenAPIEndpoint(client.regionID, client.serviceCode)
|
||||
}
|
||||
|
||||
//NewClient using location service
|
||||
func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret, securityToken string) {
|
||||
locationClient := NewLocationClient(accessKeyId, accessKeySecret, securityToken)
|
||||
locationClient.SetDebug(true)
|
||||
ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode)
|
||||
if ep == "" {
|
||||
ep = loadEndpointFromFile(region, serviceCode)
|
||||
|
@ -251,12 +257,34 @@ func (client *Client) SetSecurityToken(securityToken string) {
|
|||
client.securityToken = securityToken
|
||||
}
|
||||
|
||||
func (client *Client) initEndpoint() error {
|
||||
// if set any value to "CUSTOMIZED_ENDPOINT" could skip location service.
|
||||
// example: export CUSTOMIZED_ENDPOINT=true
|
||||
if os.Getenv("CUSTOMIZED_ENDPOINT") != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.serviceCode != "" && client.regionID != "" {
|
||||
endpoint := client.getEndpointByLocation()
|
||||
if endpoint == "" {
|
||||
return GetCustomError("InvalidEndpoint", "endpoint is empty,pls check")
|
||||
}
|
||||
client.endpoint = endpoint
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Invoke sends the raw HTTP request for ECS services
|
||||
func (client *Client) Invoke(action string, args interface{}, response interface{}) error {
|
||||
if err := client.ensureProperties(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//init endpoint
|
||||
if err := client.initEndpoint(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := Request{}
|
||||
request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID)
|
||||
|
||||
|
@ -330,6 +358,11 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res
|
|||
return err
|
||||
}
|
||||
|
||||
//init endpoint
|
||||
if err := client.initEndpoint(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := Request{}
|
||||
request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID)
|
||||
|
||||
|
@ -406,6 +439,11 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf
|
|||
return err
|
||||
}
|
||||
|
||||
//init endpoint
|
||||
if err := client.initEndpoint(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := Request{}
|
||||
request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID)
|
||||
data := util.ConvertToQueryValues(request)
|
||||
|
@ -500,3 +538,13 @@ func GetClientErrorFromString(str string) error {
|
|||
func GetClientError(err error) error {
|
||||
return GetClientErrorFromString(err.Error())
|
||||
}
|
||||
|
||||
func GetCustomError(code, message string) error {
|
||||
return &Error{
|
||||
ErrorResponse: ErrorResponse{
|
||||
Code: code,
|
||||
Message: message,
|
||||
},
|
||||
StatusCode: 400,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -50,6 +52,12 @@ var (
|
|||
"rds": "https://rds.eu-central-1.aliyuncs.com",
|
||||
"vpc": "https://vpc.eu-central-1.aliyuncs.com",
|
||||
},
|
||||
EUWest1: {
|
||||
"ecs": "https://ecs.eu-west-1.aliyuncs.com",
|
||||
"slb": "https://slb.eu-west-1.aliyuncs.com",
|
||||
"rds": "https://rds.eu-west-1.aliyuncs.com",
|
||||
"vpc": "https://vpc.eu-west-1.aliyuncs.com",
|
||||
},
|
||||
Zhangjiakou: {
|
||||
"ecs": "https://ecs.cn-zhangjiakou.aliyuncs.com",
|
||||
"slb": "https://slb.cn-zhangjiakou.aliyuncs.com",
|
||||
|
@ -140,7 +148,6 @@ func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode
|
|||
if endpoint := getProductRegionEndpoint(region, serviceCode); endpoint != "" {
|
||||
return endpoint
|
||||
}
|
||||
|
||||
defaultProtocols := HTTP_PROTOCOL
|
||||
|
||||
args := &DescribeEndpointsArgs{
|
||||
|
@ -149,8 +156,18 @@ func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode
|
|||
Type: "openAPI",
|
||||
}
|
||||
|
||||
endpoint, err := client.DescribeEndpoints(args)
|
||||
if err != nil || len(endpoint.Endpoints.Endpoint) <= 0 {
|
||||
var endpoint *DescribeEndpointsResponse
|
||||
var err error
|
||||
for index := 0; index < 5; index++ {
|
||||
endpoint, err = client.DescribeEndpoints(args)
|
||||
if err == nil && endpoint != nil && len(endpoint.Endpoints.Endpoint) > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
if err != nil || endpoint == nil || len(endpoint.Endpoints.Endpoint) <= 0 {
|
||||
log.Printf("aliyungo: can not get endpoint from service, use default. endpoint=[%v], error=[%v]\n", endpoint, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -163,7 +180,7 @@ func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode
|
|||
|
||||
ep := fmt.Sprintf("%s://%s", defaultProtocols, endpoint.Endpoints.Endpoint[0].Endpoint)
|
||||
|
||||
//setProductRegionEndpoint(region, serviceCode, ep)
|
||||
setProductRegionEndpoint(region, serviceCode, ep)
|
||||
return ep
|
||||
}
|
||||
|
||||
|
|
|
@ -1336,6 +1336,17 @@
|
|||
<Product><ProductName>Slb</ProductName><DomainName>slb.eu-central-1.aliyuncs.com</DomainName></Product>
|
||||
</Products>
|
||||
</Endpoint>
|
||||
<Endpoint name="eu-west-1">
|
||||
<RegionIds><RegionId>eu-west-1</RegionId></RegionIds>
|
||||
<Products>
|
||||
<Product><ProductName>Rds</ProductName><DomainName>rds.eu-west-1.aliyuncs.com</DomainName></Product>
|
||||
<Product><ProductName>Ecs</ProductName><DomainName>ecs.eu-west-1.aliyuncs.com</DomainName></Product>
|
||||
<Product><ProductName>Vpc</ProductName><DomainName>vpc.eu-west-1.aliyuncs.com</DomainName></Product>
|
||||
<Product><ProductName>Kms</ProductName><DomainName>kms.eu-west-1.aliyuncs.com</DomainName></Product>
|
||||
<Product><ProductName>Cms</ProductName><DomainName>metrics.cn-hangzhou.aliyuncs.com</DomainName></Product>
|
||||
<Product><ProductName>Slb</ProductName><DomainName>slb.eu-west-1.aliyuncs.com</DomainName></Product>
|
||||
</Products>
|
||||
</Endpoint>
|
||||
<Endpoint name="cn-zhangjiakou">
|
||||
<RegionIds><RegionId>cn-zhangjiakou</RegionId></RegionIds>
|
||||
<Products>
|
||||
|
|
|
@ -28,6 +28,7 @@ const (
|
|||
MEEast1 = Region("me-east-1")
|
||||
|
||||
EUCentral1 = Region("eu-central-1")
|
||||
EUWest1 = Region("eu-west-1")
|
||||
|
||||
ShenZhenFinance = Region("cn-shenzhen-finance-1")
|
||||
ShanghaiFinance = Region("cn-shanghai-finance-1")
|
||||
|
@ -39,6 +40,16 @@ var ValidRegions = []Region{
|
|||
APNorthEast1, APSouthEast1, APSouthEast2, APSouthEast3, APSouthEast5,
|
||||
APSouth1,
|
||||
MEEast1,
|
||||
EUCentral1,
|
||||
EUCentral1, EUWest1,
|
||||
ShenZhenFinance, ShanghaiFinance,
|
||||
}
|
||||
|
||||
// IsValidRegion checks if r is an Ali supported region.
|
||||
func IsValidRegion(r string) bool {
|
||||
for _, v := range ValidRegions {
|
||||
if r == string(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -113,3 +113,12 @@ func NewVPCClientWithEndpointAndSecurityToken(endpoint string, accessKeyId strin
|
|||
InitClient()
|
||||
return client
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// NewVPCClientWithRegion creates a new instance of VPC client automatically get endpoint
|
||||
// ---------------------------------------
|
||||
func NewVPCClientWithRegion(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client {
|
||||
client := &Client{}
|
||||
client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID)
|
||||
return client
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ type DescribeDisksArgs struct {
|
|||
Category DiskCategory //enum for all(default) | cloud | ephemeral
|
||||
Status DiskStatus //enum for In_use | Available | Attaching | Detaching | Creating | ReIniting | All(default)
|
||||
SnapshotId string
|
||||
Name string
|
||||
DiskName string
|
||||
Portable *bool //optional
|
||||
DeleteWithInstance *bool //optional
|
||||
DeleteAutoSnapshot *bool //optional
|
||||
|
@ -78,6 +78,7 @@ type DiskItemType struct {
|
|||
DiskName string
|
||||
Description string
|
||||
Type DiskType
|
||||
Encrypted bool
|
||||
Category DiskCategory
|
||||
Size int
|
||||
ImageId string
|
||||
|
@ -242,7 +243,7 @@ func (client *Client) DetachDisk(instanceId string, diskId string) error {
|
|||
}
|
||||
|
||||
type ResizeDiskArgs struct {
|
||||
DiskId string
|
||||
DiskId string
|
||||
NewSize int
|
||||
}
|
||||
|
||||
|
@ -255,15 +256,14 @@ type ResizeDiskResponse struct {
|
|||
// You can read doc at https://help.aliyun.com/document_detail/25522.html
|
||||
func (client *Client) ResizeDisk(diskId string, sizeGB int) error {
|
||||
args := ResizeDiskArgs{
|
||||
DiskId:diskId,
|
||||
NewSize:sizeGB,
|
||||
DiskId: diskId,
|
||||
NewSize: sizeGB,
|
||||
}
|
||||
response := ResizeDiskResponse{}
|
||||
err := client.Invoke("ResizeDisk", &args, &response)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
type ResetDiskArgs struct {
|
||||
DiskId string
|
||||
SnapshotId string
|
||||
|
@ -273,7 +273,6 @@ type ResetDiskResponse struct {
|
|||
common.Response
|
||||
}
|
||||
|
||||
|
||||
// ResetDisk resets disk to original status
|
||||
//
|
||||
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
)
|
||||
|
||||
type CreateNetworkInterfaceArgs struct {
|
||||
RegionId common.Region
|
||||
VSwitchId string
|
||||
PrimaryIpAddress string // optional
|
||||
SecurityGroupId string
|
||||
NetworkInterfaceName string // optional
|
||||
Description string // optional
|
||||
ClientToken string // optional
|
||||
}
|
||||
|
||||
type CreateNetworkInterfaceResponse struct {
|
||||
common.Response
|
||||
NetworkInterfaceId string
|
||||
}
|
||||
type DeleteNetworkInterfaceArgs struct {
|
||||
RegionId common.Region
|
||||
NetworkInterfaceId string
|
||||
}
|
||||
|
||||
type DeleteNetworkInterfaceResponse struct {
|
||||
common.Response
|
||||
}
|
||||
|
||||
type DescribeNetworkInterfacesArgs struct {
|
||||
RegionId common.Region
|
||||
VSwitchId string
|
||||
PrimaryIpAddress string
|
||||
SecurityGroupId string
|
||||
NetworkInterfaceName string
|
||||
Type string
|
||||
InstanceId string
|
||||
NetworkInterfaceId []string `query:"list"`
|
||||
PageNumber int
|
||||
PageSize int
|
||||
}
|
||||
type NetworkInterfaceType struct {
|
||||
NetworkInterfaceId string
|
||||
NetworkInterfaceName string
|
||||
PrimaryIpAddress string
|
||||
MacAddress string
|
||||
Status string
|
||||
PrivateIpAddress string
|
||||
}
|
||||
|
||||
type DescribeNetworkInterfacesResponse struct {
|
||||
common.Response
|
||||
NetworkInterfaceSets struct {
|
||||
NetworkInterfaceSet []NetworkInterfaceType
|
||||
}
|
||||
TotalCount int
|
||||
PageNumber int
|
||||
PageSize int
|
||||
}
|
||||
type AttachNetworkInterfaceArgs struct {
|
||||
RegionId common.Region
|
||||
NetworkInterfaceId string
|
||||
InstanceId string
|
||||
}
|
||||
|
||||
type AttachNetworkInterfaceResponse common.Response
|
||||
|
||||
type DetachNetworkInterfaceArgs AttachNetworkInterfaceArgs
|
||||
|
||||
type DetachNetworkInterfaceResponse common.Response
|
||||
|
||||
type ModifyNetworkInterfaceAttributeArgs struct {
|
||||
RegionId common.Region
|
||||
NetworkInterfaceId string
|
||||
SecurityGroupId []string
|
||||
NetworkInterfaceName string
|
||||
Description string
|
||||
}
|
||||
type ModifyNetworkInterfaceAttributeResponse common.Response
|
||||
|
||||
type UnassignPrivateIpAddressesArgs struct {
|
||||
RegionId common.Region
|
||||
NetworkInterfaceId string
|
||||
PrivateIpAddress []string `query:"list"`
|
||||
}
|
||||
|
||||
type UnassignPrivateIpAddressesResponse common.Response
|
||||
|
||||
type AssignPrivateIpAddressesArgs struct {
|
||||
RegionId common.Region
|
||||
NetworkInterfaceId string
|
||||
PrivateIpAddress []string `query:"list"` // optional
|
||||
SecondaryPrivateIpAddressCount int // optional
|
||||
}
|
||||
|
||||
type AssignPrivateIpAddressesResponse common.Response
|
||||
|
||||
func (client *Client) CreateNetworkInterface(args *CreateNetworkInterfaceArgs) (resp *CreateNetworkInterfaceResponse, err error) {
|
||||
resp = &CreateNetworkInterfaceResponse{}
|
||||
err = client.Invoke("CreateNetworkInterface", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) DeleteNetworkInterface(args *DeleteNetworkInterfaceArgs) (resp *DeleteNetworkInterfaceResponse, err error) {
|
||||
resp = &DeleteNetworkInterfaceResponse{}
|
||||
err = client.Invoke("DeleteNetworkInterface", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) DescribeNetworkInterfaces(args *DescribeNetworkInterfacesArgs) (resp *DescribeNetworkInterfacesResponse, err error) {
|
||||
resp = &DescribeNetworkInterfacesResponse{}
|
||||
err = client.Invoke("DescribeNetworkInterfaces", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) AttachNetworkInterface(args *AttachNetworkInterfaceArgs) error {
|
||||
resp := &AttachNetworkInterfaceResponse{}
|
||||
err := client.Invoke("AttachNetworkInterface", args, resp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *Client) DetachNetworkInterface(args *DetachNetworkInterfaceArgs) (resp *DetachNetworkInterfaceResponse, err error) {
|
||||
resp = &DetachNetworkInterfaceResponse{}
|
||||
err = client.Invoke("DetachNetworkInterface", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) ModifyNetworkInterfaceAttribute(args *ModifyNetworkInterfaceAttributeArgs) (resp *ModifyNetworkInterfaceAttributeResponse, err error) {
|
||||
resp = &ModifyNetworkInterfaceAttributeResponse{}
|
||||
err = client.Invoke("ModifyNetworkInterfaceAttribute", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) UnassignPrivateIpAddresses(args *UnassignPrivateIpAddressesArgs) (resp *UnassignPrivateIpAddressesResponse, err error) {
|
||||
resp = &UnassignPrivateIpAddressesResponse{}
|
||||
err = client.Invoke("UnassignPrivateIpAddresses", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (client *Client) AssignPrivateIpAddresses(args *AssignPrivateIpAddressesArgs) (resp *AssignPrivateIpAddressesResponse, err error) {
|
||||
resp = &AssignPrivateIpAddressesResponse{}
|
||||
err = client.Invoke("AssignPrivateIpAddresses", args, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Default timeout value for WaitForInstance method
|
||||
const NetworkInterfacesDefaultTimeout = 120
|
||||
|
||||
// WaitForInstance waits for instance to given status
|
||||
func (client *Client) WaitForNetworkInterface(regionId common.Region, eniID string, status string, timeout int) error {
|
||||
if timeout <= 0 {
|
||||
timeout = NetworkInterfacesDefaultTimeout
|
||||
}
|
||||
for {
|
||||
|
||||
eniIds := []string{eniID}
|
||||
|
||||
describeNetworkInterfacesArgs := DescribeNetworkInterfacesArgs{
|
||||
RegionId: regionId,
|
||||
NetworkInterfaceId: eniIds,
|
||||
}
|
||||
|
||||
nisResponse, err := client.DescribeNetworkInterfaces(&describeNetworkInterfacesArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to describe network interface %v: %v", eniID, err)
|
||||
}
|
||||
|
||||
if len(nisResponse.NetworkInterfaceSets.NetworkInterfaceSet) > 0 && nisResponse.NetworkInterfaceSets.NetworkInterfaceSet[0].Status == status {
|
||||
break
|
||||
}
|
||||
|
||||
timeout = timeout - DefaultWaitForInterval
|
||||
if timeout <= 0 {
|
||||
return fmt.Errorf("Timeout for waiting available status for network interfaces")
|
||||
}
|
||||
time.Sleep(DefaultWaitForInterval * time.Second)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -37,6 +37,13 @@ const (
|
|||
ImageUsageNone = ImageUsage("none")
|
||||
)
|
||||
|
||||
type ImageFormatType string
|
||||
|
||||
const (
|
||||
RAW = ImageFormatType("RAW")
|
||||
VHD = ImageFormatType("VHD")
|
||||
)
|
||||
|
||||
// DescribeImagesArgs repsents arguments to describe images
|
||||
type DescribeImagesArgs struct {
|
||||
RegionId common.Region
|
||||
|
@ -63,8 +70,10 @@ type DescribeImagesResponse struct {
|
|||
type DiskDeviceMapping struct {
|
||||
SnapshotId string
|
||||
//Why Size Field is string-type.
|
||||
Size string
|
||||
Device string
|
||||
Size string
|
||||
// Now the key Size change to DiskImageSize
|
||||
DiskImageSize string
|
||||
Device string
|
||||
//For import images
|
||||
Format string
|
||||
OSSBucket string
|
||||
|
@ -151,6 +160,7 @@ func (client *Client) CreateImage(args *CreateImageArgs) (imageId string, err er
|
|||
type DeleteImageArgs struct {
|
||||
RegionId common.Region
|
||||
ImageId string
|
||||
Force bool
|
||||
}
|
||||
|
||||
type DeleteImageResponse struct {
|
||||
|
@ -170,6 +180,20 @@ func (client *Client) DeleteImage(regionId common.Region, imageId string) error
|
|||
return client.Invoke("DeleteImage", &args, &response)
|
||||
}
|
||||
|
||||
// DeleteImage deletes Image
|
||||
//
|
||||
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&deleteimage
|
||||
func (client *Client) DeleteImageWithForce(regionId common.Region, imageId string, force bool) error {
|
||||
args := DeleteImageArgs{
|
||||
RegionId: regionId,
|
||||
ImageId: imageId,
|
||||
Force: force,
|
||||
}
|
||||
|
||||
response := &DeleteImageResponse{}
|
||||
return client.Invoke("DeleteImage", &args, &response)
|
||||
}
|
||||
|
||||
// ModifyImageSharePermission repsents arguments to share image
|
||||
type ModifyImageSharePermissionArgs struct {
|
||||
RegionId common.Region
|
||||
|
|
|
@ -9,10 +9,19 @@ type DescribeInstanceTypesArgs struct {
|
|||
//
|
||||
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype
|
||||
type InstanceTypeItemType struct {
|
||||
InstanceTypeId string
|
||||
CpuCoreCount int
|
||||
MemorySize float64
|
||||
InstanceTypeFamily string
|
||||
InstanceTypeId string
|
||||
CpuCoreCount int
|
||||
MemorySize float64
|
||||
InstanceTypeFamily string
|
||||
GPUAmount int
|
||||
GPUSpec string
|
||||
InitialCredit int
|
||||
BaselineCredit int
|
||||
EniQuantity int
|
||||
EniPrivateIpAddressQuantity int
|
||||
LocalStorageCapacity int
|
||||
LocalStorageAmount int
|
||||
LocalStorageCategory string
|
||||
}
|
||||
|
||||
type DescribeInstanceTypesResponse struct {
|
||||
|
|
|
@ -245,7 +245,7 @@ type InstanceAttributesType struct {
|
|||
SerialNumber string
|
||||
Status InstanceStatus
|
||||
OperationLocks OperationLocksType
|
||||
SecurityGroupIds struct {
|
||||
SecurityGroupIds struct {
|
||||
SecurityGroupId []string
|
||||
}
|
||||
PublicIpAddress IpAddressSetType
|
||||
|
@ -260,7 +260,7 @@ type InstanceAttributesType struct {
|
|||
IoOptimized StringOrBool
|
||||
InstanceChargeType common.InstanceChargeType
|
||||
ExpiredTime util.ISO6801Time
|
||||
Tags struct {
|
||||
Tags struct {
|
||||
Tag []TagItemType
|
||||
}
|
||||
SpotStrategy SpotStrategyType
|
||||
|
@ -440,6 +440,28 @@ func (client *Client) DescribeInstancesWithRaw(args *DescribeInstancesArgs) (res
|
|||
return response, nil
|
||||
}
|
||||
|
||||
type ModifyInstanceAutoReleaseTimeArgs struct {
|
||||
InstanceId string
|
||||
AutoReleaseTime string
|
||||
}
|
||||
|
||||
type ModifyInstanceAutoReleaseTimeResponse struct {
|
||||
common.Response
|
||||
}
|
||||
|
||||
// 对给定的实例设定自动释放时间。
|
||||
//
|
||||
// You can read doc at https://help.aliyun.com/document_detail/47576.html
|
||||
func (client *Client) ModifyInstanceAutoReleaseTime(instanceId, time string) error {
|
||||
args := ModifyInstanceAutoReleaseTimeArgs{
|
||||
InstanceId: instanceId,
|
||||
AutoReleaseTime: time,
|
||||
}
|
||||
response := ModifyInstanceAutoReleaseTimeResponse{}
|
||||
err := client.Invoke("ModifyInstanceAutoReleaseTime", &args, &response)
|
||||
return err
|
||||
}
|
||||
|
||||
type DeleteInstanceArgs struct {
|
||||
InstanceId string
|
||||
}
|
||||
|
@ -512,34 +534,43 @@ var (
|
|||
IoOptimizedOptimized = IoOptimized("optimized")
|
||||
)
|
||||
|
||||
type SecurityEnhancementStrategy string
|
||||
|
||||
var (
|
||||
InactiveSecurityEnhancementStrategy = SecurityEnhancementStrategy("Active")
|
||||
DeactiveSecurityEnhancementStrategy = SecurityEnhancementStrategy("Deactive")
|
||||
)
|
||||
|
||||
type CreateInstanceArgs struct {
|
||||
RegionId common.Region
|
||||
ZoneId string
|
||||
ImageId string
|
||||
InstanceType string
|
||||
SecurityGroupId string
|
||||
InstanceName string
|
||||
Description string
|
||||
InternetChargeType common.InternetChargeType
|
||||
InternetMaxBandwidthIn int
|
||||
InternetMaxBandwidthOut int
|
||||
HostName string
|
||||
Password string
|
||||
IoOptimized IoOptimized
|
||||
SystemDisk SystemDiskType
|
||||
DataDisk []DataDiskType
|
||||
VSwitchId string
|
||||
PrivateIpAddress string
|
||||
ClientToken string
|
||||
InstanceChargeType common.InstanceChargeType
|
||||
Period int
|
||||
UserData string
|
||||
AutoRenew bool
|
||||
AutoRenewPeriod int
|
||||
SpotStrategy SpotStrategyType
|
||||
SpotPriceLimit float64
|
||||
KeyPairName string
|
||||
RamRoleName string
|
||||
RegionId common.Region
|
||||
ZoneId string
|
||||
ImageId string
|
||||
InstanceType string
|
||||
SecurityGroupId string
|
||||
InstanceName string
|
||||
Description string
|
||||
InternetChargeType common.InternetChargeType
|
||||
InternetMaxBandwidthIn int
|
||||
InternetMaxBandwidthOut int
|
||||
HostName string
|
||||
Password string
|
||||
IoOptimized IoOptimized
|
||||
SystemDisk SystemDiskType
|
||||
DataDisk []DataDiskType
|
||||
VSwitchId string
|
||||
PrivateIpAddress string
|
||||
ClientToken string
|
||||
InstanceChargeType common.InstanceChargeType
|
||||
Period int
|
||||
PeriodUnit common.TimeType
|
||||
UserData string
|
||||
AutoRenew bool
|
||||
AutoRenewPeriod int
|
||||
SpotStrategy SpotStrategyType
|
||||
SpotPriceLimit float64
|
||||
KeyPairName string
|
||||
RamRoleName string
|
||||
SecurityEnhancementStrategy SecurityEnhancementStrategy
|
||||
}
|
||||
|
||||
type CreateInstanceResponse struct {
|
||||
|
@ -682,3 +713,120 @@ func (client *Client) DescribeInstanceRamRole(args *AttachInstancesArgs) (resp *
|
|||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type ModifyInstanceSpecArgs struct {
|
||||
InstanceId string
|
||||
InstanceType string
|
||||
InternetMaxBandwidthOut *int
|
||||
InternetMaxBandwidthIn *int
|
||||
ClientToken string
|
||||
}
|
||||
|
||||
type ModifyInstanceSpecResponse struct {
|
||||
common.Response
|
||||
}
|
||||
|
||||
//ModifyInstanceSpec modify instance specification
|
||||
//
|
||||
// Notice: 1. An instance that was successfully modified once cannot be modified again within 5 minutes.
|
||||
// 2. The API only can be used Pay-As-You-Go (PostPaid) instance
|
||||
//
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/57633.htm
|
||||
func (client *Client) ModifyInstanceSpec(args *ModifyInstanceSpecArgs) error {
|
||||
response := ModifyInstanceSpecResponse{}
|
||||
return client.Invoke("ModifyInstanceSpec", args, &response)
|
||||
}
|
||||
|
||||
type ModifyInstanceVpcAttributeArgs struct {
|
||||
InstanceId string
|
||||
VSwitchId string
|
||||
PrivateIpAddress string
|
||||
}
|
||||
|
||||
type ModifyInstanceVpcAttributeResponse struct {
|
||||
common.Response
|
||||
}
|
||||
|
||||
//ModifyInstanceVpcAttribute modify instance vswitchID and private ip address
|
||||
//
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/25504.htm
|
||||
func (client *Client) ModifyInstanceVpcAttribute(args *ModifyInstanceVpcAttributeArgs) error {
|
||||
response := ModifyInstanceVpcAttributeResponse{}
|
||||
return client.Invoke("ModifyInstanceVpcAttribute", args, &response)
|
||||
}
|
||||
|
||||
type ModifyInstanceChargeTypeArgs struct {
|
||||
InstanceIds string
|
||||
RegionId common.Region
|
||||
Period int
|
||||
PeriodUnit common.TimeType
|
||||
IncludeDataDisks bool
|
||||
DryRun bool
|
||||
AutoPay bool
|
||||
ClientToken string
|
||||
}
|
||||
|
||||
type ModifyInstanceChargeTypeResponse struct {
|
||||
common.Response
|
||||
Order string
|
||||
}
|
||||
|
||||
//ModifyInstanceChargeType modify instance charge type
|
||||
//
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/25504.htm
|
||||
func (client *Client) ModifyInstanceChargeType(args *ModifyInstanceChargeTypeArgs) (*ModifyInstanceChargeTypeResponse, error) {
|
||||
response := &ModifyInstanceChargeTypeResponse{}
|
||||
if err := client.Invoke("ModifyInstanceChargeType", args, response); err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type RenewalStatus string
|
||||
|
||||
const (
|
||||
RenewAutoRenewal = RenewalStatus("AutoRenewal")
|
||||
RenewNormal = RenewalStatus("Normal")
|
||||
RenewNotRenewal = RenewalStatus("NotRenewal")
|
||||
)
|
||||
|
||||
type ModifyInstanceAutoRenewAttributeArgs struct {
|
||||
InstanceId string
|
||||
RegionId common.Region
|
||||
Duration int
|
||||
AutoRenew bool
|
||||
RenewalStatus RenewalStatus
|
||||
}
|
||||
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/52843.htm
|
||||
func (client *Client) ModifyInstanceAutoRenewAttribute(args *ModifyInstanceAutoRenewAttributeArgs) error {
|
||||
response := &common.Response{}
|
||||
return client.Invoke("ModifyInstanceAutoRenewAttribute", args, response)
|
||||
}
|
||||
|
||||
type DescribeInstanceAutoRenewAttributeArgs struct {
|
||||
InstanceId string
|
||||
RegionId common.Region
|
||||
}
|
||||
|
||||
type InstanceRenewAttribute struct {
|
||||
InstanceId string
|
||||
Duration int
|
||||
AutoRenewEnabled bool
|
||||
PeriodUnit string
|
||||
RenewalStatus RenewalStatus
|
||||
}
|
||||
|
||||
type DescribeInstanceAutoRenewAttributeResponse struct {
|
||||
common.Response
|
||||
InstanceRenewAttributes struct {
|
||||
InstanceRenewAttribute []InstanceRenewAttribute
|
||||
}
|
||||
}
|
||||
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/52844.htm
|
||||
func (client *Client) DescribeInstanceAutoRenewAttribute(args *DescribeInstanceAutoRenewAttributeArgs) (*DescribeInstanceAutoRenewAttributeResponse, error) {
|
||||
response := &DescribeInstanceAutoRenewAttributeResponse{}
|
||||
err := client.Invoke("DescribeInstanceAutoRenewAttribute", args, response)
|
||||
return response, err
|
||||
}
|
||||
|
|
|
@ -28,6 +28,16 @@ type SnatTableIdType struct {
|
|||
SnatTableId []string
|
||||
}
|
||||
|
||||
type IpListsType struct {
|
||||
IpList []IpListItem
|
||||
}
|
||||
|
||||
type IpListItem struct {
|
||||
IpAddress string
|
||||
AllocationId string
|
||||
UsingStatus string
|
||||
}
|
||||
|
||||
type BandwidthPackageIdType struct {
|
||||
BandwidthPackageId []string
|
||||
}
|
||||
|
@ -57,6 +67,7 @@ type NatGatewaySetType struct {
|
|||
BandwidthPackageIds BandwidthPackageIdType
|
||||
ForwardTableIds ForwardTableIdType
|
||||
SnatTableIds SnatTableIdType
|
||||
IpLists IpListsType
|
||||
InstanceChargeType string
|
||||
Name string
|
||||
NatGatewayId string
|
||||
|
@ -147,7 +158,8 @@ func (client *Client) DeleteNatGateway(args *DeleteNatGatewayArgs) error {
|
|||
}
|
||||
|
||||
type DescribeBandwidthPackagesArgs struct {
|
||||
RegionId common.Region
|
||||
RegionId common.Region
|
||||
common.Pagination
|
||||
BandwidthPackageId string
|
||||
NatGatewayId string
|
||||
}
|
||||
|
@ -170,12 +182,13 @@ type DescribeBandwidthPackageType struct {
|
|||
|
||||
type DescribeBandwidthPackagesResponse struct {
|
||||
common.Response
|
||||
common.PaginationResult
|
||||
BandwidthPackages struct {
|
||||
BandwidthPackage []DescribeBandwidthPackageType
|
||||
}
|
||||
}
|
||||
|
||||
func (client *Client) DescribeBandwidthPackages(args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) {
|
||||
func (client *Client) DescribeBandwidthPackages(args *DescribeBandwidthPackagesArgs) (*DescribeBandwidthPackagesResponse, error) {
|
||||
response := &DescribeBandwidthPackagesResponse{}
|
||||
|
||||
err := client.Invoke("DescribeBandwidthPackages", args, response)
|
||||
|
@ -183,7 +196,7 @@ func (client *Client) DescribeBandwidthPackages(args *DescribeBandwidthPackagesA
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return response.BandwidthPackages.BandwidthPackage, err
|
||||
return response, err
|
||||
}
|
||||
|
||||
type DeleteBandwidthPackageArgs struct {
|
||||
|
|
|
@ -36,8 +36,9 @@ func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress stri
|
|||
|
||||
type ModifyInstanceNetworkSpec struct {
|
||||
InstanceId string
|
||||
InternetMaxBandwidthOut int
|
||||
InternetMaxBandwidthIn int
|
||||
InternetMaxBandwidthOut *int
|
||||
InternetMaxBandwidthIn *int
|
||||
NetworkChargeType common.InternetChargeType
|
||||
}
|
||||
|
||||
type ModifyInstanceNetworkSpecResponse struct {
|
||||
|
@ -57,6 +58,7 @@ type AllocateEipAddressArgs struct {
|
|||
RegionId common.Region
|
||||
Bandwidth int
|
||||
InternetChargeType common.InternetChargeType
|
||||
ISP string
|
||||
ClientToken string
|
||||
}
|
||||
|
||||
|
@ -81,9 +83,19 @@ func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddre
|
|||
return response.EipAddress, response.AllocationId, nil
|
||||
}
|
||||
|
||||
type EipInstanceType string
|
||||
|
||||
const (
|
||||
EcsInstance = "EcsInstance"
|
||||
SlbInstance = "SlbInstance"
|
||||
Nat = "Nat"
|
||||
HaVip = "HaVip"
|
||||
)
|
||||
|
||||
type AssociateEipAddressArgs struct {
|
||||
AllocationId string
|
||||
InstanceId string
|
||||
InstanceType EipInstanceType
|
||||
}
|
||||
|
||||
type AssociateEipAddressResponse struct {
|
||||
|
@ -102,6 +114,11 @@ func (client *Client) AssociateEipAddress(allocationId string, instanceId string
|
|||
return client.Invoke("AssociateEipAddress", &args, &response)
|
||||
}
|
||||
|
||||
func (client *Client) NewAssociateEipAddress(args *AssociateEipAddressArgs) error {
|
||||
response := ModifyInstanceNetworkSpecResponse{}
|
||||
return client.Invoke("AssociateEipAddress", args, &response)
|
||||
}
|
||||
|
||||
// Status of disks
|
||||
type EipStatus string
|
||||
|
||||
|
@ -112,11 +129,22 @@ const (
|
|||
EipStatusAvailable = EipStatus("Available")
|
||||
)
|
||||
|
||||
type AssociatedInstanceType string
|
||||
|
||||
const (
|
||||
AssociatedInstanceTypeEcsInstance = AssociatedInstanceType("EcsInstance")
|
||||
AssociatedInstanceTypeSlbInstance = AssociatedInstanceType("SlbInstance")
|
||||
AssociatedInstanceTypeNat = AssociatedInstanceType("Nat")
|
||||
AssociatedInstanceTypeHaVip = AssociatedInstanceType("HaVip")
|
||||
)
|
||||
|
||||
type DescribeEipAddressesArgs struct {
|
||||
RegionId common.Region
|
||||
Status EipStatus //enum Associating | Unassociating | InUse | Available
|
||||
EipAddress string
|
||||
AllocationId string
|
||||
RegionId common.Region
|
||||
Status EipStatus //enum Associating | Unassociating | InUse | Available
|
||||
EipAddress string
|
||||
AllocationId string
|
||||
AssociatedInstanceType AssociatedInstanceType //enum EcsInstance | SlbInstance | Nat | HaVip
|
||||
AssociatedInstanceId string //绑定的资源的Id。 这是一个过滤器性质的参数,若不指定,则表示不适用该条件对结果进行过滤。 如果要使用该过滤器,必须同时使用AssociatedInstanceType。若InstanceType为EcsInstance,则此处填写ECS实例Id。若InstanceType为SlbInstance,则此处填写VPC类型的私网SLB 的实例ID。若InstanceType为Nat,则此处填写NAT 的实例ID。。若InstanceType为HaVip,则此处填写HaVipId。
|
||||
common.Pagination
|
||||
}
|
||||
|
||||
|
@ -128,6 +156,7 @@ type EipAddressSetType struct {
|
|||
AllocationId string
|
||||
Status EipStatus
|
||||
InstanceId string
|
||||
InstanceType string
|
||||
Bandwidth string // Why string
|
||||
InternetChargeType common.InternetChargeType
|
||||
OperationLocks OperationLocksType
|
||||
|
|
|
@ -100,8 +100,8 @@ func (client *Client) DescribeRouteTablesWithRaw(args *DescribeRouteTablesArgs)
|
|||
type NextHopType string
|
||||
|
||||
const (
|
||||
NextHopIntance = NextHopType("Instance") //Default
|
||||
NextHopTunnel = NextHopType("Tunnel")
|
||||
NextHopIntance = NextHopType("Instance") //Default
|
||||
NextHopTunnel = NextHopType("Tunnel")
|
||||
NextHopTunnelRouterInterface = NextHopType("RouterInterface")
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
)
|
||||
|
||||
|
@ -19,6 +21,8 @@ const (
|
|||
Idl = InterfaceStatus("Idl")
|
||||
Active = InterfaceStatus("Active")
|
||||
Inactive = InterfaceStatus("Inactive")
|
||||
// 'Idle' means the router interface is not connected. 'Idl' may be a incorrect status.
|
||||
Idle = InterfaceStatus("Idle")
|
||||
|
||||
InitiatingSide = Role("InitiatingSide")
|
||||
AcceptingSide = Role("AcceptingSide")
|
||||
|
@ -77,7 +81,7 @@ type Filter struct {
|
|||
type DescribeRouterInterfacesArgs struct {
|
||||
RegionId common.Region
|
||||
common.Pagination
|
||||
Filter []Filter
|
||||
Filter []Filter
|
||||
}
|
||||
|
||||
type RouterInterfaceItemType struct {
|
||||
|
@ -225,3 +229,29 @@ func (client *Client) DeleteRouterInterface(args *OperateRouterInterfaceArgs) (r
|
|||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// WaitForRouterInterface waits for router interface to given status
|
||||
func (client *Client) WaitForRouterInterfaceAsyn(regionId common.Region, interfaceId string, status InterfaceStatus, timeout int) error {
|
||||
if timeout <= 0 {
|
||||
timeout = InstanceDefaultTimeout
|
||||
}
|
||||
for {
|
||||
interfaces, err := client.DescribeRouterInterfaces(&DescribeRouterInterfacesArgs{
|
||||
RegionId: regionId,
|
||||
Filter: []Filter{Filter{Key: "RouterInterfaceId", Value: []string{interfaceId}}},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if interfaces != nil && InterfaceStatus(interfaces.RouterInterfaceSet.RouterInterfaceType[0].Status) == status {
|
||||
//TODO
|
||||
break
|
||||
}
|
||||
timeout = timeout - DefaultWaitForInterval
|
||||
if timeout <= 0 {
|
||||
return common.GetClientErrorFromString("Timeout")
|
||||
}
|
||||
time.Sleep(DefaultWaitForInterval * time.Second)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,10 +6,15 @@ import (
|
|||
)
|
||||
|
||||
type NicType string
|
||||
type Direction string
|
||||
|
||||
const (
|
||||
NicTypeInternet = NicType("internet")
|
||||
NicTypeIntranet = NicType("intranet")
|
||||
|
||||
DirectionIngress = Direction("ingress")
|
||||
DirectionEgress = Direction("egress")
|
||||
DirectionAll = Direction("all")
|
||||
)
|
||||
|
||||
type IpProtocol string
|
||||
|
@ -29,11 +34,18 @@ const (
|
|||
PermissionPolicyDrop = PermissionPolicy("drop")
|
||||
)
|
||||
|
||||
type GroupInnerAccessPolicy string
|
||||
|
||||
const (
|
||||
GroupInnerAccept = GroupInnerAccessPolicy("Accept")
|
||||
GroupInnerDrop = GroupInnerAccessPolicy("Drop")
|
||||
)
|
||||
|
||||
type DescribeSecurityGroupAttributeArgs struct {
|
||||
SecurityGroupId string
|
||||
RegionId common.Region
|
||||
NicType NicType //enum for internet (default) |intranet
|
||||
Direction string // enum ingress egress
|
||||
NicType NicType //enum for internet (default) |intranet
|
||||
Direction Direction // enum ingress egress
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -64,7 +76,8 @@ type DescribeSecurityGroupAttributeResponse struct {
|
|||
Permissions struct {
|
||||
Permission []PermissionType
|
||||
}
|
||||
VpcId string
|
||||
VpcId string
|
||||
InnerAccessPolicy GroupInnerAccessPolicy
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -197,6 +210,21 @@ func (client *Client) ModifySecurityGroupAttribute(args *ModifySecurityGroupAttr
|
|||
return err
|
||||
}
|
||||
|
||||
type ModifySecurityGroupPolicyArgs struct {
|
||||
RegionId common.Region
|
||||
SecurityGroupId string
|
||||
InnerAccessPolicy GroupInnerAccessPolicy
|
||||
}
|
||||
|
||||
// ModifySecurityGroupPolicy modifies inner access policy of security group
|
||||
//
|
||||
// You can read doc at https://www.alibabacloud.com/help/doc-detail/57315.htm
|
||||
func (client *Client) ModifySecurityGroupPolicy(args *ModifySecurityGroupPolicyArgs) error {
|
||||
response := common.Response{}
|
||||
err := client.Invoke("ModifySecurityGroupPolicy", args, &response)
|
||||
return err
|
||||
}
|
||||
|
||||
type AuthorizeSecurityGroupArgs struct {
|
||||
SecurityGroupId string
|
||||
RegionId common.Region
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue