summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/completion.go76
-rw-r--r--cmd/podman/common/completion_test.go13
-rw-r--r--cmd/podman/containers/logs.go4
-rw-r--r--cmd/podman/machine/init.go3
-rw-r--r--cmd/podman/play/kube.go48
-rw-r--r--cmd/podman/pods/ps.go2
-rw-r--r--cmd/podman/volumes/import.go97
7 files changed, 207 insertions, 36 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 9a4524b46..3966606e3 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -985,40 +985,14 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
f = f.Elem()
}
- // // the only supported type is struct
+ // the only supported type is struct
if f.Kind() != reflect.Struct {
return nil, cobra.ShellCompDirectiveNoFileComp
}
// last field get all names to suggest
if i == len(fields)-1 {
- suggestions := []string{}
- for j := 0; j < f.NumField(); j++ {
- fname := f.Type().Field(j).Name
- suffix := "}}"
- kind := f.Type().Field(j).Type.Kind()
- if kind == reflect.Ptr {
- // make sure to read the actual type when it is a pointer
- kind = f.Type().Field(j).Type.Elem().Kind()
- }
- // when we have a nested struct do not append braces instead append a dot
- if kind == reflect.Struct {
- suffix = "."
- }
- if strings.HasPrefix(fname, fields[i]) {
- // add field name with closing braces
- suggestions = append(suggestions, fname+suffix)
- }
- }
-
- for j := 0; j < f.NumMethod(); j++ {
- fname := f.Type().Method(j).Name
- if strings.HasPrefix(fname, fields[i]) {
- // add method name with closing braces
- suggestions = append(suggestions, fname+"}}")
- }
- }
-
+ suggestions := getStructFields(f, fields[i])
// add the current toComplete value in front so that the shell can complete this correctly
toCompArr := strings.Split(toComplete, ".")
toCompArr[len(toCompArr)-1] = ""
@@ -1032,6 +1006,52 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
}
}
+// getStructFields reads all struct field names and method names and returns them.
+func getStructFields(f reflect.Value, prefix string) []string {
+ suggestions := []string{}
+ // follow the pointer first
+ if f.Kind() == reflect.Ptr {
+ f = f.Elem()
+ }
+ // we only support structs
+ if f.Kind() != reflect.Struct {
+ return nil
+ }
+ // loop over all field names
+ for j := 0; j < f.NumField(); j++ {
+ field := f.Type().Field(j)
+ fname := field.Name
+ suffix := "}}"
+ kind := field.Type.Kind()
+ if kind == reflect.Ptr {
+ // make sure to read the actual type when it is a pointer
+ kind = field.Type.Elem().Kind()
+ }
+ // when we have a nested struct do not append braces instead append a dot
+ if kind == reflect.Struct {
+ suffix = "."
+ }
+ if strings.HasPrefix(fname, prefix) {
+ // add field name with suffix
+ suggestions = append(suggestions, fname+suffix)
+ }
+ // if field is anonymous add the child fields as well
+ if field.Anonymous {
+ suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...)
+ }
+ }
+
+ for j := 0; j < f.NumMethod(); j++ {
+ fname := f.Type().Method(j).Name
+ if strings.HasPrefix(fname, prefix) {
+ // add method name with closing braces
+ suggestions = append(suggestions, fname+"}}")
+ }
+ }
+
+ return suggestions
+}
+
// AutocompleteEventFilter - Autocomplete event filter flag options.
// -> "container=", "event=", "image=", "pod=", "volume=", "type="
func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go
index 5bd627b85..84b3c1132 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -17,6 +17,10 @@ type Car struct {
Extras map[string]string
}
+type Anonymous struct {
+ Hello string
+}
+
func (c Car) Type() string {
return ""
}
@@ -30,7 +34,10 @@ func TestAutocompleteFormat(t *testing.T) {
Name string
Age int
Car *Car
- }{}
+ *Anonymous
+ }{
+ Anonymous: &Anonymous{},
+ }
testStruct.Car = &Car{}
testStruct.Car.Extras = map[string]string{"test": "1"}
@@ -73,12 +80,12 @@ func TestAutocompleteFormat(t *testing.T) {
{
"fist level struct field name",
"{{.",
- []string{"{{.Name}}", "{{.Age}}", "{{.Car."},
+ []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"},
},
{
"fist level struct field name",
"{{ .",
- []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."},
+ []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"},
},
{
"fist level struct field name",
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 00a8d4b52..1548c6c24 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -120,7 +120,7 @@ func logsFlags(cmd *cobra.Command) {
func logs(_ *cobra.Command, args []string) error {
if logsOptions.SinceRaw != "" {
// parse time, error out if something is wrong
- since, err := util.ParseInputTime(logsOptions.SinceRaw)
+ since, err := util.ParseInputTime(logsOptions.SinceRaw, true)
if err != nil {
return errors.Wrapf(err, "error parsing --since %q", logsOptions.SinceRaw)
}
@@ -128,7 +128,7 @@ func logs(_ *cobra.Command, args []string) error {
}
if logsOptions.UntilRaw != "" {
// parse time, error out if something is wrong
- until, err := util.ParseInputTime(logsOptions.UntilRaw)
+ until, err := util.ParseInputTime(logsOptions.UntilRaw, false)
if err != nil {
return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw)
}
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index f4133dbde..ac0d06a07 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -34,6 +34,7 @@ func init() {
Parent: machineCmd,
})
flags := initCmd.Flags()
+ cfg := registry.PodmanConfig()
cpusFlagName := "cpus"
flags.Uint64Var(
@@ -61,7 +62,7 @@ func init() {
_ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
ImagePathFlagName := "image-path"
- flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, "", "Path to qcow image")
+ flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Engine.MachineImage, "Path to qcow image")
_ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault)
IgnitionPathFlagName := "ignition-path"
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index 2eebd9f86..9308371d2 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -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 {
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 14e3e2ea9..60aadf224 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -57,7 +57,7 @@ func init() {
formatFlagName := "format"
flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template")
- _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{}))
+ _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{ListPodsReport: &entities.ListPodsReport{}}))
flags.Bool("noheading", false, "Do not print headers")
flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod")
diff --git a/cmd/podman/volumes/import.go b/cmd/podman/volumes/import.go
new file mode 100644
index 000000000..441bd0fe4
--- /dev/null
+++ b/cmd/podman/volumes/import.go
@@ -0,0 +1,97 @@
+package volumes
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/podman/v3/cmd/podman/common"
+ "github.com/containers/podman/v3/cmd/podman/inspect"
+ "github.com/containers/podman/v3/cmd/podman/parse"
+ "github.com/containers/podman/v3/cmd/podman/registry"
+ "github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/utils"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ importDescription = `Imports contents into a podman volume from specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).`
+ importCommand = &cobra.Command{
+ Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
+ Use: "import VOLUME [SOURCE]",
+ Short: "Import a tarball contents into a podman volume",
+ Long: importDescription,
+ RunE: importVol,
+ Args: cobra.ExactArgs(2),
+ ValidArgsFunction: common.AutocompleteVolumes,
+ Example: `podman volume import my_vol /home/user/import.tar
+ cat ctr.tar | podman import volume my_vol -`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: importCommand,
+ Parent: volumeCmd,
+ })
+}
+
+func importVol(cmd *cobra.Command, args []string) error {
+ var inspectOpts entities.InspectOptions
+ var tarFile *os.File
+ containerEngine := registry.ContainerEngine()
+ ctx := registry.Context()
+ // create a slice of volumes since inspect expects slice as arg
+ volumes := []string{args[0]}
+ tarPath := args[1]
+
+ if tarPath != "-" {
+ err := parse.ValidateFileName(tarPath)
+ if err != nil {
+ return err
+ }
+
+ // open tar file
+ tarFile, err = os.Open(tarPath)
+ if err != nil {
+ return err
+ }
+ } else {
+ tarFile = os.Stdin
+ }
+
+ inspectOpts.Type = inspect.VolumeType
+ volumeData, _, err := containerEngine.VolumeInspect(ctx, volumes, inspectOpts)
+ if err != nil {
+ return err
+ }
+ if len(volumeData) < 1 {
+ return errors.New("no volume data found")
+ }
+ mountPoint := volumeData[0].VolumeConfigResponse.Mountpoint
+ driver := volumeData[0].VolumeConfigResponse.Driver
+ volumeOptions := volumeData[0].VolumeConfigResponse.Options
+ volumeMountStatus, err := containerEngine.VolumeMounted(ctx, args[0])
+ if err != nil {
+ return err
+ }
+ if mountPoint == "" {
+ return errors.New("volume is not mounted anywhere on host")
+ }
+ // Check if volume is using external plugin and export only if volume is mounted
+ if driver != "" && driver != "local" {
+ if !volumeMountStatus.Value {
+ return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint)
+ }
+ }
+ // Check if volume is using `local` driver and has mount options type other than tmpfs
+ if driver == "local" {
+ if mountOptionType, ok := volumeOptions["type"]; ok {
+ if mountOptionType != "tmpfs" && !volumeMountStatus.Value {
+ return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint)
+ }
+ }
+ }
+ // dont care if volume is mounted or not we are gonna import everything to mountPoint
+ return utils.UntarToFileSystem(mountPoint, tarFile, nil)
+}