Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
c3fe759f
Commit
c3fe759f
authored
Aug 16, 2016
by
Andy Goldstein
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Always return exec command output
Always return exec command output, even if the command invocation returns nonzero. This applies to exec probes and kubelet RunInContainer calls.
parent
7b762fb3
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
152 additions
and
38 deletions
+152
-38
kubelet.go
pkg/kubelet/kubelet.go
+4
-5
kubelet_test.go
pkg/kubelet/kubelet_test.go
+50
-28
prober.go
pkg/kubelet/prober/prober.go
+6
-5
prober_test.go
pkg/kubelet/prober/prober_test.go
+92
-0
No files found.
pkg/kubelet/kubelet.go
View file @
c3fe759f
...
...
@@ -2880,11 +2880,10 @@ func (kl *Kubelet) RunInContainer(podFullName string, podUID types.UID, containe
var
buffer
bytes
.
Buffer
output
:=
ioutils
.
WriteCloserWrapper
(
&
buffer
)
err
=
kl
.
runner
.
ExecInContainer
(
container
.
ID
,
cmd
,
nil
,
output
,
output
,
false
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
return
buffer
.
Bytes
(),
nil
// Even if err is non-nil, there still may be output (e.g. the exec wrote to stdout or stderr but
// the command returned a nonzero exit code). Therefore, always return the output along with the
// error.
return
buffer
.
Bytes
(),
err
}
// ExecInContainer executes a command in a container, connecting the supplied
...
...
pkg/kubelet/kubelet_test.go
View file @
c3fe759f
...
...
@@ -18,6 +18,7 @@ package kubelet
import
(
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
...
...
@@ -975,6 +976,7 @@ func TestMakeVolumeMounts(t *testing.T) {
}
type
fakeContainerCommandRunner
struct
{
// what was passed in
Cmd
[]
string
ID
kubecontainer
.
ContainerID
PodID
types
.
UID
...
...
@@ -985,15 +987,25 @@ type fakeContainerCommandRunner struct {
TTY
bool
Port
uint16
Stream
io
.
ReadWriteCloser
// what to return
StdoutData
string
StderrData
string
}
func
(
f
*
fakeContainerCommandRunner
)
ExecInContainer
(
id
kubecontainer
.
ContainerID
,
cmd
[]
string
,
in
io
.
Reader
,
out
,
err
io
.
WriteCloser
,
tty
bool
,
resize
<-
chan
term
.
Size
)
error
{
// record params
f
.
Cmd
=
cmd
f
.
ID
=
id
f
.
Stdin
=
in
f
.
Stdout
=
out
f
.
Stderr
=
err
f
.
TTY
=
tty
// Copy stdout/stderr data
fmt
.
Fprint
(
out
,
f
.
StdoutData
)
fmt
.
Fprint
(
out
,
f
.
StderrData
)
return
f
.
E
}
...
...
@@ -1027,35 +1039,45 @@ func TestRunInContainerNoSuchPod(t *testing.T) {
}
func
TestRunInContainer
(
t
*
testing
.
T
)
{
testKubelet
:=
newTestKubelet
(
t
,
false
/* controllerAttachDetachEnabled */
)
kubelet
:=
testKubelet
.
kubelet
fakeRuntime
:=
testKubelet
.
fakeRuntime
fakeCommandRunner
:=
fakeContainerCommandRunner
{}
kubelet
.
runner
=
&
fakeCommandRunner
containerID
:=
kubecontainer
.
ContainerID
{
Type
:
"test"
,
ID
:
"abc1234"
}
fakeRuntime
.
PodList
=
[]
*
containertest
.
FakePod
{
{
Pod
:
&
kubecontainer
.
Pod
{
ID
:
"12345678"
,
Name
:
"podFoo"
,
Namespace
:
"nsFoo"
,
Containers
:
[]
*
kubecontainer
.
Container
{
{
Name
:
"containerFoo"
,
ID
:
containerID
,
for
_
,
testError
:=
range
[]
error
{
nil
,
errors
.
New
(
"foo"
)}
{
testKubelet
:=
newTestKubelet
(
t
,
false
/* controllerAttachDetachEnabled */
)
kubelet
:=
testKubelet
.
kubelet
fakeRuntime
:=
testKubelet
.
fakeRuntime
fakeCommandRunner
:=
fakeContainerCommandRunner
{
E
:
testError
,
StdoutData
:
"foo"
,
StderrData
:
"bar"
,
}
kubelet
.
runner
=
&
fakeCommandRunner
containerID
:=
kubecontainer
.
ContainerID
{
Type
:
"test"
,
ID
:
"abc1234"
}
fakeRuntime
.
PodList
=
[]
*
containertest
.
FakePod
{
{
Pod
:
&
kubecontainer
.
Pod
{
ID
:
"12345678"
,
Name
:
"podFoo"
,
Namespace
:
"nsFoo"
,
Containers
:
[]
*
kubecontainer
.
Container
{
{
Name
:
"containerFoo"
,
ID
:
containerID
,
},
},
},
}},
}
cmd
:=
[]
string
{
"ls"
}
_
,
err
:=
kubelet
.
RunInContainer
(
"podFoo_nsFoo"
,
""
,
"containerFoo"
,
cmd
)
if
fakeCommandRunner
.
ID
!=
containerID
{
t
.
Errorf
(
"unexpected Name: %s"
,
fakeCommandRunner
.
ID
)
}
if
!
reflect
.
DeepEqual
(
fakeCommandRunner
.
Cmd
,
cmd
)
{
t
.
Errorf
(
"unexpected command: %s"
,
fakeCommandRunner
.
Cmd
)
}
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}},
}
cmd
:=
[]
string
{
"ls"
}
actualOutput
,
err
:=
kubelet
.
RunInContainer
(
"podFoo_nsFoo"
,
""
,
"containerFoo"
,
cmd
)
if
fakeCommandRunner
.
ID
!=
containerID
{
t
.
Errorf
(
"(testError=%v) unexpected Name: %s"
,
testError
,
fakeCommandRunner
.
ID
)
}
if
!
reflect
.
DeepEqual
(
fakeCommandRunner
.
Cmd
,
cmd
)
{
t
.
Errorf
(
"(testError=%v) unexpected command: %s"
,
testError
,
fakeCommandRunner
.
Cmd
)
}
// this isn't 100% foolproof as a bug in a real ContainerCommandRunner where it fails to copy to stdout/stderr wouldn't be caught by this test
if
"foobar"
!=
string
(
actualOutput
)
{
t
.
Errorf
(
"(testError=%v) unexpected output %q"
,
testError
,
actualOutput
)
}
if
e
,
a
:=
fmt
.
Sprintf
(
"%v"
,
testError
),
fmt
.
Sprintf
(
"%v"
,
err
);
e
!=
a
{
t
.
Errorf
(
"(testError=%v) error: expected %s, got %s"
,
testError
,
e
,
a
)
}
}
}
...
...
pkg/kubelet/prober/prober.go
View file @
c3fe759f
...
...
@@ -223,6 +223,8 @@ func formatURL(scheme string, host string, port int, path string) *url.URL {
}
type
execInContainer
struct
{
// run executes a command in a container. Combined stdout and stderr output is always returned. An
// error is returned if one occurred.
run
func
()
([]
byte
,
error
)
}
...
...
@@ -231,11 +233,10 @@ func (p *prober) newExecInContainer(container api.Container, containerID kubecon
var
buffer
bytes
.
Buffer
output
:=
ioutils
.
WriteCloserWrapper
(
&
buffer
)
err
:=
p
.
runner
.
ExecInContainer
(
containerID
,
cmd
,
nil
,
output
,
output
,
false
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
return
buffer
.
Bytes
(),
nil
// Even if err is non-nil, there still may be output (e.g. the exec wrote to stdout or stderr but
// the command returned a nonzero exit code). Therefore, always return the output along with the
// error.
return
buffer
.
Bytes
(),
err
}}
}
...
...
pkg/kubelet/prober/prober_test.go
View file @
c3fe759f
...
...
@@ -19,6 +19,7 @@ package prober
import
(
"errors"
"fmt"
"io"
"net/http"
"reflect"
"testing"
...
...
@@ -29,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/prober/results"
"k8s.io/kubernetes/pkg/probe"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/term"
)
func
TestFormatURL
(
t
*
testing
.
T
)
{
...
...
@@ -276,3 +278,93 @@ func TestProbe(t *testing.T) {
}
}
}
type
fakeContainerCommandRunner
struct
{
// what to return
stdoutData
string
stderrData
string
err
error
// actual values when invoked
containerID
kubecontainer
.
ContainerID
cmd
[]
string
stdin
io
.
Reader
tty
bool
resize
<-
chan
term
.
Size
}
var
_
kubecontainer
.
ContainerCommandRunner
=
&
fakeContainerCommandRunner
{}
func
(
f
*
fakeContainerCommandRunner
)
ExecInContainer
(
containerID
kubecontainer
.
ContainerID
,
cmd
[]
string
,
stdin
io
.
Reader
,
stdout
,
stderr
io
.
WriteCloser
,
tty
bool
,
resize
<-
chan
term
.
Size
)
error
{
// record invoked values
f
.
containerID
=
containerID
f
.
cmd
=
cmd
f
.
stdin
=
stdin
f
.
tty
=
tty
f
.
resize
=
resize
fmt
.
Fprint
(
stdout
,
f
.
stdoutData
)
fmt
.
Fprint
(
stdout
,
f
.
stderrData
)
return
f
.
err
}
func
(
f
*
fakeContainerCommandRunner
)
PortForward
(
pod
*
kubecontainer
.
Pod
,
port
uint16
,
stream
io
.
ReadWriteCloser
)
error
{
panic
(
"not implemented"
)
}
func
TestNewExecInContainer
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
err
error
}{
{
name
:
"no error"
,
err
:
nil
,
},
{
name
:
"error - make sure we get output"
,
err
:
errors
.
New
(
"bad"
),
},
}
for
_
,
test
:=
range
tests
{
runner
:=
&
fakeContainerCommandRunner
{
stdoutData
:
"foo"
,
stderrData
:
"bar"
,
err
:
test
.
err
,
}
prober
:=
&
prober
{
runner
:
runner
,
}
container
:=
api
.
Container
{}
containerID
:=
kubecontainer
.
ContainerID
{
Type
:
"docker"
,
ID
:
"containerID"
}
cmd
:=
[]
string
{
"/foo"
,
"bar"
}
exec
:=
prober
.
newExecInContainer
(
container
,
containerID
,
cmd
)
actualOutput
,
err
:=
exec
.
CombinedOutput
()
if
e
,
a
:=
containerID
,
runner
.
containerID
;
e
!=
a
{
t
.
Errorf
(
"%s: container id: expected %v, got %v"
,
test
.
name
,
e
,
a
)
}
if
e
,
a
:=
cmd
,
runner
.
cmd
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"%s: cmd: expected %v, got %v"
,
test
.
name
,
e
,
a
)
}
if
runner
.
stdin
!=
nil
{
t
.
Errorf
(
"%s: stdin: expected nil, got %v"
,
test
.
name
,
runner
.
stdin
)
}
if
runner
.
tty
{
t
.
Errorf
(
"%s: tty: expected false"
,
test
.
name
)
}
if
runner
.
resize
!=
nil
{
t
.
Errorf
(
"%s: resize chan: expected nil, got %v"
,
test
.
name
,
runner
.
resize
)
}
// this isn't 100% foolproof as a bug in a real ContainerCommandRunner where it fails to copy to stdout/stderr wouldn't be caught by this test
if
e
,
a
:=
"foobar"
,
string
(
actualOutput
);
e
!=
a
{
t
.
Errorf
(
"%s: output: expected %q, got %q"
,
test
.
name
,
e
,
a
)
}
if
e
,
a
:=
fmt
.
Sprintf
(
"%v"
,
test
.
err
),
fmt
.
Sprintf
(
"%v"
,
err
);
e
!=
a
{
t
.
Errorf
(
"%s: error: expected %s, got %s"
,
test
.
name
,
e
,
a
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment