podman kube play --replace should force removal of pods and containers

Fixes: https://github.com/containers/podman/issues/20025

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2023-10-27 07:23:53 -04:00
parent 3ce62d3cc4
commit 91df369ae6
7 changed files with 100 additions and 20 deletions

View File

@ -25,6 +25,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
Network []string `schema:"network"`
NoHosts bool `schema:"noHosts"`
NoTrunc bool `schema:"noTrunc"`
Replace bool `schema:"replace"`
PublishPorts []string `schema:"publishPorts"`
ServiceContainer bool `schema:"serviceContainer"`
Start bool `schema:"start"`
@ -97,6 +98,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
Password: password,
PublishPorts: query.PublishPorts,
Quiet: true,
Replace: query.Replace,
ServiceContainer: query.ServiceContainer,
StaticIPs: staticIPs,
StaticMACs: staticMACs,

View File

@ -17,31 +17,53 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
// parameters:
// - in: query
// name: logDriver
// type: string
// description: Logging driver for the containers in the pod.
// - in: query
// name: logOptions
// type: array
// description: logging driver options
// items:
// type: string
// - in: query
// name: network
// type: array
// description: USe the network mode or specify an array of networks.
// items:
// type: string
// - in: query
// name: tlsVerify
// name: noHosts
// type: boolean
// default: true
// description: Require HTTPS and verify signatures when contacting registries.
// default: false
// description: do not setup /etc/hosts file in container
// - in: query
// name: logDriver
// type: string
// description: Logging driver for the containers in the pod.
// - in: query
// name: start
// name: noTrunc
// type: boolean
// default: true
// description: Start the pod after creating it.
// default: false
// description: use annotations that are not truncated to the Kubernetes maximum length of 63 characters
// - in: query
// name: publishPorts
// type: array
// description: publish a container's port, or a range of ports, to the host
// items:
// type: string
// - in: query
// name: replace
// type: boolean
// default: false
// description: replace existing pods and containers
// - in: query
// name: serviceContainer
// type: boolean
// default: false
// description: Starts a service container before all pods.
// - in: query
// name: start
// type: boolean
// default: true
// description: Start the pod after creating it.
// - in: query
// name: staticIPs
// type: array
// description: Static IPs used for the pods.
@ -54,19 +76,19 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// items:
// type: string
// - in: query
// name: wait
// name: tlsVerify
// type: boolean
// default: false
// description: Clean up all objects created when a SIGTERM is received or pods exit.
// - in: query
// name: noTrunc
// type: boolean
// default: false
// description: use annotations that are not truncated to the Kubernetes maximum length of 63 characters
// default: true
// description: Require HTTPS and verify signatures when contacting registries.
// - in: query
// name: userns
// type: string
// description: Set the user namespace mode for the pods.
// - in: query
// name: wait
// type: boolean
// default: false
// description: Clean up all objects created when a SIGTERM is received or pods exit.
// - in: body
// name: request
// description: Kubernetes YAML file.

View File

@ -42,6 +42,8 @@ type PlayOptions struct {
LogDriver *string
// LogOptions for the container. For example: journald
LogOptions *[]string
// Replace - replace existing pods and containers
Replace *bool
// Start - don't start the pod if false
Start *bool
// NoTrunc - use annotations that were not truncated to the

View File

@ -258,6 +258,21 @@ func (o *PlayOptions) GetLogOptions() []string {
return *o.LogOptions
}
// WithReplace set field Replace to given value
func (o *PlayOptions) WithReplace(value bool) *PlayOptions {
o.Replace = &value
return o
}
// GetReplace returns value of field Replace
func (o *PlayOptions) GetReplace() bool {
if o.Replace == nil {
var z bool
return z
}
return *o.Replace
}
// WithStart set field Start to given value
func (o *PlayOptions) WithStart(value bool) *PlayOptions {
o.Start = &value

View File

@ -109,6 +109,12 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri
// via the `sdNotifyAnnotation` annotation in the K8s YAML.
opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore))
if options.Replace {
if _, err := ic.ContainerRm(ctx, []string{spec.Name}, entities.RmOptions{Force: true, Ignore: true}); err != nil {
return nil, err
}
}
// Create a new libpod container based on the spec.
ctr, err := ic.Libpod.NewContainer(ctx, runtimeSpec, spec, false, opts...)
if err != nil {
@ -813,6 +819,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return nil, nil, err
}
opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore))
if options.Replace {
if _, err := ic.ContainerRm(ctx, []string{spec.Name}, entities.RmOptions{Force: true, Ignore: true}); err != nil {
return nil, nil, err
}
}
ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...)
if err != nil {
return nil, nil, err
@ -913,6 +925,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
opts = append(opts, libpod.WithSdNotifySocket(proxy.SocketPath()))
}
if options.Replace {
if _, err := ic.ContainerRm(ctx, []string{spec.Name}, entities.RmOptions{Force: true, Ignore: true}); err != nil {
return nil, nil, err
}
}
ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...)
if err != nil {
return nil, nil, err
@ -1516,7 +1534,7 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, opt
return nil, err
}
reports.RmReport, err = ic.PodRm(ctx, podNames, entities.PodRmOptions{Ignore: true})
reports.RmReport, err = ic.PodRm(ctx, podNames, entities.PodRmOptions{Ignore: true, Force: true})
if err != nil {
return nil, err
}

View File

@ -58,7 +58,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en
options := new(kube.PlayOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Networks).WithSeccompProfileRoot(opts.SeccompProfileRoot)
options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs).WithWait(opts.Wait).WithServiceContainer(opts.ServiceContainer)
options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs).WithWait(opts.Wait).WithServiceContainer(opts.ServiceContainer).WithReplace(opts.Replace)
if len(opts.LogOptions) > 0 {
options.WithLogOptions(opts.LogOptions)
}

View File

@ -411,6 +411,27 @@ _EOF
run_podman rmi -f userimage:latest
}
# Ocassionaly a remnant storage container is left behind which causes
# podman play kube --replace to fail. This tests created a conflicting
# storage container name using buildah to make sure --replace, still
# functions proplery by removing the storage container.
@test "podman kube play --replace external storage" {
TESTDIR=$PODMAN_TMPDIR/testdir
mkdir -p $TESTDIR
echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman play kube $PODMAN_TMPDIR/test.yaml
# Force removal of container
run_podman rm --force -t0 test_pod-test
# Create external container using buildah with same name
buildah from --name test_pod-test $IMAGE
# --replace deletes the buildah container and replace it with new one
run_podman play kube --replace $PODMAN_TMPDIR/test.yaml
run_podman stop -a -t 0
run_podman pod rm -t 0 -f test_pod
run_podman rmi -f userimage:latest
}
@test "podman kube --annotation" {
TESTDIR=$PODMAN_TMPDIR/testdir
RANDOMSTRING=$(random_string 15)