From 1e176923b15360559d859000f2fb0c0a3e30f792 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Mon, 16 Aug 2021 09:37:50 -0500 Subject: teardown play kube add the ability for play kube to tear down based on the yaml used to play it. it is indicated by --down in the play kube command. volumes are NOT deleted during the teardown. pods and their containers are stopped and removed. Signed-off-by: Brent Baude --- pkg/api/handlers/libpod/play.go | 44 +++++++++++++++++++-- pkg/api/server/register_play.go | 15 +++++++ pkg/bindings/play/play.go | 29 ++++++++++++++ pkg/bindings/play/types.go | 4 +- pkg/domain/entities/engine_container.go | 1 + pkg/domain/entities/play.go | 13 ++++++ pkg/domain/infra/abi/play.go | 70 +++++++++++++++++++++++++++++++++ pkg/domain/infra/tunnel/play.go | 4 ++ 8 files changed, 176 insertions(+), 4 deletions(-) (limited to 'pkg') diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 90332924c..4f79d5f20 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -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) { @@ -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 } @@ -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) } diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go index c51301aa8..915d0d02e 100644 --- a/pkg/api/server/register_play.go +++ b/pkg/api/server/register_play.go @@ -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 } diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index 4b735c821..a45508fed 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -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" @@ -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 +} diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go index 52a72c7b6..787069169 100644 --- a/pkg/bindings/play/types.go +++ b/pkg/bindings/play/types.go @@ -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 diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5d3c9480e..5acf7211c 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -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) diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 01de73ebe..77329e328 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -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. @@ -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 } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 6224feff5..f22b2dbbb 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -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 +} diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go index e66ff0308..e39751a18 100644 --- a/pkg/domain/infra/tunnel/play.go +++ b/pkg/domain/infra/tunnel/play.go @@ -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) +} -- cgit v1.2.3-54-g00ecf