diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 065d6b9..d5f9124 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,19 +7,28 @@ on: branches: [ master ] jobs: - - build: + test: runs-on: windows-latest steps: - - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 + - name: Set up Go 1.15 + uses: actions/setup-go@v1 with: go-version: 1.15 - - - name: Get - run: go get ./... + - name: Check-out code + uses: actions/checkout@v2 + - name: Run unit tests + run: | + .\test.ps1 - - name: Build - run: go build -v ./... + tidy: + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.15 + uses: actions/setup-go@v1 + with: + go-version: 1.15 + - name: Check-out code + uses: actions/checkout@v2 + - name: Check tidiness + run: | + ./ci/check-tidy.sh diff --git a/build.ps1 b/build.ps1 index 63cbd84..e34da1b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,2 +1 @@ -go get ./... go build -v ./... \ No newline at end of file diff --git a/ci/check-tidy.sh b/ci/check-tidy.sh new file mode 100755 index 0000000..c119e3a --- /dev/null +++ b/ci/check-tidy.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +: "${GO:=go}" + +goversion=$($GO version) + +$GO mod tidy + +rc=0 +diff=$(git diff --exit-code -- go.mod go.sum) || rc=$? + +if [ $rc -ne 0 ]; then + echo "Found some differences when running 'go mod tidy'" + echo "**********" + echo "$diff" + echo "**********" + echo "Please ensure you are using the correct version of go ($goversion), run 'go mod tidy', and commit the changes" +fi + +exit $rc diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a4407cc --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/rakelkar/gonetsh + +go 1.15 + +require ( + github.com/antoninbas/go-powershell v0.1.0 + github.com/stretchr/testify v1.6.1 + k8s.io/utils v0.0.0-20200410111917-5770800c2500 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8d3e329 --- /dev/null +++ b/go.sum @@ -0,0 +1,20 @@ +github.com/antoninbas/go-powershell v0.1.0 h1:LKwuZJt3loyr++Y2Qc+FZoUt8TwbrTWB3HublPoykxA= +github.com/antoninbas/go-powershell v0.1.0/go.mod h1:01pgKhz1CJxGnCWqXVDgvmp/QmHgWgEdxdYP+1azopE= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/utils v0.0.0-20200410111917-5770800c2500 h1:iwoQwtHT3aQtbNKwKlQ2HdCTxv3thi/Tog6lLqIpdO8= +k8s.io/utils v0.0.0-20200410111917-5770800c2500/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= diff --git a/netroute/netroute.go b/netroute/netroute.go index 3c0d296..5a82c9b 100644 --- a/netroute/netroute.go +++ b/netroute/netroute.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - ps "github.com/benmoss/go-powershell" - psbe "github.com/benmoss/go-powershell/backend" + ps "github.com/antoninbas/go-powershell" + psbe "github.com/antoninbas/go-powershell/backend" "fmt" "math/big" diff --git a/netsh/netsh_test.go b/netsh/netsh_test.go index 0553997..eae2eb8 100644 --- a/netsh/netsh_test.go +++ b/netsh/netsh_test.go @@ -5,23 +5,24 @@ import ( "testing" "github.com/stretchr/testify/assert" - utilexec "k8s.io/utils/exec" + "k8s.io/utils/exec" + fakeexec "k8s.io/utils/exec/testing" ) -func getFakeExecTemplate(fakeCmd *utilexec.FakeCmd) utilexec.FakeExec { - var fakeTemplate []utilexec.FakeCommandAction - for i := 0; i < len((*fakeCmd).CombinedOutputScript); i++ { - fakeTemplate = append(fakeTemplate, func(cmd string, args ...string) utilexec.Cmd { return utilexec.InitFakeCmd(fakeCmd, cmd, args...) }) +func getFakeExecTemplate(fakeCmd *fakeexec.FakeCmd) fakeexec.FakeExec { + var fakeTemplate []fakeexec.FakeCommandAction + for i := 0; i < len(fakeCmd.CombinedOutputScript); i++ { + fakeTemplate = append(fakeTemplate, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(fakeCmd, cmd, args...) }) } - return utilexec.FakeExec{ + return fakeexec.FakeExec{ CommandScript: fakeTemplate, } } func TestGetInterfacesGoldenPath(t *testing.T) { - fakeCmd := utilexec.FakeCmd{ - CombinedOutputScript: []utilexec.FakeCombinedOutputAction{ - func() ([]byte, error) { + fakeCmd := fakeexec.FakeCmd{ + CombinedOutputScript: []fakeexec.FakeAction{ + func() ([]byte, []byte, error) { return []byte(` Configuration for interface "Ethernet" @@ -46,16 +47,16 @@ Configuration for interface "Loopback Pseudo-Interface 1" Subnet Prefix: 127.0.0.0/8 (mask 255.0.0.0) InterfaceMetric: 75 - `), nil + `), nil, nil }, - func() ([]byte, error) { + func() ([]byte, []byte, error) { return []byte(` Idx Met MTU State Name --- ---------- ---------- ------------ --------------------------- 9 25 1500 connected Ethernet 1 75 4294967295 connected Loopback Pseudo-Interface 1 2 15 1500 connected Local Area Connection* 1 - 14 15 1500 connected Wi-Fi`), nil + 14 15 1500 connected Wi-Fi`), nil, nil }, }, } @@ -85,14 +86,14 @@ Configuration for interface "Loopback Pseudo-Interface 1" func TestGetInterfacesFailsGracefully(t *testing.T) { - fakeCmd := utilexec.FakeCmd{ - CombinedOutputScript: []utilexec.FakeCombinedOutputAction{ + fakeCmd := fakeexec.FakeCmd{ + CombinedOutputScript: []fakeexec.FakeAction{ // Failure. - func() ([]byte, error) { return nil, &utilexec.FakeExitError{Status: 2} }, + func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} }, // Empty Response. - func() ([]byte, error) { return []byte{}, nil }, + func() ([]byte, []byte, error) { return []byte{}, nil, nil }, // Junk Response. - func() ([]byte, error) { return []byte("fake error from netsh"), nil }, + func() ([]byte, []byte, error) { return []byte("fake error from netsh"), nil, nil }, }, } @@ -119,17 +120,17 @@ func TestGetInterfacesFailsGracefully(t *testing.T) { } func TestGetInterfaceNameToIndexMap(t *testing.T) { - fake := utilexec.FakeCmd{ - CombinedOutputScript: []utilexec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(`badinput`), nil }, - func() ([]byte, error) { + fake := fakeexec.FakeCmd{ + CombinedOutputScript: []fakeexec.FakeAction{ + func() ([]byte, []byte, error) { return []byte(`badinput`), nil, nil }, + func() ([]byte, []byte, error) { return []byte(` Idx Met MTU State Name --- ---------- ---------- ------------ --------------------------- 9 25 1500 connected Ethernet 1 75 4294967295 connected Loopback Pseudo-Interface 1 2 15 1500 connected vEthernet (New Virtual Switch) - 14 15 1500 connected vEthernet (HNS Internal NIC)`), nil + 14 15 1500 connected vEthernet (HNS Internal NIC)`), nil, nil }, }, } diff --git a/test.ps1 b/test.ps1 index 26cd43f..7088a89 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,2 +1 @@ -go get -t ./... go test -v ./... \ No newline at end of file diff --git a/vendor/github.com/benmoss/go-powershell/LICENSE b/vendor/github.com/benmoss/go-powershell/LICENSE deleted file mode 100644 index 0e06ff1..0000000 --- a/vendor/github.com/benmoss/go-powershell/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2017, Gorillalabs - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -http://www.opensource.org/licenses/MIT diff --git a/vendor/github.com/benmoss/go-powershell/README.md b/vendor/github.com/benmoss/go-powershell/README.md deleted file mode 100644 index 9387479..0000000 --- a/vendor/github.com/benmoss/go-powershell/README.md +++ /dev/null @@ -1,110 +0,0 @@ -# go-powershell - -This package is inspired by [jPowerShell](https://github.com/profesorfalken/jPowerShell) -and allows one to run and remote-control a PowerShell session. Use this if you -don't have a static script that you want to execute, bur rather run dynamic -commands. - -## Installation - - go get github.com/benmoss/go-powershell - -## Usage - -To start a PowerShell shell, you need a backend. Backends take care of starting -and controlling the actual powershell.exe process. In most cases, you will want -to use the Local backend, which just uses ``os/exec`` to start the process. - -```go -package main - -import ( - "fmt" - - ps "github.com/benmoss/go-powershell" - "github.com/benmoss/go-powershell/backend" -) - -func main() { - // choose a backend - back := &backend.Local{} - - // start a local powershell process - shell, err := ps.New(back) - if err != nil { - panic(err) - } - defer shell.Exit() - - // ... and interact with it - stdout, stderr, err := shell.Execute("Get-WmiObject -Class Win32_Processor") - if err != nil { - panic(err) - } - - fmt.Println(stdout) -} -``` - -## Remote Sessions - -You can use an existing PS shell to use PSSession cmdlets to connect to remote -computers. Instead of manually handling that, you can use the Session middleware, -which takes care of authentication. Note that you can still use the "raw" shell -to execute commands on the computer where the powershell host process is running. - -```go -package main - -import ( - "fmt" - - ps "github.com/benmoss/go-powershell" - "github.com/benmoss/go-powershell/backend" - "github.com/benmoss/go-powershell/middleware" -) - -func main() { - // choose a backend - back := &backend.Local{} - - // start a local powershell process - shell, err := ps.New(back) - if err != nil { - panic(err) - } - - // prepare remote session configuration - config := middleware.NewSessionConfig() - config.ComputerName = "remote-pc-1" - - // create a new shell by wrapping the existing one in the session middleware - session, err := middleware.NewSession(shell, config) - if err != nil { - panic(err) - } - defer session.Exit() // will also close the underlying ps shell! - - // everything run via the session is run on the remote machine - stdout, stderr, err = session.Execute("Get-WmiObject -Class Win32_Processor") - if err != nil { - panic(err) - } - - fmt.Println(stdout) -} -``` - -Note that a single shell instance is not safe for concurrent use, as are remote -sessions. You can have as many remote sessions using the same shell as you like, -but you must execute commands serially. If you need concurrency, you can just -spawn multiple PowerShell processes (i.e. call ``.New()`` multiple times). - -Also, note that all commands that you execute are wrapped in special echo -statements to delimit the stdout/stderr streams. After ``.Execute()``ing a command, -you can therefore not access ``$LastExitCode`` anymore and expect meaningful -results. - -## License - -MIT, see LICENSE file. diff --git a/vendor/github.com/benmoss/go-powershell/backend/local.go b/vendor/github.com/benmoss/go-powershell/backend/local.go deleted file mode 100644 index 429b1fe..0000000 --- a/vendor/github.com/benmoss/go-powershell/backend/local.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package backend - -import ( - "io" - "os/exec" - - "github.com/juju/errors" -) - -type Local struct{} - -func (b *Local) StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error) { - command := exec.Command(cmd, args...) - - stdin, err := command.StdinPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stdin stream") - } - - stdout, err := command.StdoutPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stdout stream") - } - - stderr, err := command.StderrPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stderr stream") - } - - err = command.Start() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not spawn PowerShell process") - } - - return command, stdin, stdout, stderr, nil -} diff --git a/vendor/github.com/benmoss/go-powershell/backend/ssh.go b/vendor/github.com/benmoss/go-powershell/backend/ssh.go deleted file mode 100644 index 68c9182..0000000 --- a/vendor/github.com/benmoss/go-powershell/backend/ssh.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package backend - -import ( - "fmt" - "io" - "regexp" - "strings" - - "github.com/juju/errors" -) - -// sshSession exists so we don't create a hard dependency on crypto/ssh. -type sshSession interface { - Waiter - - StdinPipe() (io.WriteCloser, error) - StdoutPipe() (io.Reader, error) - StderrPipe() (io.Reader, error) - Start(string) error -} - -type SSH struct { - Session sshSession -} - -func (b *SSH) StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error) { - stdin, err := b.Session.StdinPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stdin stream") - } - - stdout, err := b.Session.StdoutPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stdout stream") - } - - stderr, err := b.Session.StderrPipe() - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stderr stream") - } - - err = b.Session.Start(b.createCmd(cmd, args)) - if err != nil { - return nil, nil, nil, nil, errors.Annotate(err, "Could not spawn process via SSH") - } - - return b.Session, stdin, stdout, stderr, nil -} - -func (b *SSH) createCmd(cmd string, args []string) string { - parts := []string{cmd} - simple := regexp.MustCompile(`^[a-z0-9_/.~+-]+$`) - - for _, arg := range args { - if !simple.MatchString(arg) { - arg = b.quote(arg) - } - - parts = append(parts, arg) - } - - return strings.Join(parts, " ") -} - -func (b *SSH) quote(s string) string { - return fmt.Sprintf(`"%s"`, s) -} diff --git a/vendor/github.com/benmoss/go-powershell/backend/types.go b/vendor/github.com/benmoss/go-powershell/backend/types.go deleted file mode 100644 index 505471c..0000000 --- a/vendor/github.com/benmoss/go-powershell/backend/types.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package backend - -import "io" - -type Waiter interface { - Wait() error -} - -type Starter interface { - StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error) -} diff --git a/vendor/github.com/benmoss/go-powershell/middleware/session.go b/vendor/github.com/benmoss/go-powershell/middleware/session.go deleted file mode 100644 index 51b021c..0000000 --- a/vendor/github.com/benmoss/go-powershell/middleware/session.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package middleware - -import ( - "fmt" - "strings" - - "github.com/benmoss/go-powershell/utils" - "github.com/juju/errors" -) - -type session struct { - upstream Middleware - name string -} - -func NewSession(upstream Middleware, config *SessionConfig) (Middleware, error) { - asserted, ok := config.Credential.(credential) - if ok { - credentialParamValue, err := asserted.prepare(upstream) - if err != nil { - return nil, errors.Annotate(err, "Could not setup credentials") - } - - config.Credential = credentialParamValue - } - - name := "goSess" + utils.CreateRandomString(8) - args := strings.Join(config.ToArgs(), " ") - - _, _, err := upstream.Execute(fmt.Sprintf("$%s = New-PSSession %s", name, args)) - if err != nil { - return nil, errors.Annotate(err, "Could not create new PSSession") - } - - return &session{upstream, name}, nil -} - -func (s *session) Execute(cmd string) (string, string, error) { - return s.upstream.Execute(fmt.Sprintf("Invoke-Command -Session $%s -Script {%s}", s.name, cmd)) -} - -func (s *session) Exit() { - s.upstream.Execute(fmt.Sprintf("Disconnect-PSSession -Session $%s", s.name)) - s.upstream.Exit() -} diff --git a/vendor/github.com/benmoss/go-powershell/middleware/session_config.go b/vendor/github.com/benmoss/go-powershell/middleware/session_config.go deleted file mode 100644 index b37bba1..0000000 --- a/vendor/github.com/benmoss/go-powershell/middleware/session_config.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package middleware - -import ( - "fmt" - "strconv" - - "github.com/benmoss/go-powershell/utils" - "github.com/juju/errors" -) - -const ( - HTTPPort = 5985 - HTTPSPort = 5986 -) - -type SessionConfig struct { - ComputerName string - AllowRedirection bool - Authentication string - CertificateThumbprint string - Credential interface{} - Port int - UseSSL bool -} - -func NewSessionConfig() *SessionConfig { - return &SessionConfig{} -} - -func (c *SessionConfig) ToArgs() []string { - args := make([]string, 0) - - if c.ComputerName != "" { - args = append(args, "-ComputerName") - args = append(args, utils.QuoteArg(c.ComputerName)) - } - - if c.AllowRedirection { - args = append(args, "-AllowRedirection") - } - - if c.Authentication != "" { - args = append(args, "-Authentication") - args = append(args, utils.QuoteArg(c.Authentication)) - } - - if c.CertificateThumbprint != "" { - args = append(args, "-CertificateThumbprint") - args = append(args, utils.QuoteArg(c.CertificateThumbprint)) - } - - if c.Port > 0 { - args = append(args, "-Port") - args = append(args, strconv.Itoa(c.Port)) - } - - if asserted, ok := c.Credential.(string); ok { - args = append(args, "-Credential") - args = append(args, asserted) // do not quote, as it contains a variable name when using password auth - } - - if c.UseSSL { - args = append(args, "-UseSSL") - } - - return args -} - -type credential interface { - prepare(Middleware) (interface{}, error) -} - -type UserPasswordCredential struct { - Username string - Password string -} - -func (c *UserPasswordCredential) prepare(s Middleware) (interface{}, error) { - name := "goCred" + utils.CreateRandomString(8) - pwname := "goPass" + utils.CreateRandomString(8) - - _, _, err := s.Execute(fmt.Sprintf("$%s = ConvertTo-SecureString -String %s -AsPlainText -Force", pwname, utils.QuoteArg(c.Password))) - if err != nil { - return nil, errors.Annotate(err, "Could not convert password to secure string") - } - - _, _, err = s.Execute(fmt.Sprintf("$%s = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList %s, $%s", name, utils.QuoteArg(c.Username), pwname)) - if err != nil { - return nil, errors.Annotate(err, "Could not create PSCredential object") - } - - return fmt.Sprintf("$%s", name), nil -} diff --git a/vendor/github.com/benmoss/go-powershell/middleware/types.go b/vendor/github.com/benmoss/go-powershell/middleware/types.go deleted file mode 100644 index ce35ffa..0000000 --- a/vendor/github.com/benmoss/go-powershell/middleware/types.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package middleware - -type Middleware interface { - Execute(cmd string) (string, string, error) - Exit() -} diff --git a/vendor/github.com/benmoss/go-powershell/middleware/utf8.go b/vendor/github.com/benmoss/go-powershell/middleware/utf8.go deleted file mode 100644 index 51b0ff2..0000000 --- a/vendor/github.com/benmoss/go-powershell/middleware/utf8.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package middleware - -import ( - "encoding/base64" - "fmt" - - "github.com/benmoss/go-powershell/utils" -) - -// utf8 implements a primitive middleware that encodes all outputs -// as base64 to prevent encoding issues between remote PowerShell -// shells and the receiver. Just setting $OutputEncoding does not -// work reliably enough, sadly. -type utf8 struct { - upstream Middleware - wrapper string -} - -func NewUTF8(upstream Middleware) (Middleware, error) { - wrapper := "goUTF8" + utils.CreateRandomString(8) - - _, _, err := upstream.Execute(fmt.Sprintf(`function %s { process { if ($_) { [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($_)) } else { '' } } }`, wrapper)) - - return &utf8{upstream, wrapper}, err -} - -func (u *utf8) Execute(cmd string) (string, string, error) { - // Out-String to concat all lines into a single line, - // Write-Host to prevent line breaks at the "window width" - cmd = fmt.Sprintf(`%s | Out-String | %s | Write-Host`, cmd, u.wrapper) - - stdout, stderr, err := u.upstream.Execute(cmd) - if err != nil { - return stdout, stderr, err - } - - decoded, err := base64.StdEncoding.DecodeString(stdout) - if err != nil { - return stdout, stderr, err - } - - return string(decoded), stderr, nil -} - -func (u *utf8) Exit() { - u.upstream.Exit() -} diff --git a/vendor/github.com/benmoss/go-powershell/shell.go b/vendor/github.com/benmoss/go-powershell/shell.go deleted file mode 100644 index 586884c..0000000 --- a/vendor/github.com/benmoss/go-powershell/shell.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package powershell - -import ( - "fmt" - "io" - "regexp" - "sync" - - "github.com/benmoss/go-powershell/backend" - "github.com/benmoss/go-powershell/utils" - "github.com/juju/errors" -) - -const newline = "\r\n" - -type Shell interface { - Execute(cmd string) (string, string, error) - Exit() -} - -type shell struct { - handle backend.Waiter - stdin io.Writer - stdout io.Reader - stderr io.Reader -} - -func New(backend backend.Starter) (Shell, error) { - handle, stdin, stdout, stderr, err := backend.StartProcess("powershell.exe", "-NoExit", "-Command", "-") - if err != nil { - return nil, err - } - - return &shell{handle, stdin, stdout, stderr}, nil -} - -func (s *shell) Execute(cmd string) (string, string, error) { - if s.handle == nil { - return "", "", errors.Annotate(errors.New(cmd), "Cannot execute commands on closed shells.") - } - - outBoundary := createBoundary() - errBoundary := createBoundary() - - // wrap the command in special markers so we know when to stop reading from the pipes - full := fmt.Sprintf("%s; echo '%s'; [Console]::Error.WriteLine('%s')%s", cmd, outBoundary, errBoundary, newline) - - _, err := s.stdin.Write([]byte(full)) - if err != nil { - return "", "", errors.Annotate(errors.Annotate(err, cmd), "Could not send PowerShell command") - } - - // read stdout and stderr - sout := "" - serr := "" - - waiter := &sync.WaitGroup{} - waiter.Add(2) - - go streamReader(s.stdout, outBoundary, &sout, waiter) - go streamReader(s.stderr, errBoundary, &serr, waiter) - - waiter.Wait() - - if len(serr) > 0 { - return sout, serr, errors.Annotate(errors.New(cmd), serr) - } - - return sout, serr, nil -} - -func (s *shell) Exit() { - s.stdin.Write([]byte("exit" + newline)) - - // if it's possible to close stdin, do so (some backends, like the local one, - // do support it) - closer, ok := s.stdin.(io.Closer) - if ok { - closer.Close() - } - - s.handle.Wait() - - s.handle = nil - s.stdin = nil - s.stdout = nil - s.stderr = nil -} - -func streamReader(stream io.Reader, boundary string, buffer *string, signal *sync.WaitGroup) error { - // read all output until we have found our boundary token - output := "" - bufsize := 64 - marker := regexp.MustCompile("(?s)(.*)" + regexp.QuoteMeta(boundary)) - - for { - buf := make([]byte, bufsize) - read, err := stream.Read(buf) - if err != nil { - return err - } - - output = output + string(buf[:read]) - - if marker.MatchString(output) { - break - } - } - - *buffer = marker.FindStringSubmatch(output)[1] - signal.Done() - - return nil -} - -func createBoundary() string { - return "$gorilla" + utils.CreateRandomString(12) + "$" -} diff --git a/vendor/github.com/benmoss/go-powershell/shell_test.go b/vendor/github.com/benmoss/go-powershell/shell_test.go deleted file mode 100644 index 91acc71..0000000 --- a/vendor/github.com/benmoss/go-powershell/shell_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package powershell - -import ( - "io/ioutil" - "os" - "testing" - "time" - - "github.com/benmoss/go-powershell/backend" -) - -type context struct { - shell Shell -} - -func TestLocalShell(t *testing.T) { - c := context{} - dir, err := ioutil.TempDir("", "pwsh-test") - if err != nil { - t.Errorf("error from TempDir: %v", err) - } - defer os.RemoveAll(dir) - f, err := ioutil.TempFile(dir, "cowabunga") - if err != nil { - t.Errorf("error from Tempfile: %v", err) - } - f.Close() - if err := os.Chdir(dir); err != nil { - t.Errorf("error from Chdir: %v", err) - } - - s, err := New(&backend.Local{}) - if err != nil { - t.Errorf("error from New: %v", err) - } - c.shell = s - - t.Run("it can run commands", c.basicTest) - t.Run("it correctly parses its boundary token", c.boundaryTest) -} - -func (c *context) basicTest(t *testing.T) { - sout, serr, err := c.shell.Execute(`echo "hi from stdout"`) - if err != nil { - t.Errorf("error from Execute: %v", err) - } - - if sout != "hi from stdout\r\n" { - t.Errorf("unexpected stdout: %q", sout) - } - if serr != "" { - t.Errorf("unexpected stderr: %q", sout) - } -} - -func (c *context) boundaryTest(t *testing.T) { - done := make(chan bool) - for i := 0; i < 1000; i++ { - timeout := time.After(10 * time.Second) - - go func() { - _, _, err := c.shell.Execute("ls") - if err != nil { - t.Errorf("error from Execute: %v", err) - } - done <- true - }() - - select { - case <-timeout: - t.Fatalf("Timed out waiting for command after %d iterations", i) - case <-done: - continue - } - } -} diff --git a/vendor/github.com/benmoss/go-powershell/utils/quote.go b/vendor/github.com/benmoss/go-powershell/utils/quote.go deleted file mode 100644 index 84dd1d3..0000000 --- a/vendor/github.com/benmoss/go-powershell/utils/quote.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package utils - -import "strings" - -func QuoteArg(s string) string { - return "'" + strings.Replace(s, "'", "\"", -1) + "'" -} diff --git a/vendor/github.com/benmoss/go-powershell/utils/quote_test.go b/vendor/github.com/benmoss/go-powershell/utils/quote_test.go deleted file mode 100644 index f1c91fb..0000000 --- a/vendor/github.com/benmoss/go-powershell/utils/quote_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package utils - -import "testing" - -func TestQuotingArguments(t *testing.T) { - testcases := [][]string{ - {"", "''"}, - {"test", "'test'"}, - {"two words", "'two words'"}, - {"quo\"ted", "'quo\"ted'"}, - {"quo'ted", "'quo\"ted'"}, - {"quo\\'ted", "'quo\\\"ted'"}, - {"quo\"t'ed", "'quo\"t\"ed'"}, - {"es\\caped", "'es\\caped'"}, - {"es`caped", "'es`caped'"}, - {"es\\`caped", "'es\\`caped'"}, - } - - for i, testcase := range testcases { - quoted := QuoteArg(testcase[0]) - - if quoted != testcase[1] { - t.Errorf("test %02d failed: input '%s', expected %s, actual %s", i+1, testcase[0], testcase[1], quoted) - } - } -} diff --git a/vendor/github.com/benmoss/go-powershell/utils/rand.go b/vendor/github.com/benmoss/go-powershell/utils/rand.go deleted file mode 100644 index 245243e..0000000 --- a/vendor/github.com/benmoss/go-powershell/utils/rand.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package utils - -import ( - "crypto/rand" - "encoding/hex" -) - -func CreateRandomString(bytes int) string { - c := bytes - b := make([]byte, c) - - _, err := rand.Read(b) - if err != nil { - panic(err) - } - - return hex.EncodeToString(b) -} diff --git a/vendor/github.com/benmoss/go-powershell/utils/rand_test.go b/vendor/github.com/benmoss/go-powershell/utils/rand_test.go deleted file mode 100644 index 8000a9b..0000000 --- a/vendor/github.com/benmoss/go-powershell/utils/rand_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017 Gorillalabs. All rights reserved. - -package utils - -import "testing" - -func TestRandomStrings(t *testing.T) { - r1 := CreateRandomString(8) - r2 := CreateRandomString(8) - - if r1 == r2 { - t.Error("Failed to create random strings: The two generated strings are identical.") - } else if len(r1) != 16 { - t.Errorf("Expected the random string to contain 16 characters, but got %d.", len(r1)) - } -} diff --git a/vendor/k8s.io/utils/exec/exec.go b/vendor/k8s.io/utils/exec/exec.go deleted file mode 100644 index 5ea4b84..0000000 --- a/vendor/k8s.io/utils/exec/exec.go +++ /dev/null @@ -1,167 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package exec - -import ( - "io" - osexec "os/exec" - "syscall" -) - -// ErrExecutableNotFound is returned if the executable is not found. -var ErrExecutableNotFound = osexec.ErrNotFound - -// Interface is an interface that presents a subset of the os/exec API. Use this -// when you want to inject fakeable/mockable exec behavior. -type Interface interface { - // Command returns a Cmd instance which can be used to run a single command. - // This follows the pattern of package os/exec. - Command(cmd string, args ...string) Cmd - - // LookPath wraps os/exec.LookPath - LookPath(file string) (string, error) -} - -// Cmd is an interface that presents an API that is very similar to Cmd from os/exec. -// As more functionality is needed, this can grow. Since Cmd is a struct, we will have -// to replace fields with get/set method pairs. -type Cmd interface { - // CombinedOutput runs the command and returns its combined standard output - // and standard error. This follows the pattern of package os/exec. - CombinedOutput() ([]byte, error) - // Output runs the command and returns standard output, but not standard err - Output() ([]byte, error) - SetDir(dir string) - SetStdin(in io.Reader) - SetStdout(out io.Writer) -} - -// ExitError is an interface that presents an API similar to os.ProcessState, which is -// what ExitError from os/exec is. This is designed to make testing a bit easier and -// probably loses some of the cross-platform properties of the underlying library. -type ExitError interface { - String() string - Error() string - Exited() bool - ExitStatus() int -} - -// Implements Interface in terms of really exec()ing. -type executor struct{} - -// New returns a new Interface which will os/exec to run commands. -func New() Interface { - return &executor{} -} - -// Command is part of the Interface interface. -func (executor *executor) Command(cmd string, args ...string) Cmd { - return (*cmdWrapper)(osexec.Command(cmd, args...)) -} - -// LookPath is part of the Interface interface -func (executor *executor) LookPath(file string) (string, error) { - return osexec.LookPath(file) -} - -// Wraps exec.Cmd so we can capture errors. -type cmdWrapper osexec.Cmd - -func (cmd *cmdWrapper) SetDir(dir string) { - cmd.Dir = dir -} - -func (cmd *cmdWrapper) SetStdin(in io.Reader) { - cmd.Stdin = in -} - -func (cmd *cmdWrapper) SetStdout(out io.Writer) { - cmd.Stdout = out -} - -// CombinedOutput is part of the Cmd interface. -func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) { - out, err := (*osexec.Cmd)(cmd).CombinedOutput() - if err != nil { - return out, handleError(err) - } - return out, nil -} - -func (cmd *cmdWrapper) Output() ([]byte, error) { - out, err := (*osexec.Cmd)(cmd).Output() - if err != nil { - return out, handleError(err) - } - return out, nil -} - -func handleError(err error) error { - if ee, ok := err.(*osexec.ExitError); ok { - // Force a compile fail if exitErrorWrapper can't convert to ExitError. - var x ExitError = &ExitErrorWrapper{ee} - return x - } - if ee, ok := err.(*osexec.Error); ok { - if ee.Err == osexec.ErrNotFound { - return ErrExecutableNotFound - } - } - return err -} - -// ExitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError. -// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited(). -type ExitErrorWrapper struct { - *osexec.ExitError -} - -var _ ExitError = ExitErrorWrapper{} - -// ExitStatus is part of the ExitError interface. -func (eew ExitErrorWrapper) ExitStatus() int { - ws, ok := eew.Sys().(syscall.WaitStatus) - if !ok { - panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper") - } - return ws.ExitStatus() -} - -// CodeExitError is an implementation of ExitError consisting of an error object -// and an exit code (the upper bits of os.exec.ExitStatus). -type CodeExitError struct { - Err error - Code int -} - -var _ ExitError = CodeExitError{} - -func (e CodeExitError) Error() string { - return e.Err.Error() -} - -func (e CodeExitError) String() string { - return e.Err.Error() -} - -func (e CodeExitError) Exited() bool { - return true -} - -func (e CodeExitError) ExitStatus() int { - return e.Code -} \ No newline at end of file diff --git a/vendor/k8s.io/utils/exec/fake_exec.go b/vendor/k8s.io/utils/exec/fake_exec.go deleted file mode 100644 index 9ba6910..0000000 --- a/vendor/k8s.io/utils/exec/fake_exec.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package exec - -import ( - "fmt" - "io" -) - -// A simple scripted Interface type. -type FakeExec struct { - CommandScript []FakeCommandAction - CommandCalls int - LookPathFunc func(string) (string, error) -} - -type FakeCommandAction func(cmd string, args ...string) Cmd - -func (fake *FakeExec) Command(cmd string, args ...string) Cmd { - if fake.CommandCalls > len(fake.CommandScript)-1 { - panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args)) - } - i := fake.CommandCalls - fake.CommandCalls++ - return fake.CommandScript[i](cmd, args...) -} - -func (fake *FakeExec) LookPath(file string) (string, error) { - return fake.LookPathFunc(file) -} - -// A simple scripted Cmd type. -type FakeCmd struct { - Argv []string - CombinedOutputScript []FakeCombinedOutputAction - CombinedOutputCalls int - CombinedOutputLog [][]string - Dirs []string - Stdin io.Reader - Stdout io.Writer -} - -func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd { - fake.Argv = append([]string{cmd}, args...) - return fake -} - -type FakeCombinedOutputAction func() ([]byte, error) - -func (fake *FakeCmd) SetDir(dir string) { - fake.Dirs = append(fake.Dirs, dir) -} - -func (fake *FakeCmd) SetStdin(in io.Reader) { - fake.Stdin = in -} - -func (fake *FakeCmd) SetStdout(out io.Writer) { - fake.Stdout = out -} - -func (fake *FakeCmd) CombinedOutput() ([]byte, error) { - if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 { - panic("ran out of CombinedOutput() actions") - } - if fake.CombinedOutputLog == nil { - fake.CombinedOutputLog = [][]string{} - } - i := fake.CombinedOutputCalls - fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...)) - fake.CombinedOutputCalls++ - return fake.CombinedOutputScript[i]() -} - -func (fake *FakeCmd) Output() ([]byte, error) { - return nil, fmt.Errorf("unimplemented") -} - -// A simple fake ExitError type. -type FakeExitError struct { - Status int -} - -func (fake *FakeExitError) String() string { - return fmt.Sprintf("exit %d", fake.Status) -} - -func (fake *FakeExitError) Error() string { - return fake.String() -} - -func (fake *FakeExitError) Exited() bool { - return true -} - -func (fake *FakeExitError) ExitStatus() int { - return fake.Status -} \ No newline at end of file