mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +00:00
Add a util/exec interface for testing execs.
This commit is contained in:
parent
ab6065944c
commit
20a1829dad
18
pkg/util/exec/doc.go
Normal file
18
pkg/util/exec/doc.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 provides an injectable interface and implementations for running commands.
|
||||||
|
package exec
|
95
pkg/util/exec/exec.go
Normal file
95
pkg/util/exec/exec.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
osexec "os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps exec.Cmd so we can capture errors.
|
||||||
|
type cmdWrapper osexec.Cmd
|
||||||
|
|
||||||
|
// CombinedOutput is part of the Cmd interface.
|
||||||
|
func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
|
||||||
|
out, err := (*osexec.Cmd)(cmd).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
ee, ok := err.(*osexec.ExitError)
|
||||||
|
if !ok {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
// Force a compile fail if exitErrorWrapper can't convert to ExitError.
|
||||||
|
var x ExitError = &exitErrorWrapper{ee}
|
||||||
|
return out, x
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
83
pkg/util/exec/exec_test.go
Normal file
83
pkg/util/exec/exec_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecutorNoArgs(t *testing.T) {
|
||||||
|
ex := New()
|
||||||
|
|
||||||
|
cmd := ex.Command("/bin/true")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected success, got %+v", err)
|
||||||
|
}
|
||||||
|
if len(out) != 0 {
|
||||||
|
t.Errorf("expected no output, got %q", string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = ex.Command("/bin/false")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected failure, got nil error")
|
||||||
|
}
|
||||||
|
if len(out) != 0 {
|
||||||
|
t.Errorf("expected no output, got %q", string(out))
|
||||||
|
}
|
||||||
|
ee, ok := err.(ExitError)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("expected an ExitError, got %+v", err)
|
||||||
|
}
|
||||||
|
if ee.Exited() {
|
||||||
|
if code := ee.ExitStatus(); code != 1 {
|
||||||
|
t.Errorf("expected exit status 1, got %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = ex.Command("/does/not/exist")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected failure, got nil error")
|
||||||
|
}
|
||||||
|
if ee, ok := err.(ExitError); ok {
|
||||||
|
t.Errorf("expected non-ExitError, got %+v", ee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecutorWithArgs(t *testing.T) {
|
||||||
|
ex := New()
|
||||||
|
|
||||||
|
cmd := ex.Command("/bin/echo", "stdout")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected success, got %+v", err)
|
||||||
|
}
|
||||||
|
if string(out) != "stdout\n" {
|
||||||
|
t.Errorf("unexpected output: %q", string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = ex.Command("/bin/sh", "-c", "echo stderr > /dev/stderr")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected success, got %+v", err)
|
||||||
|
}
|
||||||
|
if string(out) != "stderr\n" {
|
||||||
|
t.Errorf("unexpected output: %q", string(out))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user