diff options
22 files changed, 391 insertions, 58 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index d1170710b..dda709ecd 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -765,11 +765,15 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + volumeDesciption := "Bind mount a volume into the container" + if registry.IsRemote() { + volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" + } volumeFlagName := "volume" createFlags.StringArrayVarP( &cf.Volume, volumeFlagName, "v", volumes(), - "Bind mount a volume into the container", + volumeDesciption, ) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) @@ -804,4 +808,10 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { "Configure cgroup v2 (key=value)", ) _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) + + _ = createFlags.MarkHidden("signature-policy") + if registry.IsRemote() { + _ = createFlags.MarkHidden("env-host") + _ = createFlags.MarkHidden("http-proxy") + } } diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index af9278ce1..2da9aaf5e 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -63,11 +63,6 @@ func createFlags(cmd *cobra.Command) { common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) - - _ = flags.MarkHidden("signature-policy") - if registry.IsRemote() { - _ = flags.MarkHidden("http-proxy") - } } func init() { diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index db7180ca9..1a9fa2f0f 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -76,13 +76,11 @@ func runFlags(cmd *cobra.Command) { detachKeysFlagName := "detach-keys" flags.StringVar(&runOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) - - _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { - _ = flags.MarkHidden("http-proxy") _ = flags.MarkHidden("preserve-fds") } } + func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go new file mode 100644 index 000000000..3c8368c6b --- /dev/null +++ b/cmd/podman/machine/list.go @@ -0,0 +1,143 @@ +// +build amd64,linux arm64,linux amd64,darwin arm64,darwin + +package machine + +import ( + "os" + "sort" + "text/tabwriter" + "text/template" + "time" + + "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/machine" + "github.com/containers/podman/v3/pkg/machine/qemu" + "github.com/docker/go-units" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + lsCmd = &cobra.Command{ + Use: "list [options]", + Aliases: []string{"ls"}, + Short: "List machines", + Long: "List Podman managed virtual machines.", + RunE: list, + Args: validate.NoArgs, + Example: `podman machine list, + podman machine ls`, + ValidArgsFunction: completion.AutocompleteNone, + } + listFlag = listFlagType{} +) + +type listFlagType struct { + format string + noHeading bool +} + +type machineReporter struct { + Name string + Created string + LastUp string + VMType string +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: lsCmd, + Parent: machineCmd, + }) + + flags := lsCmd.Flags() + formatFlagName := "format" + flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n", "Format volume output using Go template") + _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) +} + +func list(cmd *cobra.Command, args []string) error { + var opts machine.ListOptions + // We only have qemu VM's for now + listResponse, err := qemu.List(opts) + if err != nil { + return errors.Wrap(err, "error listing vms") + } + + // Sort by last run + sort.Slice(listResponse, func(i, j int) bool { + return listResponse[i].LastUp.After(listResponse[j].LastUp) + }) + // Bring currently running machines to top + sort.Slice(listResponse, func(i, j int) bool { + return listResponse[i].Running + }) + machineReporter, err := toHumanFormat(listResponse) + if err != nil { + return err + } + + return outputTemplate(cmd, machineReporter) +} + +func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { + headers := report.Headers(machineReporter{}, map[string]string{ + "LastUp": "LAST UP", + "VmType": "VM TYPE", + }) + + row := report.NormalizeFormat(listFlag.format) + format := parse.EnforceRange(row) + + tmpl, err := template.New("list machines").Parse(format) + if err != nil { + return err + } + w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) + defer w.Flush() + + if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + listFlag.noHeading = true + } + + if !listFlag.noHeading { + if err := tmpl.Execute(w, headers); err != nil { + return errors.Wrapf(err, "failed to write report column headers") + } + } + return tmpl.Execute(w, responses) +} + +func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) { + cfg, err := config.ReadCustomConfig() + if err != nil { + return nil, err + } + + humanResponses := make([]*machineReporter, 0, len(vms)) + for _, vm := range vms { + response := new(machineReporter) + if vm.Name == cfg.Engine.ActiveService { + response.Name = vm.Name + "*" + } else { + response.Name = vm.Name + } + if vm.Running { + response.LastUp = "Currently running" + } else { + response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago" + } + response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago" + response.VMType = vm.VMType + + humanResponses = append(humanResponses, response) + } + return humanResponses, nil +} diff --git a/docs/Readme.md b/docs/README.md index e0918cd54..83f5c79a3 100644 --- a/docs/Readme.md +++ b/docs/README.md @@ -52,3 +52,19 @@ likely caused by broken metadata needed to protect clients from cross-site-scrip style attacks. Please [notify a maintainer](https://github.com/containers/podman#communications) so they may investigate how/why the `swagger.yaml` file's CORS-metadata is incorrect, or the file isn't accessible for some other reason. + +## Local Testing + +Assuming that you have the [dependencies](https://podman.io/getting-started/installation#build-and-run-dependencies) +installed, then also install (showing Fedora in the example): + +``` +# dnf install python3-sphinx python3-recommonmark +# pip install sphinx-markdown-tables +``` +After that completes, cd to the `docs` directory in your Podman sandbox and then do `make html`. + +You can then preview the html files in `docs/build/html` with: +``` +python -m http.server 8000 --directory build/html +``` diff --git a/docs/source/machine.rst b/docs/source/machine.rst index be9ef1e95..16c042173 100644 --- a/docs/source/machine.rst +++ b/docs/source/machine.rst @@ -3,6 +3,7 @@ Machine :doc:`init <markdown/podman-machine-init.1>` Initialize a new virtual machine +:doc:`list <markdown/podman-machine-list.1>` List virtual machines :doc:`rm <markdown/podman-machine-rm.1>` Remove a virtual machine :doc:`ssh <markdown/podman-machine-ssh.1>` SSH into a virtual machine :doc:`start <markdown/podman-machine-start.1>` Start a virtual machine diff --git a/docs/source/markdown/links/podman-machine-ls.1 b/docs/source/markdown/links/podman-machine-ls.1 new file mode 100644 index 000000000..f6acb7f74 --- /dev/null +++ b/docs/source/markdown/links/podman-machine-ls.1 @@ -0,0 +1 @@ +.so man1/podman-machine-list.1 diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 9ae4ab207..f56319cf3 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -1029,7 +1029,7 @@ Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the Podman container. Similarly, `-v SOURCE-VOLUME:/CONTAINER-DIR` will mount the volume in the host to the container. If no such named volume exists, Podman will -create one. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> +create one. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> (Note when using the remote client, the volumes will be mounted from the remote server, not necessarly the client machine.) The _options_ is a comma delimited list and can be: diff --git a/docs/source/markdown/podman-machine-list.1.md b/docs/source/markdown/podman-machine-list.1.md new file mode 100644 index 000000000..bd5608258 --- /dev/null +++ b/docs/source/markdown/podman-machine-list.1.md @@ -0,0 +1,50 @@ +% podman-machine-ls(1) + +## NAME +podman\-machine\-list - List virtual machines + +## SYNOPSIS +**podman machine list** [*options*] + +**podman machine ls** [*options*] + +## DESCRIPTION + +List Podman managed virtual machines. + +Podman on macOS requires a virtual machine. This is because containers are Linux - +containers do not run on any other OS because containers' core functionality is +tied to the Linux kernel. + +## OPTIONS + +#### **\-\-format**=*format* + +Format list output using a Go template. + +Valid placeholders for the Go template are listed below: + +| **Placeholder** | **Description** | +| --------------- | ------------------------------- | +| .Name | VM name | +| .Created | Time since VM creation | +| .LastUp | Time since the VM was last run | +| .VMType | VM type | + +#### **\-\-help** + +Print usage statement. + +## EXAMPLES + +``` +$ podman machine list + +$ podman machine ls --format {{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n +``` + +## SEE ALSO +podman-machine(1) + +## HISTORY +March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md index a5d3b78df..693f42fe3 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -14,13 +14,14 @@ podman\-machine - Manage Podman's virtual machine | Command | Man Page | Description | | ------- | ------------------------------------------------------- | --------------------------------- | | init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | -| rm | [podman-machine-rm(1)](podman-machine-rm.1.md)| Remove a virtual machine | -| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | -| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | -| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | +| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | +| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine | +| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | +| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | +| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | ## SEE ALSO -podman(1) +podman(1), podman-machine-init(1), podman-machine-list(1), podman-machine-rm(1), podman-machine-ssh(1), podman-machine-start(1), podman-machine-stop(1) ## HISTORY March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 6d9d5ba28..fcb5a13ec 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -744,7 +744,7 @@ If a container is run within a pod, and the pod has an infra-container, the infr #### **\-\-preserve-fds**=*N* Pass down to the process N additional file descriptors (in addition to 0, 1, 2). -The total FDs will be 3+N. +The total FDs will be 3+N. (This option is not available with the remote Podman client) #### **\-\-privileged**=**true**|**false** @@ -1104,7 +1104,7 @@ Create a bind mount. If you specify _/HOST-DIR_:_/CONTAINER-DIR_, Podman bind mounts _host-dir_ in the host to _CONTAINER-DIR_ in the Podman container. Similarly, _SOURCE-VOLUME_:_/CONTAINER-DIR_ will mount the volume in the host to the container. If no such named volume exists, Podman will -create one. +create one. (Note when using the remote client, the volumes will be mounted from the remote server, not necessarly the client machine.) The _options_ is a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index e37929d27..61ea7a89e 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -23,7 +23,7 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) { filterFuncs := make([]libpod.ContainerFilter, 0, len(*filtersMap)) for k, v := range *filtersMap { - generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) + generatedFunc, err := filters.GeneratePruneContainerFilterFuncs(k, v, runtime) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go index ab9f613af..cae044a89 100644 --- a/pkg/api/handlers/compat/containers_top.go +++ b/pkg/api/handlers/compat/containers_top.go @@ -46,8 +46,16 @@ func TopContainer(w http.ResponseWriter, r *http.Request) { var body = handlers.ContainerTopOKBody{} if len(output) > 0 { body.Titles = strings.Split(output[0], "\t") + for i := range body.Titles { + body.Titles[i] = strings.TrimSpace(body.Titles[i]) + } + for _, line := range output[1:] { - body.Processes = append(body.Processes, strings.Split(line, "\t")) + process := strings.Split(line, "\t") + for i := range process { + process[i] = strings.TrimSpace(process[i]) + } + body.Processes = append(body.Processes, process) } } utils.WriteJSON(w, http.StatusOK, body) diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 21a8e7a8b..fd93c5ac7 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -22,14 +22,6 @@ import ( "golang.org/x/crypto/ssh/agent" ) -var ( - BasePath = &url.URL{ - Scheme: "http", - Host: "d", - Path: "/v" + version.APIVersion[version.Libpod][version.CurrentAPI].String() + "/libpod", - } -) - type APIResponse struct { *http.Response Request *http.Request @@ -318,16 +310,24 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, err error response *http.Response ) - safePathValues := make([]interface{}, len(pathValues)) - // Make sure path values are http url safe + + params := make([]interface{}, len(pathValues)+3) + + // Including the semver suffices breaks older services... so do not include them + v := version.APIVersion[version.Libpod][version.CurrentAPI] + params[0] = v.Major + params[1] = v.Minor + params[2] = v.Patch for i, pv := range pathValues { - safePathValues[i] = url.PathEscape(pv) + // url.URL lacks the semantics for escaping embedded path parameters... so we manually + // escape each one and assume the caller included the correct formatting in "endpoint" + params[i+3] = url.PathEscape(pv) } - // Lets eventually use URL for this which might lead to safer - // usage - safeEndpoint := fmt.Sprintf(endpoint, safePathValues...) - e := BasePath.String() + safeEndpoint - req, err := http.NewRequest(httpMethod, e, httpBody) + + uri := fmt.Sprintf("http://d/v%d.%d.%d/libpod"+endpoint, params...) + logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri) + + req, err := http.NewRequest(httpMethod, uri, httpBody) if err != nil { return nil, err } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index b0ddc7862..cb9e0721b 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -550,21 +550,28 @@ var _ = Describe("Podman containers ", func() { filtersIncorrect := map[string][]string{ "status": {"dummy"}, } - pruneResponse, err := containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) + _, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) + Expect(err).ToNot(BeNil()) + + // List filter params should not work with prune. + filtersIncorrect = map[string][]string{ + "name": {"top"}, + } + _, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).ToNot(BeNil()) // Mismatched filter params no container should be pruned. filtersIncorrect = map[string][]string{ - "name": {"r"}, + "label": {"xyz"}, } - pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) + pruneResponse, err := containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).To(BeNil()) Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(0)) Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) // Valid filter params container should be pruned now. filters := map[string][]string{ - "name": {"top"}, + "until": {"0s"}, } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filters)) Expect(err).To(BeNil()) diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 84cf03764..19d704da1 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -165,16 +165,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo return false }, nil case "until": - until, err := util.ComputeUntilTimestamp(filterValues) - if err != nil { - return nil, err - } - return func(c *libpod.Container) bool { - if !until.IsZero() && c.CreatedTime().After((until)) { - return true - } - return false - }, nil + return prepareUntilFilterFunc(filterValues) case "pod": var pods []*libpod.Pod for _, podNameOrID := range filterValues { @@ -226,3 +217,29 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo } return nil, errors.Errorf("%s is an invalid filter", filter) } + +// GeneratePruneContainerFilterFuncs return ContainerFilter functions based of filter for prune operation +func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { + switch filter { + case "label": + return func(c *libpod.Container) bool { + return util.MatchLabelFilters(filterValues, c.Labels()) + }, nil + case "until": + return prepareUntilFilterFunc(filterValues) + } + return nil, errors.Errorf("%s is an invalid filter", filter) +} + +func prepareUntilFilterFunc(filterValues []string) (func(container *libpod.Container) bool, error) { + until, err := util.ComputeUntilTimestamp(filterValues) + if err != nil { + return nil, err + } + return func(c *libpod.Container) bool { + if !until.IsZero() && c.CreatedTime().After((until)) { + return true + } + return false + }, nil +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 637531ee9..24261e5ed 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -194,7 +194,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) for k, v := range options.Filters { - generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) + generatedFunc, err := dfilters.GeneratePruneContainerFilterFuncs(k, v, ic.Libpod) if err != nil { return nil, err } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 273deca00..554ea7c97 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -5,6 +5,7 @@ import ( "net/url" "os" "path/filepath" + "time" "github.com/containers/storage/pkg/homedir" "github.com/pkg/errors" @@ -44,6 +45,16 @@ type Download struct { VMName string } +type ListOptions struct{} + +type ListResponse struct { + Name string + CreatedAt time.Time + LastUp time.Time + Running bool + VMType string +} + type SSHOptions struct { Execute bool Args []string diff --git a/pkg/machine/fcos_arm64.go b/pkg/machine/fcos_arm64.go index 4d7e4e4db..f5cd5a505 100644 --- a/pkg/machine/fcos_arm64.go +++ b/pkg/machine/fcos_arm64.go @@ -2,9 +2,9 @@ package machine import ( "encoding/json" - "fmt" "io/ioutil" "net/http" + url2 "net/url" "github.com/sirupsen/logrus" ) @@ -14,9 +14,7 @@ const aarchBaseURL = "https://fedorapeople.org/groups/fcos-images/builds/latest/ // Total hack until automation is possible. // We need a proper json file at least to automate func getFCOSDownload() (*fcosDownloadInfo, error) { - meta := Build{} - fmt.Println(aarchBaseURL + "meta.json") resp, err := http.Get(aarchBaseURL + "meta.json") if err != nil { return nil, err @@ -33,8 +31,18 @@ func getFCOSDownload() (*fcosDownloadInfo, error) { if err := json.Unmarshal(body, &meta); err != nil { return nil, err } + pathURL, err := url2.Parse(meta.BuildArtifacts.Qemu.Path) + if err != nil { + return nil, err + } + + baseURL, err := url2.Parse(aarchBaseURL) + if err != nil { + return nil, err + } + pullURL := baseURL.ResolveReference(pathURL) return &fcosDownloadInfo{ - Location: aarchBaseURL + "/" + meta.BuildArtifacts.Qemu.Path, + Location: pullURL.String(), Release: "", Sha256Sum: meta.BuildArtifacts.Qemu.Sha256, }, nil diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 426d46208..b48926524 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "strconv" + "strings" "time" "github.com/containers/podman/v3/pkg/machine" @@ -78,7 +79,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { return nil, err } vm.QMPMonitor = monitor - cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server,nowait"}...) + cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...) // Add network cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22") @@ -91,7 +92,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { // Add serial port for readiness cmd = append(cmd, []string{ "-device", "virtio-serial", - "-chardev", "socket,path=" + virtualSocketPath + ",server,nowait,id=" + vm.Name + "_ready", + "-chardev", "socket,path=" + virtualSocketPath + ",server=on,wait=off,id=" + vm.Name + "_ready", "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...) vm.CmdLine = cmd return vm, nil @@ -443,3 +444,52 @@ func getDiskSize(path string) (uint64, error) { } return tmpInfo.VirtualSize, nil } + +// List lists all vm's that use qemu virtualization +func List(opts machine.ListOptions) ([]*machine.ListResponse, error) { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return nil, err + } + + var listed []*machine.ListResponse + + if err = filepath.Walk(vmConfigDir, func(path string, info os.FileInfo, err error) error { + vm := new(MachineVM) + if strings.HasSuffix(info.Name(), ".json") { + fullPath := filepath.Join(vmConfigDir, info.Name()) + b, err := ioutil.ReadFile(fullPath) + if err != nil { + return err + } + err = json.Unmarshal(b, vm) + if err != nil { + return err + } + listEntry := new(machine.ListResponse) + + listEntry.Name = vm.Name + listEntry.VMType = "qemu" + fi, err := os.Stat(fullPath) + if err != nil { + return err + } + listEntry.CreatedAt = fi.ModTime() + + fi, err = os.Stat(vm.ImagePath) + if err != nil { + return err + } + listEntry.LastUp = fi.ModTime() + if vm.isRunning() { + listEntry.Running = true + } + + listed = append(listed, listEntry) + } + return nil + }); err != nil { + return nil, err + } + return listed, err +} diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 9030f0095..58b2dff0a 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -298,7 +298,7 @@ t POST containers/prune?filters='garb1age}' 500 \ t POST libpod/containers/prune?filters='garb1age}' 500 \ .cause="invalid character 'g' looking for beginning of value" -## Prune containers with illformed label +# Prune containers with illformed label t POST containers/prune?filters='{"label":["tes' 500 \ .cause="unexpected end of JSON input" t POST libpod/containers/prune?filters='{"label":["tes' 500 \ @@ -306,6 +306,22 @@ t POST libpod/containers/prune?filters='{"label":["tes' 500 \ t GET libpod/containers/json?filters='{"label":["testlabel"]}' 200 length=0 +# libpod api: do not use list filters for prune +t POST libpod/containers/prune?filters='{"name":["anyname"]}' 500 \ + .cause="name is an invalid filter" +t POST libpod/containers/prune?filters='{"id":["anyid"]}' 500 \ + .cause="id is an invalid filter" +t POST libpod/containers/prune?filters='{"network":["anynetwork"]}' 500 \ + .cause="network is an invalid filter" + +# compat api: do not use list filters for prune +t POST containers/prune?filters='{"name":["anyname"]}' 500 \ + .cause="name is an invalid filter" +t POST containers/prune?filters='{"id":["anyid"]}' 500 \ + .cause="id is an invalid filter" +t POST containers/prune?filters='{"network":["anynetwork"]}' 500 \ + .cause="network is an invalid filter" + # Test CPU limit (NanoCPUs) t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \ .Id~[0-9a-f]\\{64\\} diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at index 39bfa2e32..0a049d869 100644 --- a/test/apiv2/25-containersMore.at +++ b/test/apiv2/25-containersMore.at @@ -38,7 +38,8 @@ t GET libpod/containers/foo/json 200 \ # List processes of the container t GET libpod/containers/foo/top 200 \ - length=2 + length=2 \ + .Processes[0][7]="top" # List processes of none such t GET libpod/containers/nonesuch/top 404 |