mirror of https://github.com/containers/podman.git
Merge pull request #2491 from baude/healtcheckphase1
podman healthcheck run (phase 1)
This commit is contained in:
commit
c6c0b54c36
|
@ -217,6 +217,10 @@ type PauseValues struct {
|
||||||
All bool
|
All bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HealthCheckValues struct {
|
||||||
|
PodmanCommand
|
||||||
|
}
|
||||||
|
|
||||||
type KubePlayValues struct {
|
type KubePlayValues struct {
|
||||||
PodmanCommand
|
PodmanCommand
|
||||||
Authfile string
|
Authfile string
|
||||||
|
|
|
@ -121,3 +121,10 @@ func getSystemSubCommands() []*cobra.Command {
|
||||||
_renumberCommand,
|
_renumberCommand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commands that the local client implements
|
||||||
|
func getHealtcheckSubCommands() []*cobra.Command {
|
||||||
|
return []*cobra.Command{
|
||||||
|
_healthcheckrunCommand,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,3 +52,8 @@ func getTrustSubCommands() []*cobra.Command {
|
||||||
func getSystemSubCommands() []*cobra.Command {
|
func getSystemSubCommands() []*cobra.Command {
|
||||||
return []*cobra.Command{}
|
return []*cobra.Command{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commands that the remoteclient implements
|
||||||
|
func getHealtcheckSubCommands() []*cobra.Command {
|
||||||
|
return []*cobra.Command{}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
|
@ -117,6 +118,10 @@ func createInit(c *cliconfig.PodmanCommand) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(c *cliconfig.PodmanCommand, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
|
func createContainer(c *cliconfig.PodmanCommand, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
|
||||||
|
var (
|
||||||
|
hasHealthCheck bool
|
||||||
|
healthCheck *manifest.Schema2HealthConfig
|
||||||
|
)
|
||||||
if c.Bool("trace") {
|
if c.Bool("trace") {
|
||||||
span, _ := opentracing.StartSpanFromContext(Ctx, "createContainer")
|
span, _ := opentracing.StartSpanFromContext(Ctx, "createContainer")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
@ -163,12 +168,32 @@ func createContainer(c *cliconfig.PodmanCommand, runtime *libpod.Runtime) (*libp
|
||||||
} else {
|
} else {
|
||||||
imageName = newImage.ID()
|
imageName = newImage.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add healthcheck if it exists AND is correct mediatype
|
||||||
|
_, mediaType, err := newImage.Manifest(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "unable to determine mediatype of image %s", newImage.ID())
|
||||||
|
}
|
||||||
|
if mediaType == manifest.DockerV2Schema2MediaType {
|
||||||
|
healthCheck, err = newImage.GetHealthCheck(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0])
|
||||||
|
}
|
||||||
|
if healthCheck != nil {
|
||||||
|
hasHealthCheck = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
|
createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Because parseCreateOpts does derive anything from the image, we add health check
|
||||||
|
// at this point. The rest is done by WithOptions.
|
||||||
|
createConfig.HasHealthCheck = hasHealthCheck
|
||||||
|
createConfig.HealthCheck = healthCheck
|
||||||
|
|
||||||
ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx, nil)
|
ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var healthcheckDescription = "Manage health checks on containers"
|
||||||
|
var healthcheckCommand = cliconfig.PodmanCommand{
|
||||||
|
Command: &cobra.Command{
|
||||||
|
Use: "healthcheck",
|
||||||
|
Short: "Manage Healthcheck",
|
||||||
|
Long: healthcheckDescription,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands that are universally implemented
|
||||||
|
var healthcheckCommands []*cobra.Command
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
healthcheckCommand.AddCommand(healthcheckCommands...)
|
||||||
|
healthcheckCommand.AddCommand(getHealtcheckSubCommands()...)
|
||||||
|
healthcheckCommand.SetUsageTemplate(UsageTemplate())
|
||||||
|
rootCmd.AddCommand(healthcheckCommand.Command)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/adapter"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
healthcheckRunCommand cliconfig.HealthCheckValues
|
||||||
|
healthcheckRunDescription = "run the health check of a container"
|
||||||
|
_healthcheckrunCommand = &cobra.Command{
|
||||||
|
Use: "run CONTAINER",
|
||||||
|
Short: "run the health check of a container",
|
||||||
|
Long: healthcheckRunDescription,
|
||||||
|
Example: `podman healthcheck run mywebapp`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
healthcheckRunCommand.InputArgs = args
|
||||||
|
healthcheckRunCommand.GlobalFlags = MainGlobalOpts
|
||||||
|
return healthCheckCmd(&healthcheckRunCommand)
|
||||||
|
},
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 || len(args) > 1 {
|
||||||
|
return errors.New("must provide the name or ID of one container")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
healthcheckRunCommand.Command = _healthcheckrunCommand
|
||||||
|
healthcheckRunCommand.SetUsageTemplate(UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func healthCheckCmd(c *cliconfig.HealthCheckValues) error {
|
||||||
|
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get runtime")
|
||||||
|
}
|
||||||
|
status, err := runtime.HealthCheck(c)
|
||||||
|
if err != nil {
|
||||||
|
if status == libpod.HealthCheckFailure {
|
||||||
|
fmt.Println("\nunhealthy")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("\nhealthy")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -888,6 +888,26 @@ _podman_container_wait() {
|
||||||
_podman_wait
|
_podman_wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_healthcheck() {
|
||||||
|
local boolean_options="
|
||||||
|
--help
|
||||||
|
-h
|
||||||
|
"
|
||||||
|
subcommands="
|
||||||
|
run
|
||||||
|
"
|
||||||
|
__podman_subcommands "$subcommands $aliases" && return
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_podman_generate() {
|
_podman_generate() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--help
|
--help
|
||||||
|
@ -2338,6 +2358,27 @@ _podman_logout() {
|
||||||
_complete_ "$options_with_args" "$boolean_options"
|
_complete_ "$options_with_args" "$boolean_options"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_healtcheck_run() {
|
||||||
|
local options_with_args=""
|
||||||
|
|
||||||
|
local boolean_options="
|
||||||
|
-h
|
||||||
|
--help
|
||||||
|
"
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=( $( compgen -W "
|
||||||
|
$(__podman_containers --all)
|
||||||
|
" -- "$cur" ) )
|
||||||
|
__ltrim_colon_completions "$cur"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_podman_generate_kube() {
|
_podman_generate_kube() {
|
||||||
local options_with_args=""
|
local options_with_args=""
|
||||||
|
|
||||||
|
@ -2979,6 +3020,7 @@ _podman_podman() {
|
||||||
exec
|
exec
|
||||||
export
|
export
|
||||||
generate
|
generate
|
||||||
|
healthcheck
|
||||||
history
|
history
|
||||||
image
|
image
|
||||||
images
|
images
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
% podman-healthcheck-run(1)
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
podman\-healthcheck\-run- Run a container healthcheck
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**podman healthcheck run** [*options*] *container*
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
Runs the healthcheck command defined in a running container manually. The resulting error codes are defined
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
* 0 = healthcheck command succeeded
|
||||||
|
* 1 = healthcheck command failed
|
||||||
|
* 125 = an error has occurred
|
||||||
|
|
||||||
|
Possible errors that can occur during the healthcheck are:
|
||||||
|
* unable to find the container
|
||||||
|
* container has no defined healthcheck
|
||||||
|
* container is not running
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
**--help**
|
||||||
|
|
||||||
|
Print usage statement
|
||||||
|
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
$ podman healtcheck run mywebapp
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
podman-healthcheck(1)
|
||||||
|
|
||||||
|
## HISTORY
|
||||||
|
Feb 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
|
|
@ -0,0 +1,22 @@
|
||||||
|
% podman-healthcheck(1)
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
podman\-healthcheck- Manage healthchecks for containers
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**podman healthcheck** *subcommand*
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
podman healthcheck is a set of subcommands that manage container healthchecks
|
||||||
|
|
||||||
|
## SUBCOMMANDS
|
||||||
|
|
||||||
|
| Command | Man Page | Description |
|
||||||
|
| ------- | ------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||||
|
| run | [podman-healthcheck-run(1)](podman-healthcheck-run.1.md) | Run a container healthcheck |
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
podman(1)
|
||||||
|
|
||||||
|
## HISTORY
|
||||||
|
Feb 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
cnitypes "github.com/containernetworking/cni/pkg/types/current"
|
cnitypes "github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/libpod/libpod/lock"
|
"github.com/containers/libpod/libpod/lock"
|
||||||
"github.com/containers/libpod/pkg/namespaces"
|
"github.com/containers/libpod/pkg/namespaces"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
@ -365,6 +366,9 @@ type ContainerConfig struct {
|
||||||
|
|
||||||
// Systemd tells libpod to setup the container in systemd mode
|
// Systemd tells libpod to setup the container in systemd mode
|
||||||
Systemd bool `json:"systemd"`
|
Systemd bool `json:"systemd"`
|
||||||
|
|
||||||
|
// HealtchCheckConfig has the health check command and related timings
|
||||||
|
HealthCheckConfig *manifest.Schema2HealthConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStatus returns a string representation for users
|
// ContainerStatus returns a string representation for users
|
||||||
|
@ -1085,3 +1089,14 @@ func (c *Container) ContainerState() (*ContainerState, error) {
|
||||||
deepcopier.Copy(c.state).To(returnConfig)
|
deepcopier.Copy(c.state).To(returnConfig)
|
||||||
return c.state, nil
|
return c.state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasHealthCheck returns bool as to whether there is a health check
|
||||||
|
// defined for the container
|
||||||
|
func (c *Container) HasHealthCheck() bool {
|
||||||
|
return c.config.HealthCheckConfig != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckConfig returns the command and timing attributes of the health check
|
||||||
|
func (c *Container) HealthCheckConfig() *manifest.Schema2HealthConfig {
|
||||||
|
return c.config.HealthCheckConfig
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package libpod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthCheckStatus represents the current state of a container
|
||||||
|
type HealthCheckStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HealthCheckSuccess means the health worked
|
||||||
|
HealthCheckSuccess HealthCheckStatus = iota
|
||||||
|
// HealthCheckFailure means the health ran and failed
|
||||||
|
HealthCheckFailure HealthCheckStatus = iota
|
||||||
|
// HealthCheckContainerStopped means the health check cannot
|
||||||
|
// be run because the container is stopped
|
||||||
|
HealthCheckContainerStopped HealthCheckStatus = iota
|
||||||
|
// HealthCheckContainerNotFound means the container could
|
||||||
|
// not be found in local store
|
||||||
|
HealthCheckContainerNotFound HealthCheckStatus = iota
|
||||||
|
// HealthCheckNotDefined means the container has no health
|
||||||
|
// check defined in it
|
||||||
|
HealthCheckNotDefined HealthCheckStatus = iota
|
||||||
|
// HealthCheckInternalError means somes something failed obtaining or running
|
||||||
|
// a given health check
|
||||||
|
HealthCheckInternalError HealthCheckStatus = iota
|
||||||
|
// HealthCheckDefined means the healthcheck was found on the container
|
||||||
|
HealthCheckDefined HealthCheckStatus = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthCheck verifies the state and validity of the healthcheck configuration
|
||||||
|
// on the container and then executes the healthcheck
|
||||||
|
func (r *Runtime) HealthCheck(name string) (HealthCheckStatus, error) {
|
||||||
|
container, err := r.LookupContainer(name)
|
||||||
|
if err != nil {
|
||||||
|
return HealthCheckContainerNotFound, errors.Wrapf(err, "unable to lookup %s to perform a health check", name)
|
||||||
|
}
|
||||||
|
hcStatus, err := checkHealthCheckCanBeRun(container)
|
||||||
|
if err == nil {
|
||||||
|
return container.RunHealthCheck()
|
||||||
|
}
|
||||||
|
return hcStatus, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunHealthCheck runs the health check as defined by the container
|
||||||
|
func (c *Container) RunHealthCheck() (HealthCheckStatus, error) {
|
||||||
|
var newCommand []string
|
||||||
|
hcStatus, err := checkHealthCheckCanBeRun(c)
|
||||||
|
if err != nil {
|
||||||
|
return hcStatus, err
|
||||||
|
}
|
||||||
|
hcCommand := c.HealthCheckConfig().Test
|
||||||
|
if len(hcCommand) > 0 && hcCommand[0] == "CMD-SHELL" {
|
||||||
|
newCommand = []string{"sh", "-c"}
|
||||||
|
newCommand = append(newCommand, hcCommand[1:]...)
|
||||||
|
} else {
|
||||||
|
newCommand = hcCommand
|
||||||
|
}
|
||||||
|
// TODO when history/logging is implemented for healthcheck, we need to change the output streams
|
||||||
|
// so we can capture i/o
|
||||||
|
streams := new(AttachStreams)
|
||||||
|
streams.OutputStream = os.Stdout
|
||||||
|
streams.ErrorStream = os.Stderr
|
||||||
|
streams.InputStream = os.Stdin
|
||||||
|
streams.AttachOutput = true
|
||||||
|
streams.AttachError = true
|
||||||
|
streams.AttachInput = true
|
||||||
|
|
||||||
|
logrus.Debugf("executing health check command %s for %s", strings.Join(newCommand, " "), c.ID())
|
||||||
|
if err := c.Exec(false, false, []string{}, newCommand, "", "", streams, 0); err != nil {
|
||||||
|
return HealthCheckFailure, err
|
||||||
|
}
|
||||||
|
return HealthCheckSuccess, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHealthCheckCanBeRun(c *Container) (HealthCheckStatus, error) {
|
||||||
|
cstate, err := c.State()
|
||||||
|
if err != nil {
|
||||||
|
return HealthCheckInternalError, err
|
||||||
|
}
|
||||||
|
if cstate != ContainerStateRunning {
|
||||||
|
return HealthCheckContainerStopped, errors.Errorf("container %s is not running", c.ID())
|
||||||
|
}
|
||||||
|
if !c.HasHealthCheck() {
|
||||||
|
return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID())
|
||||||
|
}
|
||||||
|
return HealthCheckDefined, nil
|
||||||
|
}
|
|
@ -1151,3 +1151,32 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetConfigBlob returns a schema2image. If the image is not a schema2, then
|
||||||
|
// it will return an error
|
||||||
|
func (i *Image) GetConfigBlob(ctx context.Context) (*manifest.Schema2Image, error) {
|
||||||
|
imageRef, err := i.toImageRef(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := imageRef.ConfigBlob(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to get config blob for %s", i.ID())
|
||||||
|
}
|
||||||
|
blob := manifest.Schema2Image{}
|
||||||
|
if err := json.Unmarshal(b, &blob); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to parse image blob for %s", i.ID())
|
||||||
|
}
|
||||||
|
return &blob, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHealthCheck returns a HealthConfig for an image. This function only works with
|
||||||
|
// schema2 images.
|
||||||
|
func (i *Image) GetHealthCheck(ctx context.Context) (*manifest.Schema2HealthConfig, error) {
|
||||||
|
configBlob, err := i.GetConfigBlob(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return configBlob.ContainerConfig.Healthcheck, nil
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/libpod/pkg/namespaces"
|
"github.com/containers/libpod/pkg/namespaces"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
@ -1469,3 +1470,14 @@ func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithHealthCheck adds the healthcheck to the container config
|
||||||
|
func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return ErrCtrFinalized
|
||||||
|
}
|
||||||
|
ctr.config.HealthCheckConfig = healthCheck
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -332,3 +332,8 @@ func IsImageNotFound(err error) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HealthCheck is a wrapper to same named function in libpod
|
||||||
|
func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) {
|
||||||
|
return r.Runtime.HealthCheck(c.InputArgs[0])
|
||||||
|
}
|
||||||
|
|
|
@ -746,3 +746,8 @@ func IsImageNotFound(err error) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HealthCheck executes a container's healthcheck over a varlink connection
|
||||||
|
func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) {
|
||||||
|
return -1, libpod.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/pkg/namespaces"
|
"github.com/containers/libpod/pkg/namespaces"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
|
@ -86,6 +87,8 @@ type CreateConfig struct {
|
||||||
Env map[string]string //env
|
Env map[string]string //env
|
||||||
ExposedPorts map[nat.Port]struct{}
|
ExposedPorts map[nat.Port]struct{}
|
||||||
GroupAdd []string // group-add
|
GroupAdd []string // group-add
|
||||||
|
HasHealthCheck bool
|
||||||
|
HealthCheck *manifest.Schema2HealthConfig
|
||||||
HostAdd []string //add-host
|
HostAdd []string //add-host
|
||||||
Hostname string //hostname
|
Hostname string //hostname
|
||||||
Image string
|
Image string
|
||||||
|
@ -559,6 +562,10 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
|
||||||
// Always use a cleanup process to clean up Podman after termination
|
// Always use a cleanup process to clean up Podman after termination
|
||||||
options = append(options, libpod.WithExitCommand(c.createExitCommand()))
|
options = append(options, libpod.WithExitCommand(c.createExitCommand()))
|
||||||
|
|
||||||
|
if c.HasHealthCheck {
|
||||||
|
options = append(options, libpod.WithHealthCheck(c.HealthCheck))
|
||||||
|
logrus.Debugf("New container has a health check")
|
||||||
|
}
|
||||||
return options, nil
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,5 @@ var (
|
||||||
ALPINE = "docker.io/library/alpine:latest"
|
ALPINE = "docker.io/library/alpine:latest"
|
||||||
infra = "k8s.gcr.io/pause:3.1"
|
infra = "k8s.gcr.io/pause:3.1"
|
||||||
BB = "docker.io/library/busybox:latest"
|
BB = "docker.io/library/busybox:latest"
|
||||||
|
healthcheck = "docker.io/libpod/alpine_healthcheck:latest"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package integration
|
||||||
var (
|
var (
|
||||||
STORAGE_OPTIONS = "--storage-driver vfs"
|
STORAGE_OPTIONS = "--storage-driver vfs"
|
||||||
ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs"
|
ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs"
|
||||||
CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels}
|
CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels, healthcheck}
|
||||||
nginx = "quay.io/libpod/alpine_nginx:latest"
|
nginx = "quay.io/libpod/alpine_nginx:latest"
|
||||||
BB_GLIBC = "docker.io/library/busybox:glibc"
|
BB_GLIBC = "docker.io/library/busybox:glibc"
|
||||||
registry = "docker.io/library/registry:2"
|
registry = "docker.io/library/registry:2"
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
// +build !remoteclient
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
. "github.com/containers/libpod/test/utils"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Podman healthcheck run", func() {
|
||||||
|
var (
|
||||||
|
tempdir string
|
||||||
|
err error
|
||||||
|
podmanTest *PodmanTestIntegration
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
podmanTest = PodmanTestCreate(tempdir)
|
||||||
|
podmanTest.RestoreAllArtifacts()
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
podmanTest.Cleanup()
|
||||||
|
f := CurrentGinkgoTestDescription()
|
||||||
|
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
|
||||||
|
GinkgoWriter.Write([]byte(timedResult))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman healthcheck run bogus container", func() {
|
||||||
|
session := podmanTest.Podman([]string{"healthcheck", "run", "foobar"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman healthcheck on valid container", func() {
|
||||||
|
podmanTest.RestoreArtifact(healthcheck)
|
||||||
|
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
|
||||||
|
hc.WaitWithDefaultTimeout()
|
||||||
|
Expect(hc.ExitCode()).To(Equal(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman healthcheck that should fail", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "docker.io/libpod/badhealthcheck:latest"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
|
||||||
|
hc.WaitWithDefaultTimeout()
|
||||||
|
Expect(hc.ExitCode()).To(Equal(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman healthcheck on stopped container", func() {
|
||||||
|
podmanTest.RestoreArtifact(healthcheck)
|
||||||
|
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck, "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
|
||||||
|
hc.WaitWithDefaultTimeout()
|
||||||
|
Expect(hc.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman healthcheck on container without healthcheck", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", ALPINE, "top"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
|
||||||
|
hc.WaitWithDefaultTimeout()
|
||||||
|
Expect(hc.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
})
|
|
@ -112,6 +112,7 @@ The following podman commands do not have a Docker equivalent:
|
||||||
* [`podman container refresh`](/docs/podman-container-refresh.1.md)
|
* [`podman container refresh`](/docs/podman-container-refresh.1.md)
|
||||||
* [`podman container runlabel`](/docs/podman-container-runlabel.1.md)
|
* [`podman container runlabel`](/docs/podman-container-runlabel.1.md)
|
||||||
* [`podman container restore`](/docs/podman-container-restore.1.md)
|
* [`podman container restore`](/docs/podman-container-restore.1.md)
|
||||||
|
* [`podman healthcheck run`](/docs/podman-healthcheck-run.1.md)
|
||||||
* [`podman image exists`](./docs/podman-image-exists.1.md)
|
* [`podman image exists`](./docs/podman-image-exists.1.md)
|
||||||
* [`podman image sign`](./docs/podman-image-sign.1.md)
|
* [`podman image sign`](./docs/podman-image-sign.1.md)
|
||||||
* [`podman image trust`](./docs/podman-image-trust.1.md)
|
* [`podman image trust`](./docs/podman-image-trust.1.md)
|
||||||
|
|
Loading…
Reference in New Issue