Skip to content

Commit

Permalink
kube play: update the handling of PersistentVolumeClaim
Browse files Browse the repository at this point in the history
Up - do not fail if volume already exists, use the existing one
Down - allow the user to remove the volume by passing --force
Add tests
Update the documentation

Signed-off-by: Ygal Blum <[email protected]>
  • Loading branch information
ygalblum committed Nov 8, 2022
1 parent afc8415 commit 1c8196a
Show file tree
Hide file tree
Showing 15 changed files with 248 additions and 31 deletions.
20 changes: 18 additions & 2 deletions cmd/podman/kube/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ package kube
import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra"
)

type downKubeOptions struct {
Force bool
}

var (
downDescription = `Reads in a structured file of Kubernetes YAML.
Removes pods that have been based on the Kubernetes kind described in the YAML.`

downCmd = &cobra.Command{
Use: "down KUBEFILE|-",
Use: "down [options] KUBEFILE|-",
Short: "Remove pods based on Kubernetes YAML.",
Long: downDescription,
RunE: down,
Expand All @@ -22,19 +28,29 @@ var (
cat nginx.yml | podman kube down -
podman kube down https://example.com/nginx.yml`,
}

downOptions = downKubeOptions{}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: downCmd,
Parent: kubeCmd,
})
downFlags(downCmd)
}

func downFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.SetNormalizeFunc(utils.AliasFlags)

flags.BoolVar(&downOptions.Force, "force", false, "remove volumes")
}

func down(cmd *cobra.Command, args []string) error {
reader, err := readerFromArg(args[0])
if err != nil {
return err
}
return teardown(reader)
return teardown(reader, entities.PlayKubeDownOptions{Force: downOptions.Force})
}
32 changes: 26 additions & 6 deletions cmd/podman/kube/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func playFlags(cmd *cobra.Command) {
flags.BoolVarP(&playOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.BoolVar(&playOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
flags.BoolVar(&playOptions.StartCLI, "start", true, "Start the pod after creating it")
flags.BoolVar(&playOptions.Force, "force", false, "Remove volumes as part of --down")

authfileFlagName := "authfile"
flags.StringVar(&playOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
Expand Down Expand Up @@ -242,17 +243,21 @@ func play(cmd *cobra.Command, args []string) error {
playOptions.StaticMACs = append(playOptions.StaticMACs, m)
}

if playOptions.Force && !playOptions.Down {
return errors.New("--force may be specified only with --down")
}

reader, err := readerFromArg(args[0])
if err != nil {
return err
}

if playOptions.Down {
return teardown(reader)
return teardown(reader, entities.PlayKubeDownOptions{Force: playOptions.Force})
}

if playOptions.Replace {
if err := teardown(reader); err != nil && !errorhandling.Contains(err, define.ErrNoSuchPod) {
if err := teardown(reader, entities.PlayKubeDownOptions{Force: playOptions.Force}); err != nil && !errorhandling.Contains(err, define.ErrNoSuchPod) {
return err
}
if _, err := reader.Seek(0, 0); err != nil {
Expand Down Expand Up @@ -302,13 +307,13 @@ func readerFromArg(fileName string) (*bytes.Reader, error) {
return bytes.NewReader(data), nil
}

func teardown(body io.Reader) error {
func teardown(body io.Reader, options entities.PlayKubeDownOptions) error {
var (
podStopErrors utils.OutputErrors
podRmErrors utils.OutputErrors
volRmErrors utils.OutputErrors
)
options := new(entities.PlayKubeDownOptions)
reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), body, *options)
reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), body, options)
if err != nil {
return err
}
Expand Down Expand Up @@ -338,7 +343,22 @@ func teardown(body io.Reader) error {
}
}

return podRmErrors.PrintErrors()
lastPodRmError := podRmErrors.PrintErrors()
if lastPodRmError != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", lastPodRmError)
}

// Output rm'd volumes
fmt.Println("Volumes removed:")
for _, removed := range reports.VolumeRmReport {
if removed.Err == nil {
fmt.Println(removed.Id)
} else {
volRmErrors = append(volRmErrors, removed.Err)
}
}

return volRmErrors.PrintErrors()
}

func kubeplay(body io.Reader) error {
Expand Down
17 changes: 12 additions & 5 deletions docs/source/markdown/podman-kube-down.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@
podman-kube-down - Remove containers and pods based on Kubernetes YAML

## SYNOPSIS
**podman kube down** *file.yml|-|https://website.io/file.yml*
**podman kube down** [*options*] *file.yml|-|https://website.io/file.yml*

## DESCRIPTION
**podman kube down** reads a specified Kubernetes YAML file, tearing down pods that were created by the `podman kube play` command via the same Kubernetes YAML
file. Any volumes that were created by the previous `podman kube play` command remain intact. If the YAML file is specified as `-`, `podman kube down` reads the
YAML from stdin. The input can also be a URL that points to a YAML file such as https://podman.io/demo.yml. `podman kube down` will then teardown the pods and
containers created by `podman kube play` via the same Kubernetes YAML from the URL. However, `podman kube down` will not work with a URL if the YAML file the URL
points to has been changed or altered since the creation of the pods and containers using `podman kube play`.
file. Any volumes that were created by the previous `podman kube play` command remain intact unless the `--force` options is used. If the YAML file is
specified as `-`, `podman kube down` reads the YAML from stdin. The input can also be a URL that points to a YAML file such as https://podman.io/demo.yml.
`podman kube down` will then teardown the pods and containers created by `podman kube play` via the same Kubernetes YAML from the URL. However,
`podman kube down` will not work with a URL if the YAML file the URL points to has been changed or altered since the creation of the pods and containers using
`podman kube play`.

## OPTIONS

#### **--force**

Tear down the volumes linked to the PersistentVolumeClaims as part --down

## EXAMPLES

Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-kube-play.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ Use *path* as the build context directory for each image. Requires --build optio

@@option creds

#### **--force**

Tear down the volumes linked to the PersistentVolumeClaims as part of --down

#### **--help**, **-h**

Print usage statement
Expand Down Expand Up @@ -185,6 +189,7 @@ Start the pod after creating it, set to false to only create it.
@@option tls-verify

@@option userns.container

## EXAMPLES

Recreate the pod and containers as described in a file called `demo.yml`
Expand Down
15 changes: 13 additions & 2 deletions pkg/api/handlers/libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,20 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {

func KubePlayDown(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Force bool `schema:"force"`
}{
Force: false,
}

if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
return
}

containerEngine := abi.ContainerEngine{Libpod: runtime}
options := new(entities.PlayKubeDownOptions)
report, err := containerEngine.PlayKubeDown(r.Context(), r.Body, *options)
report, err := containerEngine.PlayKubeDown(r.Context(), r.Body, entities.PlayKubeDownOptions{Force: query.Force})
_ = r.Body.Close()
if err != nil {
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("tearing down YAML file: %w", err))
Expand Down
6 changes: 6 additions & 0 deletions pkg/api/server/register_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// - pods
// summary: Remove pods from kube play
// description: Tears down pods defined in a YAML file
// parameters:
// - in: query
// name: force
// type: boolean
// default: false
// description: Remove volumes.
// produces:
// - application/json
// responses:
Expand Down
13 changes: 9 additions & 4 deletions pkg/bindings/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e
return &report, nil
}

func Down(ctx context.Context, path string) (*entities.KubePlayReport, error) {
func Down(ctx context.Context, path string, options DownOptions) (*entities.KubePlayReport, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
Expand All @@ -78,17 +78,22 @@ func Down(ctx context.Context, path string) (*entities.KubePlayReport, error) {
}
}()

return DownWithBody(ctx, f)
return DownWithBody(ctx, f, options)
}

func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport, error) {
func DownWithBody(ctx context.Context, body io.Reader, options DownOptions) (*entities.KubePlayReport, error) {
var report entities.KubePlayReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}

response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", nil, nil)
params, err := options.ToParams()
if err != nil {
return nil, err
}

response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", params, nil)
if err != nil {
return nil, err
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/bindings/kube/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type PlayOptions struct {
Start *bool
// Userns - define the user namespace to use.
Userns *string
// Force - remove volumes on --down
Force *bool
}

// ApplyOptions are optional options for applying kube YAML files to a k8s cluster
Expand All @@ -63,3 +65,11 @@ type ApplyOptions struct {
// Service - creates a service for the container being deployed.
Service *bool
}

// DownOptions are optional options for tearing down kube YAML files to a k8s cluster
//
//go:generate go run ../generator/generator.go DownOptions
type DownOptions struct {
// Force - remove volumes on --down
Force *bool
}
33 changes: 33 additions & 0 deletions pkg/bindings/kube/types_down_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions pkg/bindings/kube/types_play_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/bindings/play/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ func KubeWithBody(ctx context.Context, body io.Reader, options *KubeOptions) (*e
return kube.PlayWithBody(ctx, body, options)
}

func Down(ctx context.Context, path string) (*entities.PlayKubeReport, error) {
return kube.Down(ctx, path)
func Down(ctx context.Context, path string, options kube.DownOptions) (*entities.PlayKubeReport, error) {
return kube.Down(ctx, path, options)
}

func DownWithBody(ctx context.Context, body io.Reader) (*entities.PlayKubeReport, error) {
return kube.DownWithBody(ctx, body)
func DownWithBody(ctx context.Context, body io.Reader, options kube.DownOptions) (*entities.PlayKubeReport, error) {
return kube.DownWithBody(ctx, body, options)
}
12 changes: 9 additions & 3 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type PlayKubeOptions struct {
Userns string
// IsRemote - was the request triggered by running podman-remote
IsRemote bool
// Force - remove volumes on --down
Force bool
}

// PlayKubePod represents a single pod and associated containers created by play kube
Expand Down Expand Up @@ -96,12 +98,16 @@ type PlayKubeReport struct {
type KubePlayReport = PlayKubeReport

// PlayKubeDownOptions are options for tearing down pods
type PlayKubeDownOptions struct{}
type PlayKubeDownOptions struct {
// Force - remove volumes if passed
Force bool
}

// PlayKubeDownReport contains the results of tearing down play kube
type PlayKubeTeardown struct {
StopReport []*PodStopReport
RmReport []*PodRmReport
StopReport []*PodStopReport
RmReport []*PodRmReport
VolumeRmReport []*VolumeRmReport
}

type PlaySecret struct {
Expand Down
Loading

0 comments on commit 1c8196a

Please sign in to comment.