Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

teardown play kube #11298

Merged
merged 2 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion cmd/podman/play/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ func init() {
flags.StringVar(&kubeOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
_ = kubeCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault)

downFlagName := "down"
flags.BoolVar(&kubeOptions.Down, downFlagName, false, "Stop pods defined in the YAML file")

if !registry.IsRemote() {
certDirFlagName := "cert-dir"
flags.StringVar(&kubeOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
Expand Down Expand Up @@ -144,12 +147,55 @@ func kube(cmd *cobra.Command, args []string) error {
}
kubeOptions.StaticMACs = append(kubeOptions.StaticMACs, m)
}
if kubeOptions.Down {
return teardown(yamlfile)
}
return playkube(yamlfile)
}

report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions)
func teardown(yamlfile string) error {
baude marked this conversation as resolved.
Show resolved Hide resolved
var (
podStopErrors utils.OutputErrors
podRmErrors utils.OutputErrors
)
options := new(entities.PlayKubeDownOptions)
reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), yamlfile, *options)
if err != nil {
return err
}

// Output stopped pods
fmt.Println("Pods stopped:")
for _, stopped := range reports.StopReport {
if len(stopped.Errs) == 0 {
fmt.Println(stopped.Id)
} else {
podStopErrors = append(podStopErrors, stopped.Errs...)
}
}
// Dump any stop errors
lastStopError := podStopErrors.PrintErrors()
if lastStopError != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", lastStopError)
baude marked this conversation as resolved.
Show resolved Hide resolved
}

// Output rm'd pods
fmt.Println("Pods removed:")
for _, removed := range reports.RmReport {
if removed.Err == nil {
fmt.Println(removed.Id)
} else {
podRmErrors = append(podRmErrors, removed.Err)
}
}
return podRmErrors.PrintErrors()
baude marked this conversation as resolved.
Show resolved Hide resolved
}

func playkube(yamlfile string) error {
report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions)
if err != nil {
return err
}
// Print volumes report
for i, volume := range report.Volumes {
if i == 0 {
Expand Down
16 changes: 15 additions & 1 deletion docs/source/markdown/podman-play-kube.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ podman-play-kube - Create containers, pods or volumes based on Kubernetes YAML

## DESCRIPTION
**podman play kube** will read in a structured file of Kubernetes YAML. It will then recreate the containers, pods or volumes described in the YAML. Containers within a pod are then started and the ID of the new Pod or the name of the new Volume is output. If the yaml file is specified as "-" then `podman play kube` will read the YAML file from stdin.

Using the `--down` command line option, it is also capable of tearing down the pods created by a previous run of `podman play kube`.
Ideally the input file would be one created by Podman (see podman-generate-kube(1)). This would guarantee a smooth import and expected results.

Currently, the supported Kubernetes kinds are:
Expand Down Expand Up @@ -96,6 +96,11 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.

#### **--down**

Tears down the pods that were created by a previous run of `play kube`. The pods are stopped and then
removed. Any volumes created are left intact.

#### **--ip**=*IP address*

Assign a static ip address to the pod. This option can be specified several times when play kube creates more than one pod.
Expand Down Expand Up @@ -146,6 +151,15 @@ Recreate the pod and containers as described in a file `demo.yml` sent to stdin
```
$ cat demo.yml | podman play kube -
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6

```
Teardown the pod and containers as described in a file `demo.yml`
```
$ podman play kube --down demo.yml
Pods stopped:
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
Pods removed:
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
```

Provide `configmap-foo.yml` and `configmap-bar.yml` as sources for environment variables within the containers.
Expand Down
3 changes: 3 additions & 0 deletions hack/swagger-check
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ sub operation_name {
if ($action eq 'df') {
$action = 'dataUsage';
}
elsif ($action eq "delete" && $endpoint eq "/libpod/play/kube") {
$action = "KubeDown"
}
# Grrrrrr, this one is annoying: some operations get an extra 'All'
elsif ($action =~ /^(delete|get|stats)$/ && $endpoint !~ /\{/) {
$action .= "All";
Expand Down
44 changes: 41 additions & 3 deletions pkg/api/handlers/libpod/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/containers/podman/v3/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

func PlayKube(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -66,9 +67,15 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
return
}
defer os.Remove(tmpfile.Name())
defer func() {
if err := os.Remove(tmpfile.Name()); err != nil {
logrus.Warn(err)
}
}()
if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
tmpfile.Close()
if err := tmpfile.Close(); err != nil {
logrus.Warn(err)
}
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
return
}
Expand Down Expand Up @@ -105,12 +112,43 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
if _, found := r.URL.Query()["start"]; found {
options.Start = types.NewOptionalBool(query.Start)
}

report, err := containerEngine.PlayKube(r.Context(), tmpfile.Name(), options)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file"))
return
}
utils.WriteResponse(w, http.StatusOK, report)
}

func PlayKubeDown(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
return
}
defer func() {
if err := os.Remove(tmpfile.Name()); err != nil {
logrus.Warn(err)
}
}()
if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
if err := tmpfile.Close(); err != nil {
logrus.Warn(err)
}
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
return
}
if err := tmpfile.Close(); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file"))
return
}
containerEngine := abi.ContainerEngine{Libpod: runtime}
options := new(entities.PlayKubeDownOptions)
report, err := containerEngine.PlayKubeDown(r.Context(), tmpfile.Name(), *options)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error tearing down YAML file"))
return
}
utils.WriteResponse(w, http.StatusOK, report)
}
15 changes: 15 additions & 0 deletions pkg/api/server/register_play.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,20 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost)
// swagger:operation DELETE /libpod/play/kube libpod PlayKubeDownLibpod
// ---
// tags:
// - containers
// - pods
// summary: Remove pods from play kube
// description: Tears down pods defined in a YAML file
// produces:
// - application/json
// responses:
// 200:
// $ref: "#/responses/DocsLibpodPlayKubeResponse"
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKubeDown)).Methods(http.MethodDelete)
return nil
}
29 changes: 29 additions & 0 deletions pkg/bindings/play/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"os"
"strconv"

"github.com/sirupsen/logrus"

"github.com/containers/podman/v3/pkg/auth"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/domain/entities"
Expand Down Expand Up @@ -54,3 +56,30 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla

return &report, nil
}

func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error) {
var report entities.PlayKubeReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}

f, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Warn(err)
}
}()
response, err := conn.DoRequest(f, http.MethodDelete, "/play/kube", nil, nil)
if err != nil {
return nil, err
}
if err := response.Process(&report); err != nil {
return nil, err
}

return &report, nil
}
4 changes: 3 additions & 1 deletion pkg/bindings/play/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package play

import "net"
import (
"net"
)

//go:generate go run ../generator/generator.go KubeOptions
// KubeOptions are optional options for replaying kube YAML files
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/entities/engine_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type ContainerEngine interface {
NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error)
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
PlayKubeDown(ctx context.Context, path string, opts PlayKubeDownOptions) (*PlayKubeReport, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrID string) (*BoolReport, error)
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
Expand Down
13 changes: 13 additions & 0 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type PlayKubeOptions struct {
Build bool
// CertDir - to a directory containing TLS certifications and keys.
CertDir string
// Down indicates whether to bring contents of a yaml file "down"
// as in stop
Down bool
// Username for authenticating against the registry.
Username string
// Password for authenticating against the registry.
Expand Down Expand Up @@ -67,4 +70,14 @@ type PlayKubeReport struct {
Pods []PlayKubePod
// Volumes - volumes created by play kube.
Volumes []PlayKubeVolume
PlayKubeTeardown
}

// PlayKubeDownOptions are options for tearing down pods
type PlayKubeDownOptions struct{}

// PlayKubeDownReport contains the results of tearing down play kube
type PlayKubeTeardown struct {
StopReport []*PodStopReport
RmReport []*PodRmReport
}
70 changes: 70 additions & 0 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,3 +586,73 @@ func getBuildFile(imageName string, cwd string) (string, error) {
}
return "", err
}

func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) {
var (
podNames []string
)
reports := new(entities.PlayKubeReport)

// read yaml document
content, err := ioutil.ReadFile(path)
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

// split yaml document
documentList, err := splitMultiDocYAML(content)
if err != nil {
return nil, err
}

// sort kube kinds
documentList, err = sortKubeKinds(documentList)
if err != nil {
return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
}

for _, document := range documentList {
kind, err := getKubeKind(document)
if err != nil {
return nil, errors.Wrapf(err, "unable to read %q as kube YAML", path)
}

switch kind {
case "Pod":
var podYAML v1.Pod
if err := yaml.Unmarshal(document, &podYAML); err != nil {
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Pod", path)
}
podNames = append(podNames, podYAML.ObjectMeta.Name)
case "Deployment":
var deploymentYAML v1apps.Deployment

if err := yaml.Unmarshal(document, &deploymentYAML); err != nil {
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
}
var numReplicas int32 = 1
deploymentName := deploymentYAML.ObjectMeta.Name
if deploymentYAML.Spec.Replicas != nil {
numReplicas = *deploymentYAML.Spec.Replicas
}
for i := 0; i < int(numReplicas); i++ {
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
podNames = append(podNames, podName)
}
default:
continue
}
}

// Add the reports
reports.StopReport, err = ic.PodStop(ctx, podNames, entities.PodStopOptions{})
if err != nil {
return nil, err
}

reports.RmReport, err = ic.PodRm(ctx, podNames, entities.PodRmOptions{})
if err != nil {
return nil, err
}
return reports, nil
}
4 changes: 4 additions & 0 deletions pkg/domain/infra/tunnel/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entit
}
return play.Kube(ic.ClientCtx, path, options)
}

func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) {
return play.KubeDown(ic.ClientCtx, path)
}
Loading