Merge pull request #1041 from brendandburns/api

Add types for lifecycle events.
This commit is contained in:
Tim Hockin 2014-09-02 15:20:45 -07:00
commit d68a0ec383
6 changed files with 95 additions and 46 deletions

View File

@ -128,8 +128,8 @@ type EnvVar struct {
Value string `yaml:"value,omitempty" json:"value,omitempty"`
}
// HTTPGetProbe describes a liveness probe based on HTTP Get requests.
type HTTPGetProbe struct {
// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
// Optional: Path to access on the HTTP server.
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Required: Name or number of the port to access on the container.
@ -138,14 +138,14 @@ type HTTPGetProbe struct {
Host string `yaml:"host,omitempty" json:"host,omitempty"`
}
// TCPSocketProbe describes a liveness probe based on opening a socket.
type TCPSocketProbe struct {
// TCPSocketAction describes an action based on opening a socket
type TCPSocketAction struct {
// Required: Port to connect to.
Port util.IntOrString `yaml:"port,omitempty" json:"port,omitempty"`
}
// ExecProbe describes a "run in container" health probe.
type ExecProbe struct {
// ExecAction describes a "run in container" action.
type ExecAction struct {
// Command is the command line to execute inside the container, the working directory for the
// command is root ('/') in the container's filesystem. The command is simply exec'd, it is
// not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use
@ -154,15 +154,16 @@ type ExecProbe struct {
}
// LivenessProbe describes a liveness probe to be examined to the container.
// TODO: pass structured data to the actions, and document that data here.
type LivenessProbe struct {
// Type of liveness probe. Current legal values "http", "tcp"
Type string `yaml:"type,omitempty" json:"type,omitempty"`
// HTTPGetProbe parameters, required if Type == 'http'
HTTPGet *HTTPGetProbe `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
// TCPSocketProbe parameter, required if Type == 'tcp'
TCPSocket *TCPSocketProbe `yaml:"tcpSocket,omitempty" json:"tcpSocket,omitempty"`
TCPSocket *TCPSocketAction `yaml:"tcpSocket,omitempty" json:"tcpSocket,omitempty"`
// ExecProbe parameter, required if Type == 'exec'
Exec *ExecProbe `yaml:"exec,omitempty" json:"exec,omitempty"`
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
@ -186,6 +187,29 @@ type Container struct {
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty" json:"volumeMounts,omitempty"`
LivenessProbe *LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
}
// Handler defines a specific action that should be taken
// TODO: pass structured data to these actions, and document that data here.
type Handler struct {
// One and only one of the following should be specified.
// Exec specifies the action to take.
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// HTTPGet specifies the http request to perform.
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
}
// Lifecycle describes actions that the management system should take in response to container lifecycle
// events. For the PostStart and PreStop lifecycle handlers, management of the container blocks
// until the action is complete, unless the container process fails, in which case the handler is aborted.
type Lifecycle struct {
// PostStart is called immediately after a container is created. If the handler fails, the container
// is terminated and restarted.
PostStart *Handler `yaml:"postStart,omitempty" json:"postStart,omitempty"`
// PreStop is called immediately before a container is terminated. The reason for termination is
// passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated.
PreStop *Handler `yaml:"preStop,omitempty" json:"preStop,omitempty"`
}
// Event is the representation of an event logged to etcd backends.

View File

@ -137,8 +137,8 @@ type EnvVar struct {
Value string `yaml:"value,omitempty" json:"value,omitempty"`
}
// HTTPGetProbe describes a liveness probe based on HTTP Get requests.
type HTTPGetProbe struct {
// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
// Optional: Path to access on the HTTP server.
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Required: Name or number of the port to access on the container.
@ -147,14 +147,14 @@ type HTTPGetProbe struct {
Host string `yaml:"host,omitempty" json:"host,omitempty"`
}
// TCPSocketProbe describes a liveness probe based on opening a socket.
type TCPSocketProbe struct {
// TCPSocketAction describes an action based on opening a socket
type TCPSocketAction struct {
// Required: Port to connect to.
Port util.IntOrString `yaml:"port,omitempty" json:"port,omitempty"`
}
// ExecProbe describes a "run in container" health probe.
type ExecProbe struct {
// ExecAction describes a "run in container" action.
type ExecAction struct {
// Command is the command line to execute inside the container, the working directory for the
// command is root ('/') in the container's filesystem. The command is simply exec'd, it is
// not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use
@ -164,15 +164,16 @@ type ExecProbe struct {
}
// LivenessProbe describes a liveness probe to be examined to the container.
// TODO: pass structured data to the actions, and document that data here.
type LivenessProbe struct {
// Type of liveness probe. Current legal values "http", "tcp"
Type string `yaml:"type,omitempty" json:"type,omitempty"`
// HTTPGetProbe parameters, required if Type == 'http'
HTTPGet *HTTPGetProbe `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
// TCPSocketProbe parameter, required if Type == 'tcp'
TCPSocket *TCPSocketProbe `yaml:"tcpSocket,omitempty" json:"tcpSocket,omitempty"`
TCPSocket *TCPSocketAction `yaml:"tcpSocket,omitempty" json:"tcpSocket,omitempty"`
// ExecProbe parameter, required if Type == 'exec'
Exec *ExecProbe `yaml:"exec,omitempty" json:"exec,omitempty"`
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
@ -196,6 +197,30 @@ type Container struct {
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty" json:"volumeMounts,omitempty"`
LivenessProbe *LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
}
// Handler defines a specific action that should be taken
// TODO: merge this with liveness probing?
// TODO: pass structured data to these actions, and document that data here.
type Handler struct {
// One and only one of the following should be specified.
// Exec specifies the action to take.
Exec *ExecAction `yaml:"exec,omitempty" json:"exec,omitempty"`
// HTTPGet specifies the http request to perform.
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
}
// Lifecycle describes actions that the management system should take in response to container lifecycle
// events. For the PostStart and PreStop lifecycle handlers, management of the container blocks
// until the action is complete, unless the container process fails, in which case the handler is aborted.
type Lifecycle struct {
// PostStart is called immediately after a container is created. If the handler fails, the container
// is terminated and restarted.
PostStart *Handler `yaml:"postStart,omitempty" json:"postStart,omitempty"`
// PreStop is called immediately before a container is terminated. The reason for termination is
// passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated.
PreStop *Handler `yaml:"preStop,omitempty" json:"preStop,omitempty"`
}
// Event is the representation of an event logged to etcd backends.

View File

@ -53,19 +53,19 @@ func TestExec(t *testing.T) {
// Ok
{Healthy, &api.LivenessProbe{
Type: "exec",
Exec: &api.ExecProbe{Command: []string{"ls", "-l"}},
Exec: &api.ExecAction{Command: []string{"ls", "-l"}},
}, false, []byte("OK"), nil},
// Run returns error
{Unknown, &api.LivenessProbe{
Type: "exec",
Exec: &api.ExecProbe{
Exec: &api.ExecAction{
Command: []string{"ls", "-l"},
},
}, true, []byte("OK, NOT"), fmt.Errorf("test error")},
// Command error
{Unhealthy, &api.LivenessProbe{
Type: "exec",
Exec: &api.ExecProbe{
Exec: &api.ExecAction{
Command: []string{"ls", "-l"},
},
}, false, []byte{}, &exec.ExitError{}},

View File

@ -59,7 +59,7 @@ func TestHealthChecker(t *testing.T) {
}
container := api.Container{
LivenessProbe: &api.LivenessProbe{
HTTPGet: &api.HTTPGetProbe{
HTTPGet: &api.HTTPGetAction{
Port: util.NewIntOrStringFromString(port),
Path: "/foo/bar",
Host: host,
@ -128,7 +128,7 @@ func TestMuxHealthChecker(t *testing.T) {
}
container := api.Container{
LivenessProbe: &api.LivenessProbe{
HTTPGet: &api.HTTPGetProbe{},
HTTPGet: &api.HTTPGetAction{},
},
}
container.LivenessProbe.Type = tt.probeType

View File

@ -29,20 +29,20 @@ import (
func TestGetURLParts(t *testing.T) {
testCases := []struct {
probe *api.HTTPGetProbe
probe *api.HTTPGetAction
ok bool
host string
port int
path string
}{
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromInt(-1), Path: ""}, false, "", -1, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromString(""), Path: ""}, false, "", -1, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromString("-1"), Path: ""}, false, "", -1, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromString("not-found"), Path: ""}, false, "", -1, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromString("found"), Path: ""}, true, "127.0.0.1", 93, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromInt(76), Path: ""}, true, "127.0.0.1", 76, ""},
{&api.HTTPGetProbe{Host: "", Port: util.NewIntOrStringFromString("118"), Path: ""}, true, "127.0.0.1", 118, ""},
{&api.HTTPGetProbe{Host: "hostname", Port: util.NewIntOrStringFromInt(76), Path: "path"}, true, "hostname", 76, "path"},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromInt(-1), Path: ""}, false, "", -1, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString(""), Path: ""}, false, "", -1, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("-1"), Path: ""}, false, "", -1, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("not-found"), Path: ""}, false, "", -1, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("found"), Path: ""}, true, "127.0.0.1", 93, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromInt(76), Path: ""}, true, "127.0.0.1", 76, ""},
{&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("118"), Path: ""}, true, "127.0.0.1", 118, ""},
{&api.HTTPGetAction{Host: "hostname", Port: util.NewIntOrStringFromInt(76), Path: "path"}, true, "hostname", 76, "path"},
}
for _, test := range testCases {
@ -90,13 +90,13 @@ func TestFormatURL(t *testing.T) {
func TestHTTPHealthChecker(t *testing.T) {
testCases := []struct {
probe *api.HTTPGetProbe
probe *api.HTTPGetAction
status int
health Status
}{
// The probe will be filled in below. This is primarily testing that an HTTP GET happens.
{&api.HTTPGetProbe{}, http.StatusOK, Healthy},
{&api.HTTPGetProbe{}, -1, Unhealthy},
{&api.HTTPGetAction{}, http.StatusOK, Healthy},
{&api.HTTPGetAction{}, -1, Unhealthy},
{nil, -1, Unknown},
}
hc := &HTTPHealthChecker{

View File

@ -29,18 +29,18 @@ import (
func TestGetTCPAddrParts(t *testing.T) {
testCases := []struct {
probe *api.TCPSocketProbe
probe *api.TCPSocketAction
ok bool
host string
port int
}{
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromInt(-1)}, false, "", -1},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromString("")}, false, "", -1},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromString("-1")}, false, "", -1},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromString("not-found")}, false, "", -1},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromString("found")}, true, "1.2.3.4", 93},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromInt(76)}, true, "1.2.3.4", 76},
{&api.TCPSocketProbe{Port: util.NewIntOrStringFromString("118")}, true, "1.2.3.4", 118},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromInt(-1)}, false, "", -1},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromString("")}, false, "", -1},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromString("-1")}, false, "", -1},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromString("not-found")}, false, "", -1},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromString("found")}, true, "1.2.3.4", 93},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromInt(76)}, true, "1.2.3.4", 76},
{&api.TCPSocketAction{Port: util.NewIntOrStringFromString("118")}, true, "1.2.3.4", 118},
}
for _, test := range testCases {
@ -69,13 +69,13 @@ func TestGetTCPAddrParts(t *testing.T) {
func TestTcpHealthChecker(t *testing.T) {
tests := []struct {
probe *api.TCPSocketProbe
probe *api.TCPSocketAction
expectedStatus Status
expectError bool
}{
// The probe will be filled in below. This is primarily testing that a connection is made.
{&api.TCPSocketProbe{}, Healthy, false},
{&api.TCPSocketProbe{}, Unhealthy, false},
{&api.TCPSocketAction{}, Healthy, false},
{&api.TCPSocketAction{}, Unhealthy, false},
{nil, Unknown, true},
}