commit
94c37d7d47
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -2527,4 +2527,68 @@ invalid kube kind
|
|||
Expect(inspect).Should(Exit(0))
|
||||
Expect(inspect.OutputToString()).To(ContainSubstring(`map[]`))
|
||||
})
|
||||
|
||||
It("podman play kube teardown", func() {
|
||||
pod := getPod()
|
||||
err := generateKubeYaml("pod", pod, kubeYaml)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
|
||||
kube.WaitWithDefaultTimeout()
|
||||
Expect(kube).Should(Exit(0))
|
||||
|
||||
ls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"})
|
||||
ls.WaitWithDefaultTimeout()
|
||||
Expect(ls).Should(Exit(0))
|
||||
Expect(len(ls.OutputToStringArray())).To(Equal(1))
|
||||
|
||||
// teardown
|
||||
teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml})
|
||||
teardown.WaitWithDefaultTimeout()
|
||||
Expect(teardown).Should(Exit(0))
|
||||
|
||||
checkls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"})
|
||||
checkls.WaitWithDefaultTimeout()
|
||||
Expect(checkls).Should(Exit(0))
|
||||
Expect(len(checkls.OutputToStringArray())).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman play kube teardown pod does not exist", func() {
|
||||
// teardown
|
||||
teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml})
|
||||
teardown.WaitWithDefaultTimeout()
|
||||
Expect(teardown).Should(Exit(125))
|
||||
})
|
||||
|
||||
It("podman play kube teardown with volume", func() {
|
||||
|
||||
volName := RandomString(12)
|
||||
volDevice := "tmpfs"
|
||||
volType := "tmpfs"
|
||||
volOpts := "nodev,noexec"
|
||||
|
||||
pvc := getPVC(withPVCName(volName),
|
||||
withPVCAnnotations(util.VolumeDeviceAnnotation, volDevice),
|
||||
withPVCAnnotations(util.VolumeTypeAnnotation, volType),
|
||||
withPVCAnnotations(util.VolumeMountOptsAnnotation, volOpts))
|
||||
err = generateKubeYaml("persistentVolumeClaim", pvc, kubeYaml)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
|
||||
kube.WaitWithDefaultTimeout()
|
||||
Expect(kube).Should(Exit(0))
|
||||
|
||||
exists := podmanTest.Podman([]string{"volume", "exists", volName})
|
||||
exists.WaitWithDefaultTimeout()
|
||||
Expect(exists).To(Exit(0))
|
||||
|
||||
teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml})
|
||||
teardown.WaitWithDefaultTimeout()
|
||||
Expect(teardown).To(Exit(0))
|
||||
|
||||
// volume should not be deleted on teardown
|
||||
exists = podmanTest.Podman([]string{"volume", "exists", volName})
|
||||
exists.WaitWithDefaultTimeout()
|
||||
Expect(exists).To(Exit(0))
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue