mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
ed13c3abfb
Add a trusted flag to force the cli to resolve a tag into a digest via the notary trust library and pull by digest. On push the flag the trust flag will indicate the digest and size of a manifest should be signed and push to a notary server. If a tag is given, the cli will resolve the tag into a digest and pull by digest. After pulling, if a tag is given the cli makes a request to tag the image. Use certificate directory for notary requests Read certificates using same logic used by daemon for registry requests. Catch JSON syntax errors from Notary client When an uncaught error occurs in Notary it may show up in Docker as a JSON syntax error, causing a confusing error message to the user. Provide a generic error when a JSON syntax error occurs. Catch expiration errors and wrap in additional context. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
185 lines
4.8 KiB
Go
185 lines
4.8 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
Cli "github.com/docker/docker/cli"
|
|
"github.com/docker/docker/graph/tags"
|
|
"github.com/docker/docker/pkg/parsers"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/runconfig"
|
|
)
|
|
|
|
func (cli *DockerCli) pullImage(image string) error {
|
|
return cli.pullImageCustomOut(image, cli.out)
|
|
}
|
|
|
|
func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
|
v := url.Values{}
|
|
repos, tag := parsers.ParseRepositoryTag(image)
|
|
// pull only the image tagged 'latest' if no tag was specified
|
|
if tag == "" {
|
|
tag = tags.DEFAULTTAG
|
|
}
|
|
v.Set("fromImage", repos)
|
|
v.Set("tag", tag)
|
|
|
|
// Resolve the Repository name from fqn to RepositoryInfo
|
|
repoInfo, err := registry.ParseRepositoryInfo(repos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Resolve the Auth config relevant for this server
|
|
authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
|
|
buf, err := json.Marshal(authConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
registryAuthHeader := []string{
|
|
base64.URLEncoding.EncodeToString(buf),
|
|
}
|
|
sopts := &streamOpts{
|
|
rawTerminal: true,
|
|
out: out,
|
|
headers: map[string][]string{"X-Registry-Auth": registryAuthHeader},
|
|
}
|
|
if _, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type cidFile struct {
|
|
path string
|
|
file *os.File
|
|
written bool
|
|
}
|
|
|
|
func newCIDFile(path string) (*cidFile, error) {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
|
}
|
|
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
|
}
|
|
|
|
return &cidFile{path: path, file: f}, nil
|
|
}
|
|
|
|
func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
containerValues := url.Values{}
|
|
if name != "" {
|
|
containerValues.Set("name", name)
|
|
}
|
|
|
|
mergedConfig := runconfig.MergeConfigs(config, hostConfig)
|
|
|
|
var containerIDFile *cidFile
|
|
if cidfile != "" {
|
|
var err error
|
|
if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
|
return nil, err
|
|
}
|
|
defer containerIDFile.Close()
|
|
}
|
|
|
|
repo, tag := parsers.ParseRepositoryTag(config.Image)
|
|
if tag == "" {
|
|
tag = tags.DEFAULTTAG
|
|
}
|
|
|
|
ref := registry.ParseReference(tag)
|
|
var trustedRef registry.Reference
|
|
|
|
if isTrusted() && !ref.HasDigest() {
|
|
var err error
|
|
trustedRef, err = cli.trustedReference(repo, ref)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.Image = trustedRef.ImageName(repo)
|
|
}
|
|
|
|
//create the container
|
|
serverResp, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
|
|
//if image not found try to pull it
|
|
if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
|
|
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.ImageName(repo))
|
|
|
|
// we don't want to write to stdout anything apart from container.ID
|
|
if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
|
|
return nil, err
|
|
}
|
|
if trustedRef != nil && !ref.HasDigest() {
|
|
repoInfo, err := registry.ParseRepositoryInfo(repo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := cli.tagTrusted(repoInfo, trustedRef, ref); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// Retry
|
|
if serverResp, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
|
|
return nil, err
|
|
}
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer serverResp.body.Close()
|
|
|
|
var response types.ContainerCreateResponse
|
|
if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, warning := range response.Warnings {
|
|
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
|
}
|
|
if containerIDFile != nil {
|
|
if err = containerIDFile.Write(response.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &response, nil
|
|
}
|
|
|
|
// CmdCreate creates a new container from a given image.
|
|
//
|
|
// Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
|
|
func (cli *DockerCli) CmdCreate(args ...string) error {
|
|
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
|
|
addTrustedFlags(cmd, true)
|
|
|
|
// These are flags not stored in Config/HostConfig
|
|
var (
|
|
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
)
|
|
|
|
config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
|
|
if err != nil {
|
|
cmd.ReportError(err.Error(), true)
|
|
os.Exit(1)
|
|
}
|
|
if config.Image == "" {
|
|
cmd.Usage()
|
|
return nil
|
|
}
|
|
response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cli.out, "%s\n", response.ID)
|
|
return nil
|
|
}
|