mirror of https://github.com/containers/podman.git
Added --tls-verify functionality to podman search, with tests
Signed-off-by: haircommander <pehunt@redhat.com> Closes: #932 Approved by: baude
This commit is contained in:
parent
894ae2bf76
commit
b43677c9fd
|
@ -2,16 +2,19 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/docker"
|
"github.com/containers/image/docker"
|
||||||
|
"github.com/containers/image/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectatomic/libpod/cmd/podman/formats"
|
"github.com/projectatomic/libpod/cmd/podman/formats"
|
||||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||||
"github.com/projectatomic/libpod/libpod/common"
|
"github.com/projectatomic/libpod/libpod/common"
|
||||||
sysreg "github.com/projectatomic/libpod/pkg/registries"
|
sysreg "github.com/projectatomic/libpod/pkg/registries"
|
||||||
|
"github.com/projectatomic/libpod/pkg/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -43,6 +46,10 @@ var (
|
||||||
Name: "registry",
|
Name: "registry",
|
||||||
Usage: "specific registry to search",
|
Usage: "specific registry to search",
|
||||||
},
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "tls-verify",
|
||||||
|
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
searchDescription = `
|
searchDescription = `
|
||||||
Search registries for a given image. Can search all the default registries or a specific registry.
|
Search registries for a given image. Can search all the default registries or a specific registry.
|
||||||
|
@ -106,15 +113,9 @@ func searchCmd(c *cli.Context) error {
|
||||||
limit: c.Int("limit"),
|
limit: c.Int("limit"),
|
||||||
filter: c.StringSlice("filter"),
|
filter: c.StringSlice("filter"),
|
||||||
}
|
}
|
||||||
|
registries, sc, err := getSystemContextAndRegistries(c)
|
||||||
var registries []string
|
if err != nil {
|
||||||
if len(c.StringSlice("registry")) > 0 {
|
return err
|
||||||
registries = c.StringSlice("registry")
|
|
||||||
} else {
|
|
||||||
registries, err = sysreg.GetRegistries()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error getting registries to search")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filter, err := parseSearchFilter(&opts)
|
filter, err := parseSearchFilter(&opts)
|
||||||
|
@ -122,7 +123,7 @@ func searchCmd(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return generateSearchOutput(term, registries, opts, *filter)
|
return generateSearchOutput(term, registries, opts, *filter, sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func genSearchFormat(format string) string {
|
func genSearchFormat(format string) string {
|
||||||
|
@ -153,8 +154,54 @@ func (s *searchParams) headerMap() map[string]string {
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) ([]searchParams, error) {
|
// A wrapper for GetSystemContext and GetInsecureRegistries
|
||||||
|
// Sets up system context and active list of registries to search with
|
||||||
|
func getSystemContextAndRegistries(c *cli.Context) ([]string, *types.SystemContext, error) {
|
||||||
sc := common.GetSystemContext("", "", false)
|
sc := common.GetSystemContext("", "", false)
|
||||||
|
|
||||||
|
// Variables for setting up Registry and TLSVerify
|
||||||
|
tlsVerify := c.BoolT("tls-verify")
|
||||||
|
forceSecure := false
|
||||||
|
|
||||||
|
if c.IsSet("tls-verify") {
|
||||||
|
forceSecure = c.BoolT("tls-verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
var registries []string
|
||||||
|
if len(c.StringSlice("registry")) > 0 {
|
||||||
|
registries = c.StringSlice("registry")
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
registries, err = sysreg.GetRegistries()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "error getting registries to search")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user flagged to skip verify for HTTP connections, set System Context as such
|
||||||
|
if !tlsVerify {
|
||||||
|
// If tls-verify is set to false, allow insecure always.
|
||||||
|
sc.DockerInsecureSkipTLSVerify = true
|
||||||
|
} else if !forceSecure {
|
||||||
|
// if the user didn't allow nor disallow insecure registries, check to see if the registry is insecure
|
||||||
|
insecureRegistries, err := sysreg.GetInsecureRegistries()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "error getting insecure registries to search")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, reg := range insecureRegistries {
|
||||||
|
// if there are any insecure registries in registries, allow for HTTP
|
||||||
|
if util.StringInSlice(reg, registries) {
|
||||||
|
sc.DockerInsecureSkipTLSVerify = true
|
||||||
|
logrus.Info(fmt.Sprintf("%s is an insecure registry; searching with tls-verify=false", reg))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return registries, sc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams, sc *types.SystemContext) ([]searchParams, error) {
|
||||||
// Max number of queries by default is 25
|
// Max number of queries by default is 25
|
||||||
limit := maxQueries
|
limit := maxQueries
|
||||||
if opts.limit != 0 {
|
if opts.limit != 0 {
|
||||||
|
@ -222,8 +269,8 @@ func getSearchOutput(term string, registries []string, opts searchOpts, filter s
|
||||||
return paramsArr, nil
|
return paramsArr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) error {
|
func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams, sc *types.SystemContext) error {
|
||||||
searchOutput, err := getSearchOutput(term, registries, opts, filter)
|
searchOutput, err := getSearchOutput(term, registries, opts, filter, sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,12 @@ Do not truncate the output
|
||||||
|
|
||||||
Specific registry to search (only the given registry will be searched, not the default registries)
|
Specific registry to search (only the given registry will be searched, not the default registries)
|
||||||
|
|
||||||
|
**--tls-verify**
|
||||||
|
|
||||||
|
Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true,
|
||||||
|
then tls verification will be used, If set to false then tls verification will not be used. If not specified
|
||||||
|
both insecured and default registries will be searched through, and tls will be used when possible.
|
||||||
|
|
||||||
## EXAMPLES
|
## EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,6 +2,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -12,8 +13,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/containers/image/copy"
|
"github.com/containers/image/copy"
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
"github.com/containers/image/storage"
|
"github.com/containers/image/storage"
|
||||||
|
@ -268,6 +267,36 @@ func (s *PodmanSession) OutputToStringArray() []string {
|
||||||
return strings.Split(output, "\n")
|
return strings.Split(output, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorGrepString takes session stderr output and behaves like grep. it returns a bool
|
||||||
|
// if successful and an array of strings on positive matches
|
||||||
|
func (s *PodmanSession) ErrorGrepString(term string) (bool, []string) {
|
||||||
|
var (
|
||||||
|
greps []string
|
||||||
|
matches bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, line := range strings.Split(s.ErrorToString(), "\n") {
|
||||||
|
if strings.Contains(line, term) {
|
||||||
|
matches = true
|
||||||
|
greps = append(greps, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches, greps
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorToString formats session stderr to string
|
||||||
|
func (s *PodmanSession) ErrorToString() string {
|
||||||
|
fields := strings.Fields(fmt.Sprintf("%s", s.Err.Contents()))
|
||||||
|
return strings.Join(fields, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorToStringArray returns the stderr output as a []string
|
||||||
|
// where each array item is a line split by newline
|
||||||
|
func (s *PodmanSession) ErrorToStringArray() []string {
|
||||||
|
output := fmt.Sprintf("%s", s.Err.Contents())
|
||||||
|
return strings.Split(output, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
// IsJSONOutputValid attempts to unmarshal the session buffer
|
// IsJSONOutputValid attempts to unmarshal the session buffer
|
||||||
// and if successful, returns true, else false
|
// and if successful, returns true, else false
|
||||||
func (s *PodmanSession) IsJSONOutputValid() bool {
|
func (s *PodmanSession) IsJSONOutputValid() bool {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
|
@ -14,13 +16,26 @@ var _ = Describe("Podman search", func() {
|
||||||
err error
|
err error
|
||||||
podmanTest PodmanTest
|
podmanTest PodmanTest
|
||||||
)
|
)
|
||||||
|
const regFileContents = `
|
||||||
|
[registries.search]
|
||||||
|
registries = ['localhost:5000']
|
||||||
|
|
||||||
|
[registries.insecure]
|
||||||
|
registries = ['localhost:5000']`
|
||||||
|
|
||||||
|
const badRegFileContents = `
|
||||||
|
[registries.search]
|
||||||
|
registries = ['localhost:5000']
|
||||||
|
# empty
|
||||||
|
[registries.insecure]
|
||||||
|
registries = []`
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
tempdir, err = CreateTempDirInTempDir()
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
podmanTest = PodmanCreate(tempdir)
|
podmanTest = PodmanCreate(tempdir)
|
||||||
|
podmanTest.RestoreAllArtifacts()
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
|
@ -96,4 +111,133 @@ var _ = Describe("Podman search", func() {
|
||||||
Expect(output[i]).To(Equal(""))
|
Expect(output[i]).To(Equal(""))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman search attempts HTTP if tls-verify flag is set false", func() {
|
||||||
|
fakereg := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
|
||||||
|
fakereg.WaitWithDefaultTimeout()
|
||||||
|
Expect(fakereg.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
if !WaitContainerReady(&podmanTest, "registry", "listening on", 20, 1) {
|
||||||
|
Skip("Can not start docker registry.")
|
||||||
|
}
|
||||||
|
|
||||||
|
search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "fake/image:andtag", "--tls-verify=false"})
|
||||||
|
search.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
// if this test succeeded, there will be no output (there is no entry named fake/image:andtag in an empty registry)
|
||||||
|
// and the exit code will be 0
|
||||||
|
Expect(search.ExitCode()).To(Equal(0))
|
||||||
|
Expect(search.OutputToString()).Should(BeEmpty())
|
||||||
|
Expect(search.ErrorToString()).Should(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman search in local registry", func() {
|
||||||
|
registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry3", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
|
||||||
|
registry.WaitWithDefaultTimeout()
|
||||||
|
Expect(registry.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
if !WaitContainerReady(&podmanTest, "registry3", "listening on", 20, 1) {
|
||||||
|
Skip("Can not start docker registry.")
|
||||||
|
}
|
||||||
|
|
||||||
|
push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
|
push.WaitWithDefaultTimeout()
|
||||||
|
Expect(push.ExitCode()).To(Equal(0))
|
||||||
|
search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine", "--tls-verify=false"})
|
||||||
|
search.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
Expect(search.ExitCode()).To(Equal(0))
|
||||||
|
Expect(search.OutputToString()).ShouldNot(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() {
|
||||||
|
registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry4", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
|
||||||
|
registry.WaitWithDefaultTimeout()
|
||||||
|
Expect(registry.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
if !WaitContainerReady(&podmanTest, "registry4", "listening on", 20, 1) {
|
||||||
|
Skip("Can not start docker registry.")
|
||||||
|
}
|
||||||
|
|
||||||
|
push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
|
push.WaitWithDefaultTimeout()
|
||||||
|
Expect(push.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
// registries.conf set up
|
||||||
|
regFileBytes := []byte(regFileContents)
|
||||||
|
outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
|
||||||
|
ioutil.WriteFile(outfile, regFileBytes, 0644)
|
||||||
|
|
||||||
|
search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine"})
|
||||||
|
search.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
Expect(search.ExitCode()).To(Equal(0))
|
||||||
|
match, _ := search.GrepString("my-alpine")
|
||||||
|
Expect(match).Should(BeTrue())
|
||||||
|
Expect(search.ErrorToString()).Should(BeEmpty())
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", "")
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman search doesn't attempt HTTP if force secure is true", func() {
|
||||||
|
registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry5", "registry:2"})
|
||||||
|
registry.WaitWithDefaultTimeout()
|
||||||
|
Expect(registry.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
if !WaitContainerReady(&podmanTest, "registry5", "listening on", 20, 1) {
|
||||||
|
Skip("Can not start docker registry.")
|
||||||
|
}
|
||||||
|
push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
|
push.WaitWithDefaultTimeout()
|
||||||
|
Expect(push.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
// registries.conf set up
|
||||||
|
regFileBytes := []byte(regFileContents)
|
||||||
|
outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
|
||||||
|
ioutil.WriteFile(outfile, regFileBytes, 0644)
|
||||||
|
|
||||||
|
search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine", "--tls-verify=true"})
|
||||||
|
search.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
Expect(search.ExitCode()).To(Equal(0))
|
||||||
|
Expect(search.OutputToString()).Should(BeEmpty())
|
||||||
|
match, _ := search.ErrorGrepString("error")
|
||||||
|
Expect(match).Should(BeTrue())
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", "")
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
|
||||||
|
registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry6", "registry:2"})
|
||||||
|
registry.WaitWithDefaultTimeout()
|
||||||
|
Expect(registry.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
if !WaitContainerReady(&podmanTest, "registry6", "listening on", 20, 1) {
|
||||||
|
Skip("Can not start docker registry.")
|
||||||
|
}
|
||||||
|
push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
|
push.WaitWithDefaultTimeout()
|
||||||
|
Expect(push.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
// registries.conf set up
|
||||||
|
regFileBytes := []byte(badRegFileContents)
|
||||||
|
outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
|
||||||
|
ioutil.WriteFile(outfile, regFileBytes, 0644)
|
||||||
|
|
||||||
|
search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine"})
|
||||||
|
search.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
Expect(search.ExitCode()).To(Equal(0))
|
||||||
|
Expect(search.OutputToString()).Should(BeEmpty())
|
||||||
|
match, _ := search.ErrorGrepString("error")
|
||||||
|
Expect(match).Should(BeTrue())
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
os.Setenv("REGISTRIES_CONFIG_PATH", "")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue