diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/common/completion.go | 76 | ||||
-rw-r--r-- | cmd/podman/common/completion_test.go | 13 | ||||
-rw-r--r-- | cmd/podman/containers/logs.go | 4 | ||||
-rw-r--r-- | cmd/podman/machine/init.go | 3 | ||||
-rw-r--r-- | cmd/podman/play/kube.go | 48 | ||||
-rw-r--r-- | cmd/podman/pods/ps.go | 2 | ||||
-rw-r--r-- | cmd/podman/volumes/import.go | 97 |
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) +} |