mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
Merge pull request #8011 from brendandburns/exec
Switch exec to look at exit code not output status.
This commit is contained in:
commit
a74522a738
@ -66,6 +66,7 @@ type DockerInterface interface {
|
|||||||
Info() (*docker.Env, error)
|
Info() (*docker.Env, error)
|
||||||
CreateExec(docker.CreateExecOptions) (*docker.Exec, error)
|
CreateExec(docker.CreateExecOptions) (*docker.Exec, error)
|
||||||
StartExec(string, docker.StartExecOptions) error
|
StartExec(string, docker.StartExecOptions) error
|
||||||
|
InspectExec(id string) (*docker.ExecInspect, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubeletContainerName encapsulates a pod name and a Kubernetes container name.
|
// KubeletContainerName encapsulates a pod name and a Kubernetes container name.
|
||||||
|
@ -47,6 +47,7 @@ type FakeDockerClient struct {
|
|||||||
RemovedImages util.StringSet
|
RemovedImages util.StringSet
|
||||||
VersionInfo docker.Env
|
VersionInfo docker.Env
|
||||||
Information docker.Env
|
Information docker.Env
|
||||||
|
ExecInspect *docker.ExecInspect
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeDockerClient) ClearCalls() {
|
func (f *FakeDockerClient) ClearCalls() {
|
||||||
@ -288,6 +289,10 @@ func (f *FakeDockerClient) StartExec(_ string, _ docker.StartExecOptions) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeDockerClient) InspectExec(id string) (*docker.ExecInspect, error) {
|
||||||
|
return f.ExecInspect, f.popError("inspect_exec")
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeDockerClient) ListImages(opts docker.ListImagesOptions) ([]docker.APIImages, error) {
|
func (f *FakeDockerClient) ListImages(opts docker.ListImagesOptions) ([]docker.APIImages, error) {
|
||||||
err := f.popError("list_images")
|
err := f.popError("list_images")
|
||||||
return f.Images, err
|
return f.Images, err
|
||||||
|
@ -153,3 +153,11 @@ func (in instrumentedDockerInterface) StartExec(startExec string, opts docker.St
|
|||||||
}()
|
}()
|
||||||
return in.client.StartExec(startExec, opts)
|
return in.client.StartExec(startExec, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (in instrumentedDockerInterface) InspectExec(id string) (*docker.ExecInspect, error) {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
metrics.DockerOperationsLatency.WithLabelValues("inspect_exec").Observe(metrics.SinceInMicroseconds(start))
|
||||||
|
}()
|
||||||
|
return in.client.InspectExec(id)
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||||
@ -874,10 +875,47 @@ func (dm *DockerManager) RunInContainer(containerID string, cmd []string) ([]byt
|
|||||||
RawTerminal: false,
|
RawTerminal: false,
|
||||||
}
|
}
|
||||||
err = dm.client.StartExec(execObj.ID, startOpts)
|
err = dm.client.StartExec(execObj.ID, startOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tick := time.Tick(2 * time.Second)
|
||||||
|
for {
|
||||||
|
inspect, err2 := dm.client.InspectExec(execObj.ID)
|
||||||
|
if err2 != nil {
|
||||||
|
return buf.Bytes(), err2
|
||||||
|
}
|
||||||
|
if !inspect.Running {
|
||||||
|
if inspect.ExitCode != 0 {
|
||||||
|
err = &dockerExitError{inspect}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
<-tick
|
||||||
|
}
|
||||||
|
|
||||||
return buf.Bytes(), err
|
return buf.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dockerExitError struct {
|
||||||
|
Inspect *docker.ExecInspect
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerExitError) String() string {
|
||||||
|
return d.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerExitError) Error() string {
|
||||||
|
return fmt.Sprintf("Error executing in Docker Container: %d", d.Inspect.ExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerExitError) Exited() bool {
|
||||||
|
return !d.Inspect.Running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerExitError) ExitStatus() int {
|
||||||
|
return d.Inspect.ExitCode
|
||||||
|
}
|
||||||
|
|
||||||
// ExecInContainer uses nsenter to run the command inside the container identified by containerID.
|
// ExecInContainer uses nsenter to run the command inside the container identified by containerID.
|
||||||
//
|
//
|
||||||
// TODO:
|
// TODO:
|
||||||
|
@ -619,3 +619,12 @@ func TestProbeContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsAExitError(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
err = &dockerExitError{nil}
|
||||||
|
_, ok := err.(uexec.ExitError)
|
||||||
|
if !ok {
|
||||||
|
t.Error("couldn't cast dockerExitError to exec.ExitError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,34 +17,35 @@ limitations under the License.
|
|||||||
package exec
|
package exec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||||
uexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultHealthyOutput = "ok"
|
|
||||||
|
|
||||||
func New() ExecProber {
|
func New() ExecProber {
|
||||||
return execProber{}
|
return execProber{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecProber interface {
|
type ExecProber interface {
|
||||||
Probe(e uexec.Cmd) (probe.Result, error)
|
Probe(e exec.Cmd) (probe.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type execProber struct{}
|
type execProber struct{}
|
||||||
|
|
||||||
func (pr execProber) Probe(e uexec.Cmd) (probe.Result, error) {
|
func (pr execProber) Probe(e exec.Cmd) (probe.Result, error) {
|
||||||
data, err := e.CombinedOutput()
|
data, err := e.CombinedOutput()
|
||||||
glog.V(4).Infof("health check response: %s", string(data))
|
glog.V(4).Infof("health check response: %s", string(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
exit, ok := err.(exec.ExitError)
|
||||||
|
if ok {
|
||||||
|
if exit.ExitStatus() == 0 {
|
||||||
|
return probe.Success, nil
|
||||||
|
} else {
|
||||||
|
return probe.Failure, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return probe.Unknown, err
|
return probe.Unknown, err
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(strings.ToLower(string(data)), defaultHealthyOutput) {
|
|
||||||
return probe.Failure, nil
|
|
||||||
}
|
|
||||||
return probe.Success, nil
|
return probe.Success, nil
|
||||||
}
|
}
|
||||||
|
@ -41,16 +41,40 @@ type healthCheckTest struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeExitError struct {
|
||||||
|
exited bool
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeExitError) String() string {
|
||||||
|
return f.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeExitError) Error() string {
|
||||||
|
return "fake exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeExitError) Exited() bool {
|
||||||
|
return f.exited
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeExitError) ExitStatus() int {
|
||||||
|
return f.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
func TestExec(t *testing.T) {
|
func TestExec(t *testing.T) {
|
||||||
prober := New()
|
prober := New()
|
||||||
fake := FakeCmd{}
|
fake := FakeCmd{}
|
||||||
|
|
||||||
tests := []healthCheckTest{
|
tests := []healthCheckTest{
|
||||||
// Ok
|
// Ok
|
||||||
{probe.Success, false, []byte("OK"), nil},
|
{probe.Success, false, []byte("OK"), nil},
|
||||||
|
// Ok
|
||||||
|
{probe.Success, false, []byte("OK"), &fakeExitError{true, 0}},
|
||||||
// Run returns error
|
// Run returns error
|
||||||
{probe.Unknown, true, []byte("OK, NOT"), fmt.Errorf("test error")},
|
{probe.Unknown, true, []byte("OK, NOT"), fmt.Errorf("test error")},
|
||||||
// Unhealthy
|
// Unhealthy
|
||||||
{probe.Failure, false, []byte("Fail"), nil},
|
{probe.Failure, false, []byte("Fail"), &fakeExitError{true, 1}},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
fake.out = test.output
|
fake.out = test.output
|
||||||
|
@ -421,7 +421,7 @@ var _ = Describe("Pods", func() {
|
|||||||
{
|
{
|
||||||
Name: "liveness",
|
Name: "liveness",
|
||||||
Image: "gcr.io/google_containers/busybox",
|
Image: "gcr.io/google_containers/busybox",
|
||||||
Command: []string{"/bin/sh", "-c", "echo ok >/tmp/health; sleep 10; echo fail >/tmp/health; sleep 600"},
|
Command: []string{"/bin/sh", "-c", "echo ok >/tmp/health; sleep 10; rm -rf /tmp/health; sleep 600"},
|
||||||
LivenessProbe: &api.Probe{
|
LivenessProbe: &api.Probe{
|
||||||
Handler: api.Handler{
|
Handler: api.Handler{
|
||||||
Exec: &api.ExecAction{
|
Exec: &api.ExecAction{
|
||||||
|
Loading…
Reference in New Issue
Block a user