Skip to content

Commit

Permalink
Merge pull request containers#11298 from baude/kubeupdown
Browse files Browse the repository at this point in the history
teardown play kube
  • Loading branch information
openshift-merge-robot authored Aug 26, 2021
2 parents 70caa63 + e88b62b commit 94c37d7
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 6 deletions.
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 {
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)
}

// 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()
}

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 @@ -56,3 +58,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)
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

0 comments on commit 94c37d7

Please sign in to comment.