summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/create.go2
-rw-r--r--cmd/podman/inspect.go7
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/search.go290
-rw-r--r--completions/bash/podman14
-rw-r--r--docs/podman-build.1.md3
-rw-r--r--docs/podman-inspect.1.md10
-rw-r--r--docs/podman-login.1.md3
-rw-r--r--docs/podman-pull.1.md3
-rw-r--r--docs/podman-push.1.md3
-rw-r--r--docs/podman-run.1.md4
-rw-r--r--docs/podman-search.1.md115
-rw-r--r--docs/podman-stop.1.md4
-rw-r--r--docs/tutorials/podman_tutorial.md42
-rw-r--r--libpod/container_api.go5
-rw-r--r--libpod/oci.go1
-rw-r--r--pkg/inspect/inspect.go6
-rwxr-xr-xtest/demos.sh93
-rw-r--r--test/e2e/create_test.go2
-rw-r--r--test/e2e/images_test.go53
-rw-r--r--test/e2e/import_test.go107
-rw-r--r--test/e2e/inspect_test.go75
-rw-r--r--test/e2e/libpod_suite_test.go11
-rw-r--r--test/e2e/logs_test.go60
-rw-r--r--test/e2e/run_dns_test.go82
-rw-r--r--test/podman_images.bats37
-rw-r--r--test/podman_import.bats83
-rw-r--r--test/podman_inspect.bats51
-rw-r--r--test/podman_logs.bats51
-rw-r--r--test/podman_run_dns.bats56
-rw-r--r--test/podman_search.bats43
-rw-r--r--transfer.md2
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/containers/image/directory/directory_dest.go2
-rw-r--r--vendor/github.com/containers/image/docker/docker_client.go190
-rw-r--r--vendor/github.com/containers/image/docker/docker_image_dest.go8
-rw-r--r--vendor/github.com/containers/image/image/oci.go10
-rw-r--r--vendor/github.com/containers/image/ostree/ostree_dest.go10
-rw-r--r--vendor/github.com/containers/image/tarball/tarball_reference.go2
39 files changed, 1189 insertions, 354 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/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/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/completions/bash/podman b/completions/bash/podman
index 0087c56b9..b1033df1c 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -954,6 +954,19 @@ _podman_pull() {
_complete_ "$options_with_args" "$boolean_options"
}
+_podman_search() {
+ local options_with_args="
+ --filter -f
+ --format
+ --limit
+ --registry
+ "
+ local boolean_options="
+ --no-trunc
+ "
+ _complete_ "$options_with_args" "$boolean_options"
+}
+
_podman_unmount() {
_podman_umount $@
}
@@ -1589,6 +1602,7 @@ _podman_podman() {
rmi
run
save
+ search
start
stats
stop
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index 61c8d8aaa..d4e9af175 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -38,7 +38,8 @@ resulting image's configuration.
**--cert-dir** *path*
-Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry
+Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry.
+Default certificates directory is _/etc/containers/certs.d_.
**--creds** *creds*
diff --git a/docs/podman-inspect.1.md b/docs/podman-inspect.1.md
index 5b8a344d7..e40dfa29f 100644
--- a/docs/podman-inspect.1.md
+++ b/docs/podman-inspect.1.md
@@ -79,6 +79,16 @@ Display the total file size if the type is a container
}
```
+```
+# podman inspect a04 --format "{{.ImageName}}"
+fedora
+```
+
+```
+$ sudo podman inspect a04 --format "{{.GraphDriver.Name}}"
+overlay
+```
+
## SEE ALSO
podman(1)
diff --git a/docs/podman-login.1.md b/docs/podman-login.1.md
index fcf32870a..b22a02553 100644
--- a/docs/podman-login.1.md
+++ b/docs/podman-login.1.md
@@ -38,7 +38,8 @@ Username for registry
Path of the authentication file. Default is ${XDG_\RUNTIME\_DIR}/containers/auth.json
**--cert-dir**
-Pathname of a directory containing TLS certificates and keys used to connect to the registry
+Pathname of a directory containing TLS certificates and keys used to connect to the registry.
+Default certificates directory is _/etc/containers/certs.d_.
**--tls-verify**
Require HTTPS and verify certificates when contacting registries (default: true)
diff --git a/docs/podman-pull.1.md b/docs/podman-pull.1.md
index e49b69293..1a1611fd0 100644
--- a/docs/podman-pull.1.md
+++ b/docs/podman-pull.1.md
@@ -61,7 +61,8 @@ If the authorization state is not found there, $HOME/.docker/config.json is chec
**--cert-dir**
-Pathname of a directory containing TLS certificates and keys
+Pathname of a directory containing TLS certificates and keys.
+Default certificates directory is _/etc/containers/certs.d_.
**--creds**
diff --git a/docs/podman-push.1.md b/docs/podman-push.1.md
index b94672ebe..964758da6 100644
--- a/docs/podman-push.1.md
+++ b/docs/podman-push.1.md
@@ -59,7 +59,8 @@ value can be entered. The password is entered without echo.
**cert-dir="PATHNAME"**
-Pathname of a directory containing TLS certificates and keys
+Pathname of a directory containing TLS certificates and keys.
+Default certificates directory is _/etc/containers/certs.d_.
**--compress**
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 3a7a2954a..3cb25cdae 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -620,8 +620,8 @@ This should list the message sent to logger.
### Attaching to one or more from STDIN, STDOUT, STDERR
-If you do not specify -a then podman will attach everything (stdin,stdout,stderr)
-. You can specify to which of the three standard streams (stdin, stdout, stderr)
+If you do not specify -a then podman will attach everything (stdin,stdout,stderr).
+You can specify to which of the three standard streams (stdin, stdout, stderr)
you'd like to connect instead, as in:
# podman run -a stdin -a stdout -i -t fedora /bin/bash
diff --git a/docs/podman-search.1.md b/docs/podman-search.1.md
new file mode 100644
index 000000000..668748d3f
--- /dev/null
+++ b/docs/podman-search.1.md
@@ -0,0 +1,115 @@
+% podman(1) podman-search - Tool to search registries for an image
+% Urvashi Mohnani
+# podman-search "1" "January 2018" "podman"
+
+## NAME
+podman search - Search a registry for an image
+
+## SYNOPSIS
+**podman search**
+**TERM**
+[**--filter**|**-f**]
+[**--format**]
+[**--limit**]
+[**--no-trunc**]
+[**--registry**]
+[**--help**|**-h**]
+
+## DESCRIPTION
+**podman search** searches a registry or a list of registries for a matching image.
+The user can specify which registry to search by setting the **--registry** flag, default
+is the default registries set in the config file - **/etc/containers/registries.conf**.
+The number of results can be limited using the **--limit** flag. If more than one registry
+is being searched, the limit will be applied to each registry. The output can be filtered
+using the **--filter** flag.
+
+**podman [GLOBAL OPTIONS]**
+
+**podman search [GLOBAL OPTIONS]**
+
+**podman search [OPTIONS] TERM**
+
+## OPTIONS
+
+**--filter, -f**
+Filter output based on conditions provided (default [])
+
+Supported filters are:
+- stars (int - number of stars the image has)
+- is-automated (boolean - true | false) - is the image automated or not
+- is-official (boolean - true | false) - is the image official or not
+
+**--format**
+Change the output format to a Go template
+
+Valid placeholders for the Go template are listed below:
+
+| **Placeholder** | **Description** |
+| --------------- | ---------------------------- |
+| .Index | Registry |
+| .Name | Image name |
+| .Descriptions | Image description |
+| .Stars | Star count of image |
+| .Official | "[OK]" if image is official |
+| .Automated | "[OK]" if image is automated |
+
+**--limit**
+Limit the number of results
+Note: The results from each registry will be limited to this value.
+Example if limit is 10 and two registries are being searched, the total
+number of results will be 20, 10 from each (if there are at least 10 matches in each).
+The order of the search results is the order in which the API endpoint returns the results.
+
+**--no-trunc**
+Do not truncate the output
+
+**--registry**
+Specific registry to search (only the given registry will be searched, not the default registries)
+
+## EXAMPLES
+
+```
+# podman search --limit 3 rhel
+INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
+docker.io docker.io/richxsl/rhel7 RHEL 7 image with minimal installation 9
+docker.io docker.io/bluedata/rhel7 RHEL-7.x base container images 1
+docker.io docker.io/gidikern/rhel-oracle-jre RHEL7 with jre8u60 5 [OK]
+redhat.com redhat.com/rhel This platform image provides a minimal runti... 0
+redhat.com redhat.com/rhel6 This platform image provides a minimal runti... 0
+redhat.com redhat.com/rhel6.5 This platform image provides a minimal runti... 0
+```
+
+```
+# podman search alpine
+INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
+docker.io docker.io/library/alpine A minimal Docker image based on Alpine Linux... 3009 [OK]
+docker.io docker.io/mhart/alpine-node Minimal Node.js built on Alpine Linux 332
+docker.io docker.io/anapsix/alpine-java Oracle Java 8 (and 7) with GLIBC 2.23 over A... 272 [OK]
+docker.io docker.io/tenstartups/alpine Alpine linux base docker image with useful p... 5 [OK]
+```
+
+```
+# podman search --registry registry.fedoraproject.org fedora
+INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
+fedoraproject.org fedoraproject.org/fedora 0
+fedoraproject.org fedoraproject.org/fedora-minimal 0
+```
+
+```
+# podman search --filter=is-official alpine
+INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
+docker.io docker.io/library/alpine A minimal Docker image based on Alpine Linux... 3009 [OK]
+```
+
+```
+# podman search --registry registry.fedoraproject.org --format "table {{.Index}} {{.Name}}" fedora
+INDEX NAME
+fedoraproject.org fedoraproject.org/fedora
+fedoraproject.org fedoraproject.org/fedora-minimal
+```
+
+## SEE ALSO
+podman(1), crio(8), crio.conf(5)
+
+## HISTORY
+January 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/podman-stop.1.md b/docs/podman-stop.1.md
index f4372be5f..58cb2c39c 100644
--- a/docs/podman-stop.1.md
+++ b/docs/podman-stop.1.md
@@ -11,7 +11,9 @@ podman stop - Stop one or more containers
## DESCRIPTION
Stops one or more containers. You may use container IDs or names as input. The **--timeout** switch
allows you to specify the number of seconds to wait before forcibly stopping the container after the stop command
-is issued to the container. The default is 10 seconds.
+is issued to the container. The default is 10 seconds. By default, containers are stopped with SIGTERM
+and then SIGKILL after the timeout. The SIGTERM default can be overridden by the image used to create the
+container and also via command line when creating the container.
## OPTIONS
diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md
index 8402dd74b..5e174ac6b 100644
--- a/docs/tutorials/podman_tutorial.md
+++ b/docs/tutorials/podman_tutorial.md
@@ -12,7 +12,7 @@ for Podman until an RPM becomes available.
### Installing build and runtime dependencies
```
-# sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \
+$ sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \
gpgme-devel glib2-devel device-mapper-devel libseccomp-devel \
atomic-registries iptables skopeo-containers containernetworking-cni \
conmon
@@ -22,7 +22,7 @@ for Podman until an RPM becomes available.
# git clone https://github.com/projectatomic/libpod/ ~/src/github.com/projectatomic/libpod
# cd !$
# make
-# sudo make install PREFIX=/usr
+$ sudo make install PREFIX=/usr
```
You now have a working podman environment. Jump to [Familiarizing yourself with Podman](Familiarizing yourself with Podman)
@@ -38,8 +38,8 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use
#### Installing base packages
```
-# sudo apt-get update
-# sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \
+$ sudo apt-get update
+$ sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \
go-md2man libprotobuf-dev libprotobuf-c0-dev libseccomp-dev
```
#### Building and installing conmon
@@ -48,28 +48,28 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use
# cd ~/src/github.com/kubernetes-incubator/cri-o
# mkdir bin
# make conmon
-# sudo install -D -m 755 bin/conmon /usr/libexec/crio/conmon
+$ sudo install -D -m 755 bin/conmon /usr/libexec/crio/conmon
```
#### Adding required configuration files
```
-# sudo mkdir -p /etc/containers
-# sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf
-# sudo curl https://raw.githubusercontent.com/projectatomic/skopeo/master/default-policy.json -o /etc/containers/policy.json
+$ sudo mkdir -p /etc/containers
+$ sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf
+$ sudo curl https://raw.githubusercontent.com/projectatomic/skopeo/master/default-policy.json -o /etc/containers/policy.json
```
#### Installing CNI plugins
```
# git clone https://github.com/containernetworking/plugins.git ~/src/github.com/containernetworking/plugins
# cd ~/src/github.com/containernetworking/plugins
# ./build.sh
-# sudo mkdir -p /usr/libexec/cni
-# sudo cp bin/* /usr/libexec/cni
+$ sudo mkdir -p /usr/libexec/cni
+$ sudo cp bin/* /usr/libexec/cni
```
#### Installing runc
```
# git clone https://github.com/opencontainers/runc.git ~/src/github.com/opencontainers/runc
# cd ~/src/github.com/opencontainers/runc
# GOPATH=~/ make static BUILDTAGS="seccomp selinux"
-# sudo cp runc /usr/bin/runc
+$ sudo cp runc /usr/bin/runc
```
### Building and installing Podman
@@ -77,7 +77,7 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use
# git clone https://github.com/projectatomic/libpod/ ~/src/github.com/projectatomic/libpod
# cd ~/src/github.com/projectatomic/libpod
# make
-# sudo make install PREFIX=/usr
+$ sudo make install PREFIX=/usr
```
## Familiarizing yourself with Podman
@@ -86,7 +86,7 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use
This sample container will run a very basic httpd server that serves only its index
page.
```
-# sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \
+$ sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \
-e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \
-e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \
registry.fedoraproject.org/f26/httpd /usr/bin/run-httpd
@@ -97,7 +97,7 @@ will print the container ID after it has run.
### Listing running containers
The Podman *ps* command is used to list creating and running containers.
```
-# sudo podman ps
+$ sudo podman ps
```
Note: If you add *-a* to the *ps* command, Podman will show all containers.
@@ -109,8 +109,8 @@ install *iproute* in the container. Notice here that we use the switch **--late
created container. You could also use the container's ID listed during *podman ps* in the previous step or
when you ran the container.
```
-# sudo podman exec --latest -t dnf -y install iproute
-# sudo podman exec --latest -t ip a
+$ sudo podman exec --latest -t dnf -y install iproute
+$ sudo podman exec --latest -t ip a
```
Note the IP address of the *ethernet* device.
@@ -126,30 +126,30 @@ containerized httpd server.
### Viewing the container's logs
You can view the container's logs with Podman as well:
```
-# sudo podman logs --latest
+$ sudo podman logs --latest
```
<!-- (
### Viewing the container's pids
And you can observe the httpd pid in the container with *top*.
```
-# sudo podman top <container_id>
+$ sudo podman top <container_id>
``` ) -->
### Stopping the container
To stop the httpd container:
```
-# sudo podman stop --latest
+$ sudo podman stop --latest
```
You can also check the status of one or more containers using the *ps* subcommand. In this case, we should
use the *-a* argument to list all containers.
```
-# sudo podman ps -a
+$ sudo podman ps -a
```
### Removing the container
To remove the httpd container:
```
-# sudo podman rm --latest
+$ sudo podman rm --latest
```
You can verify the deletion of the container by running *podman ps -a*.
## More information
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 05b3e89e6..3e1d600a8 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -313,6 +313,11 @@ func (c *Container) Start() error {
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
}
+ // TODO remove this when we patch conmon to support restarting containers
+ if c.state.State == ContainerStateStopped {
+ return errors.Wrapf(ErrNotImplemented, "restarting a stopped container is not yet supported")
+ }
+
// Mount storage for the container
if err := c.mountStorage(); err != nil {
return err
diff --git a/libpod/oci.go b/libpod/oci.go
index 4ca0bfbef..86313a493 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -394,6 +394,7 @@ func (r *OCIRuntime) startContainer(ctr *Container) error {
// killContainer sends the given signal to the given container
func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error {
+ logrus.Debugf("Sending signal %d to container %s", signal, ctr.ID())
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.path, "kill", ctr.ID(), fmt.Sprintf("%d", signal)); err != nil {
return errors.Wrapf(err, "error sending signal to container %s", ctr.ID())
}
diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go
index 9e7137560..11a252535 100644
--- a/pkg/inspect/inspect.go
+++ b/pkg/inspect/inspect.go
@@ -12,9 +12,9 @@ import (
// ContainerData holds the podman inspect data for a container
type ContainerData struct {
- CtrInspectData *ContainerInspectData `json:"CtrInspectData"`
- HostConfig *HostConfig `json:"HostConfig"`
- Config *CtrConfig `json:"Config"`
+ *ContainerInspectData
+ HostConfig *HostConfig `json:"HostConfig"`
+ Config *CtrConfig `json:"Config"`
}
// HostConfig represents the host configuration for the container
diff --git a/test/demos.sh b/test/demos.sh
new file mode 100755
index 000000000..4ce29e160
--- /dev/null
+++ b/test/demos.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+echo "This is a demo of the podman search command."
+echo ""
+
+read -p "--> cat /etc/containers/registries.conf"
+cat /etc/containers/registries.conf
+echo ""
+
+read -p "--> podman search fedora"
+podman search fedora
+echo ""
+
+read -p "--> podman search --filter stars=34 fedora"
+podman search --filter stars=34 fedora
+echo ""
+
+read -p "--> podman search --filter is-automated=false --filter stars=34 --filter is-official fedora"
+podman search --filter is-automated=false --filter stars=34 --filter is-official fedora
+echo ""
+
+read -p "--> podman search --no-trunc --limit 3 fedora"
+podman search --no-trunc --limit 3 fedora
+echo ""
+
+read -p "--> podman search --registry registry.access.redhat.com rhel7"
+podman search --registry registry.access.redhat.com rhel7
+echo ""
+
+read -p "--> podman search --format \"table {{.Name}} {{.Description}}\" fedora"
+podman search --format "table {{.Name}} {{.Description}}" fedora
+echo ""
+
+read -p "Demo of a few podman run and create options"
+echo ""
+
+read -p "--> podman run --memory 80m fedora cat /sys/fs/cgroup/memory/memory.limit_in_bytes"
+podman run --rm --memory 80m fedora cat /sys/fs/cgroup/memory/memory.limit_in_bytes
+echo ""
+
+read -p "--> podman run --memory 80m --memory-reservation 40m fedora cat /sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
+podman run --rm --memory 80m --memory-reservation 40m fedora cat /sys/fs/cgroup/memory/memory.soft_limit_in_bytes
+echo ""
+
+read -p "--> podman run --memory 40m --memory-reservation 80m fedora cat /sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
+podman run --rm --memory 40m --memory-reservation 80m fedora cat /sys/fs/cgroup/memory/memory.soft_limit_in_bytes
+echo ""
+
+read -p "--> podman run --memory-swappiness 15 fedora cat /sys/fs/cgroup/memory/memory.swappiness"
+podman run --rm --memory-swappiness 15 fedora cat /sys/fs/cgroup/memory/memory.swappiness
+echo ""
+
+read -p "--> podman run --kernel-memory 40m fedora cat /sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
+podman run --rm --kernel-memory 40m fedora cat /sys/fs/cgroup/memory/memory.kmem.limit_in_bytes
+echo ""
+
+read -p "--> podman run --cpu-period 5000 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_period_us"
+podman run --rm --cpu-period 5000 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_period_us
+echo ""
+
+read -p "--> podman run --cpu-quota 15000 --cpu-period 5000 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us"
+podman run --rm --cpu-quota 15000 --cpu-period 5000 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
+echo ""
+
+read -p "--> podman run --cpus 0.5 fedora /bin/bash"
+read -p "cat /sys/fs/cgroup/cpu/cpu.cfs_period_us"
+podman run --rm --cpus 0.5 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_period_us
+read -p "cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us"
+podman run --rm --cpus 0.5 fedora cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
+echo ""
+
+read -p "--> podman run --cpu-shares 2 fedora cat /sys/fs/cgroup/cpu/cpu.shares"
+podman run --rm --cpu-shares 2 fedora cat /sys/fs/cgroup/cpu/cpu.shares
+echo ""
+
+read -p "--> podman run --cpuset-cpus=0,2 fedora cat /sys/fs/cgroup/cpuset/cpuset.cpus"
+podman run --rm --cpuset-cpus=0,2 fedora cat /sys/fs/cgroup/cpuset/cpuset.cpus
+echo ""
+
+read -p "--> podman run --sysctl net.core.somaxconn=65535 alpine sysctl net.core.somaxconn"
+podman run --rm --sysctl net.core.somaxconn=65535 alpine sysctl net.core.somaxconn
+echo ""
+
+read -p "--> podman run --ulimit nofile=1024:1028 fedora ulimit -n"
+podman run --rm --ulimit nofile=1024:1028 fedora ulimit -n
+echo ""
+
+read -p "--> podman run --blkio-weight 15 fedora cat /sys/fs/cgroup/blkio/blkio.weight"
+podman run --rm --blkio-weight 15 fedora cat /sys/fs/cgroup/blkio/blkio.weight
+echo ""
+
+read -p "End of Demo."
+echo "Thank you!" \ No newline at end of file
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index c116cea7d..e54e35761 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -38,7 +38,7 @@ var _ = Describe("Podman create", func() {
check := podmanTest.Podman([]string{"inspect", "-l"})
check.WaitWithDefaultTimeout()
data := check.InspectContainerToJSON()
- Expect(data.CtrInspectData.ID).To(ContainSubstring(cid))
+ Expect(data.ID).To(ContainSubstring(cid))
})
It("podman create container based on a remote image", func() {
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
new file mode 100644
index 000000000..099331c94
--- /dev/null
+++ b/test/e2e/images_test.go
@@ -0,0 +1,53 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman images", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+
+ })
+
+ It("podman images", func() {
+ session := podmanTest.Podman([]string{"images"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 3))
+ Expect(session.LineInOuputStartsWith("docker.io/library/alpine")).To(BeTrue())
+ Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue())
+ })
+
+ It("podman images in JSON format", func() {
+ session := podmanTest.Podman([]string{"images", "--format=json"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ })
+
+ It("podman images with short options", func() {
+ session := podmanTest.Podman([]string{"images", "-qn"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2))
+ })
+})
diff --git a/test/e2e/import_test.go b/test/e2e/import_test.go
new file mode 100644
index 000000000..23208ca62
--- /dev/null
+++ b/test/e2e/import_test.go
@@ -0,0 +1,107 @@
+package integration
+
+import (
+ "os"
+ "path/filepath"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman import", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ })
+
+ It("podman import with source and reference", func() {
+ outfile := filepath.Join(podmanTest.TempDir, "container.tar")
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ export := podmanTest.Podman([]string{"export", "-o", outfile, cid})
+ export.WaitWithDefaultTimeout()
+ Expect(export.ExitCode()).To(Equal(0))
+
+ importImage := podmanTest.Podman([]string{"import", outfile, "foobar.com/imported-image:latest"})
+ importImage.WaitWithDefaultTimeout()
+ Expect(importImage.ExitCode()).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"inspect", "--type", "image", "foobar.com/imported-image:latest"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ })
+
+ It("podman import without reference", func() {
+ outfile := filepath.Join(podmanTest.TempDir, "container.tar")
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ export := podmanTest.Podman([]string{"export", "-o", outfile, cid})
+ export.WaitWithDefaultTimeout()
+ Expect(export.ExitCode()).To(Equal(0))
+
+ importImage := podmanTest.Podman([]string{"import", outfile})
+ importImage.WaitWithDefaultTimeout()
+ Expect(importImage.ExitCode()).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"images", "-q"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ Expect(len(results.OutputToStringArray())).To(Equal(4))
+ })
+
+ It("podman import with message flag", func() {
+ outfile := filepath.Join(podmanTest.TempDir, "container.tar")
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ export := podmanTest.Podman([]string{"export", "-o", outfile, cid})
+ export.WaitWithDefaultTimeout()
+ Expect(export.ExitCode()).To(Equal(0))
+
+ importImage := podmanTest.Podman([]string{"import", "--message", "importing container test message", outfile, "imported-image"})
+ importImage.WaitWithDefaultTimeout()
+ Expect(importImage.ExitCode()).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"history", "imported-image", "--format", "{{.Comment}}"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ Expect(results.LineInOuputStartsWith("importing container test message")).To(BeTrue())
+ })
+
+ It("podman import with change flag", func() {
+ outfile := filepath.Join(podmanTest.TempDir, "container.tar")
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ export := podmanTest.Podman([]string{"export", "-o", outfile, cid})
+ export.WaitWithDefaultTimeout()
+ Expect(export.ExitCode()).To(Equal(0))
+
+ importImage := podmanTest.Podman([]string{"import", "--change", "CMD=/bin/bash", outfile, "imported-image"})
+ importImage.WaitWithDefaultTimeout()
+ Expect(importImage.ExitCode()).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"inspect", "imported-image"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ imageData := results.InspectImageJSON()
+ Expect(imageData.Config.Cmd[0]).To(Equal("/bin/bash"))
+ })
+
+})
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
new file mode 100644
index 000000000..b6020f53b
--- /dev/null
+++ b/test/e2e/inspect_test.go
@@ -0,0 +1,75 @@
+package integration
+
+import (
+ "os"
+
+ "strings"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman inspect", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+
+ })
+
+ It("podman inspect alpine image", func() {
+ session := podmanTest.Podman([]string{"inspect", "--format=json", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ imageData := session.InspectImageJSON()
+ Expect(imageData.RepoTags[0]).To(Equal("docker.io/library/alpine:latest"))
+ })
+
+ It("podman inspect bogus container", func() {
+ session := podmanTest.Podman([]string{"inspect", "foobar4321"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman inspect with GO format", func() {
+ session := podmanTest.Podman([]string{"inspect", "--format", "{{.ID}}", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"images", "-q", "--no-trunc", ALPINE})
+ result.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(strings.Trim(result.OutputToString(), "sha256:")).To(Equal(session.OutputToString()))
+ })
+
+ It("podman inspect specified type", func() {
+ session := podmanTest.Podman([]string{"inspect", "--type", "image", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman inspect container with size", func() {
+ _, ec, _ := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"inspect", "--size", "-l"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(conData.SizeRootFs).To(BeNumerically(">", 0))
+ })
+})
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 27848517f..712af7236 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -413,3 +413,14 @@ func StringInSlice(s string, sl []string) bool {
}
return false
}
+
+//LineInOutputStartsWith returns true if a line in a
+// session output starts with the supplied string
+func (s *PodmanSession) LineInOuputStartsWith(term string) bool {
+ for _, i := range s.OutputToStringArray() {
+ if strings.HasPrefix(i, term) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
new file mode 100644
index 000000000..d8fc440c0
--- /dev/null
+++ b/test/e2e/logs_test.go
@@ -0,0 +1,60 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman logs", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+
+ })
+
+ It("podman logs for container", func() {
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"logs", cid})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ })
+
+ It("podman logs tail three lines", func() {
+ Skip("Tail is not working correctly")
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"logs", "--tail", "3", cid})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ Expect(len(results.OutputToStringArray())).To(Equal(3))
+ })
+
+ It("podman logs since a given time", func() {
+ _, ec, cid := podmanTest.RunLsContainer("")
+ Expect(ec).To(Equal(0))
+
+ results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ })
+
+})
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
new file mode 100644
index 000000000..27ca1e556
--- /dev/null
+++ b/test/e2e/run_dns_test.go
@@ -0,0 +1,82 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman run dns", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+
+ })
+
+ It("podman run add search domain", func() {
+ session := podmanTest.Podman([]string{"run", "--dns-search=foobar.com", ALPINE, "cat", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOuputStartsWith("search foobar.com")
+ })
+
+ It("podman run add bad dns server", func() {
+ session := podmanTest.Podman([]string{"run", "--dns=foobar", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman run add dns server", func() {
+ session := podmanTest.Podman([]string{"run", "--dns=1.2.3.4", ALPINE, "cat", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOuputStartsWith("server 1.2.3.4")
+ })
+
+ It("podman run add dns option", func() {
+ session := podmanTest.Podman([]string{"run", "--dns-opt=debug", ALPINE, "cat", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOuputStartsWith("options debug")
+ })
+
+ It("podman run add bad host", func() {
+ session := podmanTest.Podman([]string{"run", "--add-host=foo:1.2", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman run add host", func() {
+ session := podmanTest.Podman([]string{"run", "--add-host=foobar:1.1.1.1", ALPINE, "cat", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOuputStartsWith("foobar 1.1.1.1")
+ })
+
+ It("podman run add hostname", func() {
+ session := podmanTest.Podman([]string{"run", "--hostname=foobar", ALPINE, "cat", "/etc/hostname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("foobar"))
+
+ session = podmanTest.Podman([]string{"run", "--hostname=foobar", ALPINE, "hostname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("foobar"))
+ })
+})
diff --git a/test/podman_images.bats b/test/podman_images.bats
deleted file mode 100644
index 5812e8f8b..000000000
--- a/test/podman_images.bats
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bats
-
-load helpers
-
-function teardown() {
- cleanup_test
-}
-
-function setup() {
- copy_images
-}
-@test "podman images" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} images
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "podman images test valid json" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} images --format json
- echo "$output" | python -m json.tool
- [ "$status" -eq 0 ]
-}
-
-@test "podman images check name json output" {
- ${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi -fa
- ${PODMAN_BINARY} ${PODMAN_OPTIONS} pull ${ALPINE}
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} images --format json
- [ "$status" -eq 0 ]
- name=$(echo $output | python -c 'import sys; import json; print(json.loads(sys.stdin.read())[0])["names"][0]')
- [ "$name" == "docker.io/library/alpine:latest" ]
-}
-
-@test "podman images short options" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} images -qn
- echo "$output"
- [ "$status" -eq 0 ]
-}
diff --git a/test/podman_import.bats b/test/podman_import.bats
deleted file mode 100644
index 69c704a68..000000000
--- a/test/podman_import.bats
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env bats
-
-load helpers
-
-function teardown() {
- cleanup_test
-}
-
-function setup() {
- copy_images
-}
-
-@test "podman import with source and reference" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $ALPINE sleep 60"
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run bash -cp "${PODMAN_BINARY} ${PODMAN_OPTIONS} export -o container.tar $ctr_id"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} import container.tar imported-image"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} images"
- echo "$output"
- [ "$status" -eq 0 ]
- [[ "$output" == *"imported-image"* ]]
- rm -f container.tar
-}
-
-@test "podman import without reference" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $ALPINE sleep 60"
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} export -o container.tar $ctr_id"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} import container.tar"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} images"
- echo "$output"
- [ "$status" -eq 0 ]
- [[ "$output" == *"<none>"* ]]
- rm -f container.tar
-}
-
-@test "podman import with message flag" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $ALPINE sleep 60"
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} export -o container.tar $ctr_id"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} import --message 'importing container test message' container.tar imported-image"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} history imported-image"
- echo "$output"
- [ "$status" -eq 0 ]
- [[ "$output" == *"importing container test message"* ]]
- rm -f container.tar
-}
-
-@test "podman import with change flag" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $ALPINE sleep 60"
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} export -o container.tar $ctr_id"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} import --change 'CMD=/bin/bash' container.tar imported-image"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} inspect imported-image"
- echo "$output"
- [ "$status" -eq 0 ]
- [[ "$output" == *"/bin/bash"* ]]
- rm -f container.tar
-}
diff --git a/test/podman_inspect.bats b/test/podman_inspect.bats
deleted file mode 100644
index 19e5a0a9b..000000000
--- a/test/podman_inspect.bats
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env bats
-
-load helpers
-
-function teardown() {
- cleanup_test
-}
-
-function setup() {
- copy_images
-}
-
-@test "podman inspect image" {
- run bash -c "${PODMAN_BINARY} $PODMAN_OPTIONS inspect ${ALPINE} | python -m json.tool"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "podman inspect non-existent container" {
- run ${PODMAN_BINARY} $PODMAN_OPTIONS inspect 14rcole/non-existent
- echo "$output"
- [ "$status" -ne 0 ]
-}
-
-@test "podman inspect with format" {
- run ${PODMAN_BINARY} $PODMAN_OPTIONS inspect --format {{.ID}} ${ALPINE}
- echo "$output"
- [ "$status" -eq 0 ]
- inspectOutput="$output"
- bash -c run ${PODMAN_BINARY} $PODMAN_OPTIONS images --no-trunc --quiet ${ALPINE} | sed -e 's/sha256://g'
- echo "$output"
- [ "$status" -eq 0 ]
- [ "$output" = "$inspectOutput" ]
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "podman inspect specified type" {
- run bash -c "${PODMAN_BINARY} $PODMAN_OPTIONS inspect --type image ${ALPINE} | python -m json.tool"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "podman inspect container with size" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} create ${BB} ls
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} $PODMAN_OPTIONS inspect --size -l | python -m json.tool | grep SizeRootFs"
- echo "$output"
- [ "$status" -eq 0 ]
-}
diff --git a/test/podman_logs.bats b/test/podman_logs.bats
deleted file mode 100644
index e76bf665a..000000000
--- a/test/podman_logs.bats
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env bats
-
-load helpers
-
-function teardown() {
- cleanup_test
-}
-
-function setup() {
- copy_images
-}
-
-@test "display logs for container" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $BB ls
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} logs $ctr_id
- echo "$output"
- [ "$status" -eq 0 ]
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} rm $ctr_id
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "tail three lines of logs for container" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $BB ls
- echo "$output"
- [ "$status" -eq 0 ]
- ctr_id="$output"
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} logs --tail 3 $ctr_id
- echo "$output"
- lines=$(echo "$output" | wc -l)
- [ "$status" -eq 0 ]
- [[ $(wc -l < "$output" ) -le 3 ]]
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} rm $ctr_id
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "display logs for container since a given time" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d $BB ls
- echo "$output"
- [ "$status" -eq 0 ]
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} logs --since 2017-08-07T10:10:09.056611202-04:00 -l
- echo "$output"
- [ "$status" -eq 0 ]
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} rm -l
- echo "$output"
- [ "$status" -eq 0 ]
-}
diff --git a/test/podman_run_dns.bats b/test/podman_run_dns.bats
deleted file mode 100644
index d37737093..000000000
--- a/test/podman_run_dns.bats
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env bats
-
-load helpers
-
-function teardown() {
- cleanup_test
-}
-
-function setup() {
- copy_images
-}
-
-@test "test addition of a search domain" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --dns-search=foobar.com ${ALPINE} cat /etc/resolv.conf | grep foo"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "test addition of a bad dns server" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} create --dns="foo" ${ALPINE} ls
- echo "$output"
- [ "$status" -ne 0 ]
-}
-
-@test "test addition of a dns server" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --dns='1.2.3.4' ${ALPINE} cat /etc/resolv.conf | grep '1.2.3.4'"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "test addition of a dns option" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --dns-opt='debug' ${ALPINE} cat /etc/resolv.conf | grep 'options debug'"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "test addition of a bad add-host" {
- run ${PODMAN_BINARY} ${PODMAN_OPTIONS} create --add-host="foo:1.2" ${ALPINE} ls
- echo "$output"
- [ "$status" -ne 0 ]
-}
-
-@test "test addition of add-host" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --add-host='foobar:1.1.1.1' ${ALPINE} cat /etc/hosts | grep 'foobar'"
- echo "$output"
- [ "$status" -eq 0 ]
-}
-
-@test "test addition of hostname" {
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --hostname='foobar' ${ALPINE} cat /etc/hostname | grep foobar"
- echo "$output"
- [ "$status" -eq 0 ]
- run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run --rm --hostname='foobar' ${ALPINE} hostname | grep foobar"
- echo "$output"
- [ "$status" -eq 0 ]
-}
diff --git a/test/podman_search.bats b/test/podman_search.bats
new file mode 100644
index 000000000..07621d722
--- /dev/null
+++ b/test/podman_search.bats
@@ -0,0 +1,43 @@
+#!/usr/bin/env bats
+
+load helpers
+
+function teardown() {
+ cleanup_test
+}
+
+@test "podman search" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search alpine
+ echo "$output"
+ [ "$status" -eq 0 ]
+}
+
+@test "podman search registry flag" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search --registry registry.fedoraproject.org fedora
+ echo "$output"
+ [ "$status" -eq 0 ]
+}
+
+@test "podman search filter flag" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search --filter=is-official alpine
+ echo "$output"
+ [ "$status" -eq 0 ]
+}
+
+@test "podman search format flag" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search --format "table {{.Index}} {{.Name}}" alpine
+ echo "$output"
+ [ "$status" -eq 0 ]
+}
+
+@test "podman search no-trunc flag" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search --no-trunc alpine
+ echo "$output"
+ [ "$status" -eq 0 ]
+}
+
+@test "podman search limit flag" {
+ run ${PODMAN_BINARY} ${PODMAN_OPTIONS} search --limit 3 alpine
+ echo "$output"
+ [ "$status" -eq 0 ]
+} \ No newline at end of file
diff --git a/transfer.md b/transfer.md
index 30e277a86..53d6c472c 100644
--- a/transfer.md
+++ b/transfer.md
@@ -60,6 +60,7 @@ There are other equivalents for these tools
| `docker rmi` | [`podman rmi`](./docs/podman-rmi.1.md) |
| `docker run` | [`podman run`](./docs/podman-run.1.md) |
| `docker save` | [`podman save`](./docs/podman-save.1.md) |
+| `docker search` | [`podman search`](./docs/podman-search.1.md) |
| `docker start` | [`podman start`](./docs/podman-start.1.md) |
| `docker stop` | [`podman stop`](./docs/podman-stop.1.md) |
| `docker tag` | [`podman tag`](./docs/podman-tag.1.md) |
@@ -85,7 +86,6 @@ Those Docker commands currently do not have equivalents in `podman`:
| `docker port` ||
| `docker rename` | podman does not support rename, you need to use `podman rm` and `podman create` to rename a container.|
| `docker restart` | podman does not support restart. We recommend that you put your podman containers into a systemd unit file and use it for restarting applications.|
-| `docker search` ||
| `docker secret` ||
| `docker service` ||
| `docker stack` ||
diff --git a/vendor.conf b/vendor.conf
index 4ce659199..ea659eab8 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -1,6 +1,6 @@
#
github.com/sirupsen/logrus v1.0.0
-github.com/containers/image 9b4510f6d1627c8e53c3303a8fe48ca7842c2ace
+github.com/containers/image 3ab2e31e6ff9fc2b21b81188c1f6cf545658ff4a
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/ostreedev/ostree-go master
github.com/containers/storage 1824cf917a6b42d8c41179e807bb20a5fd6c0f0a
diff --git a/vendor/github.com/containers/image/directory/directory_dest.go b/vendor/github.com/containers/image/directory/directory_dest.go
index 47d59d9fe..5f7443fa0 100644
--- a/vendor/github.com/containers/image/directory/directory_dest.go
+++ b/vendor/github.com/containers/image/directory/directory_dest.go
@@ -70,7 +70,7 @@ func newImageDestination(ref dirReference, compress bool) (types.ImageDestinatio
}
}
// create version file
- err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0755)
+ err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0644)
if err != nil {
return nil, errors.Wrapf(err, "error creating version file %q", d.ref.versionPath())
}
diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go
index 217e9dcbf..ff1af8f65 100644
--- a/vendor/github.com/containers/image/docker/docker_client.go
+++ b/vendor/github.com/containers/image/docker/docker_client.go
@@ -8,7 +8,10 @@ import (
"io"
"io/ioutil"
"net/http"
+ "net/url"
+ "os"
"path/filepath"
+ "strconv"
"strings"
"time"
@@ -24,10 +27,9 @@ import (
)
const (
- dockerHostname = "docker.io"
- dockerRegistry = "registry-1.docker.io"
-
- systemPerHostCertDirPath = "/etc/docker/certs.d"
+ dockerHostname = "docker.io"
+ dockerV1Hostname = "index.docker.io"
+ dockerRegistry = "registry-1.docker.io"
resolvedPingV2URL = "%s://%s/v2/"
resolvedPingV1URL = "%s://%s/v1/_ping"
@@ -49,6 +51,7 @@ var (
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
// ErrUnauthorizedForCredentials is returned when the status code returned is 401
ErrUnauthorizedForCredentials = errors.New("unable to retrieve auth token: invalid username/password")
+ systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
)
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
@@ -66,9 +69,10 @@ type extensionSignatureList struct {
}
type bearerToken struct {
- Token string `json:"token"`
- ExpiresIn int `json:"expires_in"`
- IssuedAt time.Time `json:"issued_at"`
+ Token string `json:"token"`
+ AccessToken string `json:"access_token"`
+ ExpiresIn int `json:"expires_in"`
+ IssuedAt time.Time `json:"issued_at"`
}
// dockerClient is configuration for dealing with a single Docker registry.
@@ -96,6 +100,24 @@ type authScope struct {
actions string
}
+func newBearerTokenFromJSONBlob(blob []byte) (*bearerToken, error) {
+ token := new(bearerToken)
+ if err := json.Unmarshal(blob, &token); err != nil {
+ return nil, err
+ }
+ if token.Token == "" {
+ token.Token = token.AccessToken
+ }
+ if token.ExpiresIn < minimumTokenLifetimeSeconds {
+ token.ExpiresIn = minimumTokenLifetimeSeconds
+ logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
+ }
+ if token.IssuedAt.IsZero() {
+ token.IssuedAt = time.Now().UTC()
+ }
+ return token, nil
+}
+
// this is cloned from docker/go-connections because upstream docker has changed
// it and make deps here fails otherwise.
// We'll drop this once we upgrade to docker 1.13.x deps.
@@ -109,19 +131,42 @@ func serverDefault() *tls.Config {
}
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
-func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
+func dockerCertDir(ctx *types.SystemContext, hostPort string) (string, error) {
if ctx != nil && ctx.DockerCertPath != "" {
- return ctx.DockerCertPath
+ return ctx.DockerCertPath, nil
}
- var hostCertDir string
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
- hostCertDir = ctx.DockerPerHostCertDirPath
- } else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
- hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
- } else {
- hostCertDir = systemPerHostCertDirPath
+ return filepath.Join(ctx.DockerPerHostCertDirPath, hostPort), nil
+ }
+
+ var (
+ hostCertDir string
+ fullCertDirPath string
+ )
+ for _, systemPerHostCertDirPath := range systemPerHostCertDirPaths {
+ if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
+ hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
+ } else {
+ hostCertDir = systemPerHostCertDirPath
+ }
+
+ fullCertDirPath = filepath.Join(hostCertDir, hostPort)
+ _, err := os.Stat(fullCertDirPath)
+ if err == nil {
+ break
+ }
+ if os.IsNotExist(err) {
+ continue
+ }
+ if os.IsPermission(err) {
+ logrus.Debugf("error accessing certs directory due to permissions: %v", err)
+ continue
+ }
+ if err != nil {
+ return "", err
+ }
}
- return filepath.Join(hostCertDir, hostPort)
+ return fullCertDirPath, nil
}
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
@@ -155,7 +200,10 @@ func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, pa
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
// generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
// undocumented and may change if docker/docker changes.
- certDir := dockerCertDir(ctx, hostName)
+ certDir, err := dockerCertDir(ctx, hostName)
+ if err != nil {
+ return nil, err
+ }
if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil {
return nil, err
}
@@ -202,6 +250,100 @@ func CheckAuth(ctx context.Context, sCtx *types.SystemContext, username, passwor
}
}
+// SearchResult holds the information of each matching image
+// It matches the output returned by the v1 endpoint
+type SearchResult struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ // StarCount states the number of stars the image has
+ StarCount int `json:"star_count"`
+ IsTrusted bool `json:"is_trusted"`
+ // IsAutomated states whether the image is an automated build
+ IsAutomated bool `json:"is_automated"`
+ // IsOfficial states whether the image is an official build
+ IsOfficial bool `json:"is_official"`
+}
+
+// SearchRegistry queries a registry for images that contain "image" in their name
+// The limit is the max number of results desired
+// Note: The limit value doesn't work with all registries
+// for example registry.access.redhat.com returns all the results without limiting it to the limit value
+func SearchRegistry(ctx context.Context, sCtx *types.SystemContext, registry, image string, limit int) ([]SearchResult, error) {
+ type V2Results struct {
+ // Repositories holds the results returned by the /v2/_catalog endpoint
+ Repositories []string `json:"repositories"`
+ }
+ type V1Results struct {
+ // Results holds the results returned by the /v1/search endpoint
+ Results []SearchResult `json:"results"`
+ }
+ v2Res := &V2Results{}
+ v1Res := &V1Results{}
+
+ // The /v2/_catalog endpoint has been disabled for docker.io therefore the call made to that endpoint will fail
+ // So using the v1 hostname for docker.io for simplicity of implementation and the fact that it returns search results
+ if registry == dockerHostname {
+ registry = dockerV1Hostname
+ }
+
+ client, err := newDockerClientWithDetails(sCtx, registry, "", "", "", nil, "")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error creating new docker client")
+ }
+
+ logrus.Debugf("trying to talk to v2 search endpoint\n")
+ resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil)
+ if err != nil {
+ logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
+ } else {
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ logrus.Debugf("error getting search results from v2 endpoint %q, status code %q", registry, resp.StatusCode)
+ } else {
+ if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
+ return nil, err
+ }
+ searchRes := []SearchResult{}
+ for _, repo := range v2Res.Repositories {
+ if strings.Contains(repo, image) {
+ res := SearchResult{
+ Name: repo,
+ }
+ searchRes = append(searchRes, res)
+ }
+ }
+ return searchRes, nil
+ }
+ }
+
+ // set up the query values for the v1 endpoint
+ u := url.URL{
+ Path: "/v1/search",
+ }
+ q := u.Query()
+ q.Set("q", image)
+ q.Set("n", strconv.Itoa(limit))
+ u.RawQuery = q.Encode()
+
+ logrus.Debugf("trying to talk to v1 search endpoint\n")
+ resp, err = client.makeRequest(ctx, "GET", u.String(), nil, nil)
+ if err != nil {
+ logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
+ } else {
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ logrus.Debugf("error getting search results from v1 endpoint %q, status code %q", registry, resp.StatusCode)
+ } else {
+ if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
+ return nil, err
+ }
+ return v1Res.Results, nil
+ }
+ }
+
+ return nil, errors.Wrapf(err, "couldn't search registry %q", registry)
+}
+
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/.
func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
@@ -332,18 +474,8 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope
if err != nil {
return nil, err
}
- var token bearerToken
- if err := json.Unmarshal(tokenBlob, &token); err != nil {
- return nil, err
- }
- if token.ExpiresIn < minimumTokenLifetimeSeconds {
- token.ExpiresIn = minimumTokenLifetimeSeconds
- logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
- }
- if token.IssuedAt.IsZero() {
- token.IssuedAt = time.Now().UTC()
- }
- return &token, nil
+
+ return newBearerTokenFromJSONBlob(tokenBlob)
}
// detectProperties detects various properties of the registry.
diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go
index 79c386225..2f3b6c2c3 100644
--- a/vendor/github.com/containers/image/docker/docker_image_dest.go
+++ b/vendor/github.com/containers/image/docker/docker_image_dest.go
@@ -131,7 +131,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
defer res.Body.Close()
if res.StatusCode != http.StatusAccepted {
logrus.Debugf("Error initiating layer upload, response %#v", *res)
- return types.BlobInfo{}, errors.Errorf("Error initiating layer upload to %s, status %d", uploadPath, res.StatusCode)
+ return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error initiating layer upload to %s", uploadPath)
}
uploadLocation, err := res.Location()
if err != nil {
@@ -167,7 +167,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
logrus.Debugf("Error uploading layer, response %#v", *res)
- return types.BlobInfo{}, errors.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode)
+ return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer to %s", uploadLocation)
}
logrus.Debugf("Upload of layer %s complete", computedDigest)
@@ -196,7 +196,7 @@ func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, erro
return true, getBlobSize(res), nil
case http.StatusUnauthorized:
logrus.Debugf("... not authorized")
- return false, -1, errors.Errorf("not authorized to read from destination repository %s", reference.Path(d.ref.ref))
+ return false, -1, client.HandleErrorResponse(res)
case http.StatusNotFound:
logrus.Debugf("... not present")
return false, -1, nil
@@ -447,7 +447,7 @@ sigExists:
logrus.Debugf("Error body %s", string(body))
}
logrus.Debugf("Error uploading signature, status %d, %#v", res.StatusCode, res)
- return errors.Errorf("Error uploading signature to %s, status %d", path, res.StatusCode)
+ return errors.Wrapf(client.HandleErrorResponse(res), "Error uploading signature to %s", path)
}
}
diff --git a/vendor/github.com/containers/image/image/oci.go b/vendor/github.com/containers/image/image/oci.go
index 3c03e49bb..e7780c5a6 100644
--- a/vendor/github.com/containers/image/image/oci.go
+++ b/vendor/github.com/containers/image/image/oci.go
@@ -149,6 +149,16 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
switch options.ManifestMIMEType {
case "": // No conversion, OK
+ case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
+ // We can't directly convert to V1, but we can transitively convert via a V2 image
+ m2, err := copy.convertToManifestSchema2()
+ if err != nil {
+ return nil, err
+ }
+ return m2.UpdatedImage(types.ManifestUpdateOptions{
+ ManifestMIMEType: options.ManifestMIMEType,
+ InformationOnly: options.InformationOnly,
+ })
case manifest.DockerV2Schema2MediaType:
return copy.convertToManifestSchema2()
default:
diff --git a/vendor/github.com/containers/image/ostree/ostree_dest.go b/vendor/github.com/containers/image/ostree/ostree_dest.go
index 8154c9851..d5f0ff80c 100644
--- a/vendor/github.com/containers/image/ostree/ostree_dest.go
+++ b/vendor/github.com/containers/image/ostree/ostree_dest.go
@@ -14,6 +14,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
+ "strings"
"syscall"
"time"
"unsafe"
@@ -175,7 +176,10 @@ func fixFiles(selinuxHnd *C.struct_selabel_handle, root string, dir string, user
if err != nil {
return err
}
- relPath = fmt.Sprintf("/%s", relPath)
+ // Handle /exports/hostfs as a special case. Files under this directory are copied to the host,
+ // thus we benefit from maintaining the same SELinux label they would have on the host as we could
+ // use hard links instead of copying the files.
+ relPath = fmt.Sprintf("/%s", strings.TrimPrefix(relPath, "exports/hostfs/"))
relPathC := C.CString(relPath)
defer C.free(unsafe.Pointer(relPathC))
@@ -237,7 +241,7 @@ func generateTarSplitMetadata(output *bytes.Buffer, file string) error {
}
defer stream.Close()
- gzReader, err := gzip.NewReader(stream)
+ gzReader, err := archive.DecompressStream(stream)
if err != nil {
return err
}
@@ -383,7 +387,7 @@ func (d *ostreeImageDestination) Commit() error {
var selinuxHnd *C.struct_selabel_handle
if os.Getuid() == 0 && selinux.GetEnabled() {
- selinuxHnd, err := C.selabel_open(C.SELABEL_CTX_FILE, nil, 0)
+ selinuxHnd, err = C.selabel_open(C.SELABEL_CTX_FILE, nil, 0)
if selinuxHnd == nil {
return errors.Wrapf(err, "cannot open the SELinux DB")
}
diff --git a/vendor/github.com/containers/image/tarball/tarball_reference.go b/vendor/github.com/containers/image/tarball/tarball_reference.go
index 4ccfb4063..a0819ac58 100644
--- a/vendor/github.com/containers/image/tarball/tarball_reference.go
+++ b/vendor/github.com/containers/image/tarball/tarball_reference.go
@@ -89,5 +89,5 @@ func (r *tarballReference) DeleteImage(ctx *types.SystemContext) error {
}
func (r *tarballReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
- return nil, fmt.Errorf("destination not implemented yet")
+ return nil, fmt.Errorf(`"tarball:" locations can only be read from, not written to`)
}