diff --git a/common/docs/containers.conf.5.md b/common/docs/containers.conf.5.md index f7c7e0ab33..6aac497838 100644 --- a/common/docs/containers.conf.5.md +++ b/common/docs/containers.conf.5.md @@ -549,6 +549,15 @@ pod consumes one lock. The default number available is 2048. If this is changed, a lock renumbering must be performed, using the `podman system renumber` command. +**pod_exit_policy**="continue" + +Set the exit policy of the pod when the last container exits. Supported policies are: + +| Exit Policy | Description | +| ------------------ | --------------------------------------------------------------------------- | +| *continue* | The pod continues running when the last container exits. Used by default. | +| *stop* | The pod is stopped when the last container exits. Used in `play kube`. | + **pull_policy**="always"|"missing"|"never" Pull image before running or creating a container. The default is **missing**. diff --git a/common/pkg/config/config.go b/common/pkg/config/config.go index d362495e37..a86eca88ef 100644 --- a/common/pkg/config/config.go +++ b/common/pkg/config/config.go @@ -349,6 +349,9 @@ type EngineConfig struct { // OCIRuntimes are the set of configured OCI runtimes (default is runc). OCIRuntimes map[string][]string `toml:"runtimes,omitempty"` + // PodExitPolicy determines the behaviour when the last container of a pod exits. + PodExitPolicy PodExitPolicy `toml:"pod_exit_policy,omitempty"` + // PullPolicy determines whether to pull image before creating or running a container // default is "missing" PullPolicy string `toml:"pull_policy,omitempty"` diff --git a/common/pkg/config/config_test.go b/common/pkg/config/config_test.go index 44d76dc176..451544797b 100644 --- a/common/pkg/config/config_test.go +++ b/common/pkg/config/config_test.go @@ -362,6 +362,7 @@ image_copy_tmp_dir="storage"` } gomega.Expect(config.Engine.EventsLogFilePath).To(gomega.BeEquivalentTo(config.Engine.TmpDir + "/events/events.log")) gomega.Expect(uint64(config.Engine.EventsLogFileMaxSize)).To(gomega.Equal(DefaultEventsLogSizeMax)) + gomega.Expect(config.Engine.PodExitPolicy).To(gomega.Equal(PodExitPolicyContinue)) }) It("should success with valid user file path", func() { @@ -402,6 +403,7 @@ image_copy_tmp_dir="storage"` gomega.Expect(err).To(gomega.BeNil()) gomega.Expect(path).To(gomega.BeEquivalentTo("/tmp/foobar")) gomega.Expect(uint64(config.Engine.EventsLogFileMaxSize)).To(gomega.Equal(uint64(500))) + gomega.Expect(config.Engine.PodExitPolicy).To(gomega.BeEquivalentTo(PodExitPolicyStop)) }) It("should fail with invalid value", func() { diff --git a/common/pkg/config/containers.conf b/common/pkg/config/containers.conf index 2b250753ea..a4e755a665 100644 --- a/common/pkg/config/containers.conf +++ b/common/pkg/config/containers.conf @@ -506,6 +506,9 @@ default_sysctls = [ # #num_locks = 2048 +# Set the exit policy of the pod when the last container exits. +#pod_exit_policy = "continue" + # Whether to pull new image before running a container # #pull_policy = "missing" diff --git a/common/pkg/config/default.go b/common/pkg/config/default.go index 62b348d6e2..8979a406bc 100644 --- a/common/pkg/config/default.go +++ b/common/pkg/config/default.go @@ -388,6 +388,8 @@ func defaultConfigFromMemory() (*EngineConfig, error) { c.MachineEnabled = false c.ChownCopiedFiles = true + c.PodExitPolicy = defaultPodExitPolicy + return c, nil } diff --git a/common/pkg/config/pod_exit_policy.go b/common/pkg/config/pod_exit_policy.go new file mode 100644 index 0000000000..f0f983077d --- /dev/null +++ b/common/pkg/config/pod_exit_policy.go @@ -0,0 +1,36 @@ +package config + +import "fmt" + +// PodExitPolicies includes the supported pod exit policies. +var PodExitPolicies = []string{string(PodExitPolicyContinue), string(PodExitPolicyStop)} + +// PodExitPolicy determines a pod's exit and stop behaviour. +type PodExitPolicy string + +const ( + // PodExitPolicyContinue instructs the pod to continue running when the + // last container has exited. + PodExitPolicyContinue PodExitPolicy = "continue" + // PodExitPolicyStop instructs the pod to stop when the last container + // has exited. + PodExitPolicyStop = "stop" + // PodExitPolicyUnsupported implies an internal error. + // Negative for backwards compat. + PodExitPolicyUnsupported = "invalid" + + defaultPodExitPolicy = PodExitPolicyContinue +) + +// ParsePodExitPolicy parses the specified policy and returns an error if it is +// invalid. +func ParsePodExitPolicy(policy string) (PodExitPolicy, error) { + switch policy { + case "", string(PodExitPolicyContinue): + return PodExitPolicyContinue, nil + case string(PodExitPolicyStop): + return PodExitPolicyStop, nil + default: + return PodExitPolicyUnsupported, fmt.Errorf("invalid pod exit policy: %q", policy) + } +} diff --git a/common/pkg/config/pod_exit_policy_test.go b/common/pkg/config/pod_exit_policy_test.go new file mode 100644 index 0000000000..cbf8ceaa31 --- /dev/null +++ b/common/pkg/config/pod_exit_policy_test.go @@ -0,0 +1,33 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParsePodExitPolicy(t *testing.T) { + tests := []struct { + input string + expected PodExitPolicy + mustFail bool + }{ + {"", PodExitPolicyContinue, false}, + {"continue", PodExitPolicyContinue, false}, + {"stop", PodExitPolicyStop, false}, + {"-", PodExitPolicyUnsupported, true}, + {" stop", PodExitPolicyUnsupported, true}, + {"continue ", PodExitPolicyUnsupported, true}, + {"invalid", PodExitPolicyUnsupported, true}, + } + + for _, test := range tests { + parsed, err := ParsePodExitPolicy(test.input) + require.Equal(t, test.expected, parsed, "%v", test) + if test.mustFail { + require.Error(t, err, "%v", test) + } else { + require.NoError(t, err, "%v", test) + } + } +} diff --git a/common/pkg/config/testdata/containers_override.conf b/common/pkg/config/testdata/containers_override.conf index cb30e77405..b04eef5a23 100644 --- a/common/pkg/config/testdata/containers_override.conf +++ b/common/pkg/config/testdata/containers_override.conf @@ -11,6 +11,7 @@ image_default_format="v2s2" image_copy_tmp_dir="/tmp/foobar" events_logfile_path = "/tmp/events.log" events_logfile_max_size="500" +pod_exit_policy="stop" [secrets] driver = "pass"