summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/create.go2
-rw-r--r--cmd/podman/formats/formats.go30
-rw-r--r--cmd/podman/inspect.go7
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/pull.go20
-rw-r--r--cmd/podman/push.go18
-rw-r--r--cmd/podman/search.go290
-rw-r--r--cmd/podman/spec.go14
8 files changed, 342 insertions, 40 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index ac6bc2969..b3aa42b8a 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -527,7 +527,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
}
// STOP SIGNAL
- stopSignal := syscall.SIGINT
+ stopSignal := syscall.SIGTERM
signalString := data.Config.StopSignal
if c.IsSet("stop-signal") {
signalString = c.String("stop-signal")
diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go
index 4b6527b30..bfd773c45 100644
--- a/cmd/podman/formats/formats.go
+++ b/cmd/podman/formats/formats.go
@@ -11,6 +11,7 @@ import (
"github.com/ghodss/yaml"
"github.com/pkg/errors"
+ "golang.org/x/crypto/ssh/terminal"
)
const (
@@ -70,7 +71,8 @@ func (j JSONStructArray) Out() error {
// If the we did get NULL back, we should spit out {} which is
// at least valid JSON for the consumer.
- fmt.Printf("%s\n", data)
+ fmt.Printf("%s", data)
+ humanNewLine()
return nil
}
@@ -95,13 +97,20 @@ func (t StdoutTemplateArray) Out() error {
if err != nil {
return errors.Wrapf(err, "Template parsing error")
}
- for _, img := range t.Output {
+ for i, img := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
err = basicTmpl.Execute(w, img)
if err != nil {
return err
}
- fmt.Fprintln(w, "")
+ if i != len(t.Output)-1 {
+ fmt.Fprintln(w, "")
+ continue
+ }
+ // Only print new line at the end of the output if stdout is the terminal
+ if terminal.IsTerminal(int(os.Stdout.Fd())) {
+ fmt.Fprintln(w, "")
+ }
}
return w.Flush()
}
@@ -112,7 +121,8 @@ func (j JSONStruct) Out() error {
if err != nil {
return err
}
- fmt.Printf("%s\n", data)
+ fmt.Printf("%s", data)
+ humanNewLine()
return nil
}
@@ -126,7 +136,7 @@ func (t StdoutTemplate) Out() error {
if err != nil {
return err
}
- fmt.Println()
+ humanNewLine()
return nil
}
@@ -138,6 +148,14 @@ func (y YAMLStruct) Out() error {
if err != nil {
return err
}
- fmt.Println(string(buf))
+ fmt.Printf("%s", string(buf))
+ humanNewLine()
return nil
}
+
+// humanNewLine prints a new line at the end of the output only if stdout is the terminal
+func humanNewLine() {
+ if terminal.IsTerminal(int(os.Stdout.Fd())) {
+ fmt.Println()
+ }
+}
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index 2e70eaa0a..ba7b17ed7 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -2,6 +2,7 @@ package main
import (
"encoding/json"
+
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/podman/formats"
@@ -163,8 +164,8 @@ func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
}
data := &inspect.ContainerData{
- CtrInspectData: ctrInspectData,
- HostConfig: &inspect.HostConfig{
+ ctrInspectData,
+ &inspect.HostConfig{
ConsoleSize: spec.Process.ConsoleSize,
OomScoreAdj: spec.Process.OOMScoreAdj,
CPUShares: shares,
@@ -210,7 +211,7 @@ func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
Ulimits: createArtifact.Resources.Ulimit,
SecurityOpt: createArtifact.SecurityOpts,
},
- Config: &inspect.CtrConfig{
+ &inspect.CtrConfig{
Hostname: spec.Hostname,
User: spec.Process.User,
Env: spec.Process.Env,
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index bda8ff517..f18615760 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -64,6 +64,7 @@ func main() {
rmiCommand,
runCommand,
saveCommand,
+ searchCommand,
startCommand,
statsCommand,
stopCommand,
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index 5726b20f1..c19e6715b 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -1,16 +1,14 @@
package main
import (
- "fmt"
"io"
"os"
- "golang.org/x/crypto/ssh/terminal"
-
"github.com/containers/image/types"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/common"
+ "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -80,19 +78,11 @@ func pullCmd(c *cli.Context) error {
image := args[0]
var registryCreds *types.DockerAuthConfig
- if c.String("creds") != "" {
- creds, err := common.ParseRegistryCreds(c.String("creds"))
+
+ if c.IsSet("creds") {
+ creds, err := util.ParseRegistryCreds(c.String("creds"))
if err != nil {
- if err == common.ErrNoPassword {
- fmt.Print("Password: ")
- password, err := terminal.ReadPassword(0)
- if err != nil {
- return errors.Wrapf(err, "could not read password from terminal")
- }
- creds.Password = string(password)
- } else {
- return err
- }
+ return err
}
registryCreds = creds
}
diff --git a/cmd/podman/push.go b/cmd/podman/push.go
index 69d6e6629..2f0d73ffa 100644
--- a/cmd/podman/push.go
+++ b/cmd/podman/push.go
@@ -13,8 +13,8 @@ import (
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/common"
+ "github.com/projectatomic/libpod/pkg/util"
"github.com/urfave/cli"
- "golang.org/x/crypto/ssh/terminal"
)
var (
@@ -97,25 +97,15 @@ func pushCmd(c *cli.Context) error {
}
}
- registryCredsString := c.String("creds")
certPath := c.String("cert-dir")
skipVerify := !c.BoolT("tls-verify")
removeSignatures := c.Bool("remove-signatures")
signBy := c.String("sign-by")
- if registryCredsString != "" {
- creds, err := common.ParseRegistryCreds(registryCredsString)
+ if c.IsSet("creds") {
+ creds, err := util.ParseRegistryCreds(c.String("creds"))
if err != nil {
- if err == common.ErrNoPassword {
- fmt.Print("Password: ")
- password, err := terminal.ReadPassword(0)
- if err != nil {
- return errors.Wrapf(err, "could not read password from terminal")
- }
- creds.Password = string(password)
- } else {
- return err
- }
+ return err
}
registryCreds = creds
}
diff --git a/cmd/podman/search.go b/cmd/podman/search.go
new file mode 100644
index 000000000..01eaa6729
--- /dev/null
+++ b/cmd/podman/search.go
@@ -0,0 +1,290 @@
+package main
+
+import (
+ "context"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/containers/image/docker"
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/formats"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/projectatomic/libpod/libpod/common"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+const (
+ descriptionTruncLength = 44
+ maxQueries = 25
+)
+
+var (
+ searchFlags = []cli.Flag{
+ cli.StringSliceFlag{
+ Name: "filter, f",
+ Usage: "filter output based on conditions provided (default [])",
+ },
+ cli.StringFlag{
+ Name: "format",
+ Usage: "change the output format to a Go template",
+ },
+ cli.IntFlag{
+ Name: "limit",
+ Usage: "limit the number of results",
+ },
+ cli.BoolFlag{
+ Name: "no-trunc",
+ Usage: "do not truncate the output",
+ },
+ cli.StringSliceFlag{
+ Name: "registry",
+ Usage: "specific registry to search",
+ },
+ }
+ searchDescription = `
+ Search registries for a given image. Can search all the default registries or a specific registry.
+ Can limit the number of results, and filter the output based on certain conditions.`
+ searchCommand = cli.Command{
+ Name: "search",
+ Usage: "search registry for image",
+ Description: searchDescription,
+ Flags: searchFlags,
+ Action: searchCmd,
+ ArgsUsage: "TERM",
+ }
+)
+
+type searchParams struct {
+ Index string
+ Name string
+ Description string
+ Stars int
+ Official string
+ Automated string
+}
+
+type searchOpts struct {
+ filter []string
+ limit int
+ noTrunc bool
+ format string
+}
+
+type searchFilterParams struct {
+ stars int
+ isAutomated *bool
+ isOfficial *bool
+}
+
+func searchCmd(c *cli.Context) error {
+ args := c.Args()
+ if len(args) > 1 {
+ return errors.Errorf("too many arguments. Requires exactly 1")
+ }
+ if len(args) == 0 {
+ return errors.Errorf("no argument given, requires exactly 1 argument")
+ }
+ term := args[0]
+
+ if err := validateFlags(c, searchFlags); err != nil {
+ return err
+ }
+
+ runtime, err := getRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ format := genSearchFormat(c.String("format"))
+ opts := searchOpts{
+ format: format,
+ noTrunc: c.Bool("no-trunc"),
+ limit: c.Int("limit"),
+ filter: c.StringSlice("filter"),
+ }
+
+ var registries []string
+ if len(c.StringSlice("registry")) > 0 {
+ registries = c.StringSlice("registry")
+ } else {
+ registries, err = libpod.GetRegistries()
+ if err != nil {
+ return errors.Wrapf(err, "error getting registries to search")
+ }
+ }
+
+ filter, err := parseSearchFilter(&opts)
+ if err != nil {
+ return err
+ }
+
+ return generateSearchOutput(term, registries, opts, *filter)
+}
+
+func genSearchFormat(format string) string {
+ if format != "" {
+ // "\t" from the command line is not being recognized as a tab
+ // replacing the string "\t" to a tab character if the user passes in "\t"
+ return strings.Replace(format, `\t`, "\t", -1)
+ }
+ return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
+}
+
+func searchToGeneric(params []searchParams) (genericParams []interface{}) {
+ for _, v := range params {
+ genericParams = append(genericParams, interface{}(v))
+ }
+ return genericParams
+}
+
+func (s *searchParams) headerMap() map[string]string {
+ v := reflect.Indirect(reflect.ValueOf(s))
+ values := make(map[string]string, v.NumField())
+
+ for i := 0; i < v.NumField(); i++ {
+ key := v.Type().Field(i).Name
+ value := key
+ values[key] = strings.ToUpper(splitCamelCase(value))
+ }
+ return values
+}
+
+func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) ([]searchParams, error) {
+ sc := common.GetSystemContext("", "", false)
+ // Max number of queries by default is 25
+ limit := maxQueries
+ if opts.limit != 0 {
+ limit = opts.limit
+ }
+
+ var paramsArr []searchParams
+ for _, reg := range registries {
+ results, err := docker.SearchRegistry(context.TODO(), sc, reg, term, limit)
+ if err != nil {
+ logrus.Errorf("error searching registry %q: %v", reg, err)
+ continue
+ }
+ index := reg
+ arr := strings.Split(reg, ".")
+ if len(arr) > 2 {
+ index = strings.Join(arr[len(arr)-2:], ".")
+ }
+
+ // limit is the number of results to output
+ // if the total number of results is less than the limit, output all
+ // if the limit has been set by the user, output those number of queries
+ limit := maxQueries
+ if len(results) < limit {
+ limit = len(results)
+ }
+ if opts.limit != 0 && opts.limit < len(results) {
+ limit = opts.limit
+ }
+
+ for i := 0; i < limit; i++ {
+ if len(opts.filter) > 0 {
+ // Check whether query matches filters
+ if !(matchesAutomatedFilter(filter, results[i]) && matchesOfficialFilter(filter, results[i]) && matchesStarFilter(filter, results[i])) {
+ continue
+ }
+ }
+ official := ""
+ if results[i].IsOfficial {
+ official = "[OK]"
+ }
+ automated := ""
+ if results[i].IsAutomated {
+ automated = "[OK]"
+ }
+ description := strings.Replace(results[i].Description, "\n", " ", -1)
+ if len(description) > 44 && !opts.noTrunc {
+ description = description[:descriptionTruncLength] + "..."
+ }
+ name := index + "/" + results[i].Name
+ if index == "docker.io" && !strings.Contains(results[i].Name, "/") {
+ name = index + "/library/" + results[i].Name
+ }
+ params := searchParams{
+ Index: index,
+ Name: name,
+ Description: description,
+ Official: official,
+ Automated: automated,
+ Stars: results[i].StarCount,
+ }
+ paramsArr = append(paramsArr, params)
+ }
+ }
+ return paramsArr, nil
+}
+
+func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) error {
+ searchOutput, err := getSearchOutput(term, registries, opts, filter)
+ if err != nil {
+ return err
+ }
+ if len(searchOutput) == 0 {
+ return nil
+ }
+ out := formats.StdoutTemplateArray{Output: searchToGeneric(searchOutput), Template: opts.format, Fields: searchOutput[0].headerMap()}
+ return formats.Writer(out).Out()
+}
+
+func parseSearchFilter(opts *searchOpts) (*searchFilterParams, error) {
+ filterParams := &searchFilterParams{}
+ ptrTrue := true
+ ptrFalse := false
+ for _, filter := range opts.filter {
+ arr := strings.Split(filter, "=")
+ switch arr[0] {
+ case "stars":
+ if len(arr) < 2 {
+ return nil, errors.Errorf("invalid `stars` filter %q, should be stars=<value>", filter)
+ }
+ stars, err := strconv.Atoi(arr[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "incorrect value type for stars filter")
+ }
+ filterParams.stars = stars
+ break
+ case "is-automated":
+ if len(arr) == 2 && arr[1] == "false" {
+ filterParams.isAutomated = &ptrFalse
+ } else {
+ filterParams.isAutomated = &ptrTrue
+ }
+ break
+ case "is-official":
+ if len(arr) == 2 && arr[1] == "false" {
+ filterParams.isOfficial = &ptrFalse
+ } else {
+ filterParams.isOfficial = &ptrTrue
+ }
+ break
+ default:
+ return nil, errors.Errorf("invalid filter type %q", filter)
+ }
+ }
+ return filterParams, nil
+}
+
+func matchesStarFilter(filter searchFilterParams, result docker.SearchResult) bool {
+ return result.StarCount >= filter.stars
+}
+
+func matchesAutomatedFilter(filter searchFilterParams, result docker.SearchResult) bool {
+ if filter.isAutomated != nil {
+ return result.IsAutomated == *filter.isAutomated
+ }
+ return true
+}
+
+func matchesOfficialFilter(filter searchFilterParams, result docker.SearchResult) bool {
+ if filter.isOfficial != nil {
+ return result.IsOfficial == *filter.isOfficial
+ }
+ return true
+}
diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go
index d21d8b6da..56e8c8d05 100644
--- a/cmd/podman/spec.go
+++ b/cmd/podman/spec.go
@@ -156,12 +156,24 @@ func addDevice(g *generate.Generator, device string) error {
// Parses information needed to create a container into an OCI runtime spec
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
+ cgroupPerm := "ro"
g := generate.New()
+ if config.Privileged {
+ cgroupPerm = "rw"
+ g.RemoveMount("/sys")
+ sysMnt := spec.Mount{
+ Destination: "/sys",
+ Type: "sysfs",
+ Source: "sysfs",
+ Options: []string{"nosuid", "noexec", "nodev", "rw"},
+ }
+ g.AddMount(sysMnt)
+ }
cgroupMnt := spec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
- Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
+ Options: []string{"nosuid", "noexec", "nodev", "relatime", cgroupPerm},
}
g.AddMount(cgroupMnt)
g.SetProcessCwd(config.WorkDir)