Merge pull request #24748 from Random-Liu/cleanup-with-new-engine-api

Automatic merge from submit-queue

Kubelet: Cleanup with new engine api

Finish step 2 of #23563

This PR:
1) Cleanup go-dockerclient reference in the code.
2) Bump up the engine-api version.
3) Cleanup the code with new engine-api.

Fixes #24076.
Fixes #23809.

/cc @yujuhong
This commit is contained in:
k8s-merge-robot 2016-05-06 03:16:53 -07:00
commit 346ddc52c2
57 changed files with 605 additions and 379 deletions

View File

@ -372,3 +372,11 @@ raw.githubusercontent.com/Microsoft/go-winio/master/NOTICE
raw.githubusercontent.com/Microsoft/go-winio/master/NOTICE.txt
raw.githubusercontent.com/Microsoft/go-winio/master/README
raw.githubusercontent.com/Microsoft/go-winio/master/README.md
raw.githubusercontent.com/docker/distribution/master/NOTICE
raw.githubusercontent.com/docker/distribution/master/NOTICE.txt
raw.githubusercontent.com/docker/distribution/master/README
raw.githubusercontent.com/docker/distribution/master/README.md
raw.githubusercontent.com/golang/mock/master/NOTICE
raw.githubusercontent.com/golang/mock/master/NOTICE.txt
raw.githubusercontent.com/golang/mock/master/README
raw.githubusercontent.com/golang/mock/master/README.md

16
Godeps/Godeps.json generated
View File

@ -414,13 +414,13 @@
},
{
"ImportPath": "github.com/docker/distribution/digest",
"Comment": "v2.4.0-rc.1-22-g55f1b76",
"Rev": "55f1b7651f6242617133312ff8af5c2e4e3628ea"
"Comment": "v2.4.0-rc.1-38-gcd27f17",
"Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51"
},
{
"ImportPath": "github.com/docker/distribution/reference",
"Comment": "v2.4.0-rc.1-22-g55f1b76",
"Rev": "55f1b7651f6242617133312ff8af5c2e4e3628ea"
"Comment": "v2.4.0-rc.1-38-gcd27f17",
"Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51"
},
{
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
@ -459,13 +459,13 @@
},
{
"ImportPath": "github.com/docker/engine-api/client",
"Comment": "v0.2.2-173-g26cdffe",
"Rev": "26cdffeca716ae4df98070051a852b3198d7d153"
"Comment": "v0.3.1-62-g3d72d39",
"Rev": "3d72d392d07bece8d7d7b2a3b6b2e57c2df376a2"
},
{
"ImportPath": "github.com/docker/engine-api/types",
"Comment": "v0.2.2-173-g26cdffe",
"Rev": "26cdffeca716ae4df98070051a852b3198d7d153"
"Comment": "v0.3.1-62-g3d72d39",
"Rev": "3d72d392d07bece8d7d7b2a3b6b2e57c2df376a2"
},
{
"ImportPath": "github.com/docker/go-connections/nat",

View File

@ -21,7 +21,7 @@ type Client struct {
addr string
// basePath holds the path to prepend to the requests.
basePath string
// transport is the interface to sends request with, it implements transport.Client.
// transport is the interface to send request with, it implements transport.Client.
transport transport.Client
// version of the server to talk to.
version string
@ -97,10 +97,14 @@ func (cli *Client) getAPIPath(p string, query url.Values) string {
} else {
apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
}
if len(query) > 0 {
apiPath += "?" + query.Encode()
u := &url.URL{
Path: apiPath,
}
return apiPath
if len(query) > 0 {
u.RawQuery = query.Encode()
}
return u.String()
}
// ClientVersion returns the version string associated with this
@ -110,6 +114,12 @@ func (cli *Client) ClientVersion() string {
return cli.version
}
// UpdateClientVersion updates the version string associated with this
// instance of the Client.
func (cli *Client) UpdateClientVersion(v string) {
cli.version = v
}
// ParseHost verifies that the given host strings is valid.
func ParseHost(host string) (string, string, string, error) {
protoAddrParts := strings.SplitN(host, "://", 2)

View File

@ -1,4 +1,4 @@
// +build linux freebsd solaris
// +build linux freebsd solaris openbsd
package client

View File

@ -11,7 +11,7 @@ import (
// It returns a types.HijackedConnection with the hijacked connection
// and the a reader to get output. It's up to the called to close
// the hijacked connection by calling types.HijackedResponse.Close.
func (cli *Client) ContainerAttach(ctx context.Context, options types.ContainerAttachOptions) (types.HijackedResponse, error) {
func (cli *Client) ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) {
query := url.Values{}
if options.Stream {
query.Set("stream", "1")
@ -30,5 +30,5 @@ func (cli *Client) ContainerAttach(ctx context.Context, options types.ContainerA
}
headers := map[string][]string{"Content-Type": {"text/plain"}}
return cli.postHijacked(ctx, "/containers/"+options.ContainerID+"/attach", query, nil, headers)
return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, headers)
}

View File

@ -2,18 +2,36 @@ package client
import (
"encoding/json"
"errors"
"net/url"
distreference "github.com/docker/distribution/reference"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/reference"
"golang.org/x/net/context"
)
// ContainerCommit applies changes into a container and creates a new tagged image.
func (cli *Client) ContainerCommit(ctx context.Context, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error) {
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error) {
var repository, tag string
if options.Reference != "" {
distributionRef, err := distreference.ParseNamed(options.Reference)
if err != nil {
return types.ContainerCommitResponse{}, err
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
return types.ContainerCommitResponse{}, errors.New("refusing to create a tag with a digest reference")
}
tag = reference.GetTagFromNamedRef(distributionRef)
repository = distributionRef.Name()
}
query := url.Values{}
query.Set("container", options.ContainerID)
query.Set("repo", options.RepositoryName)
query.Set("tag", options.Tag)
query.Set("container", container)
query.Set("repo", repository)
query.Set("tag", tag)
query.Set("comment", options.Comment)
query.Set("author", options.Author)
for _, change := range options.Changes {

View File

@ -30,17 +30,17 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
}
// CopyToContainer copies content into the container filesystem.
func (cli *Client) CopyToContainer(ctx context.Context, options types.CopyToContainerOptions) error {
func (cli *Client) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error {
query := url.Values{}
query.Set("path", filepath.ToSlash(options.Path)) // Normalize the paths used in the API.
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
if !options.AllowOverwriteDirWithFile {
query.Set("noOverwriteDirNonDir", "true")
}
path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
apiPath := fmt.Sprintf("/containers/%s/archive", container)
response, err := cli.putRaw(ctx, path, query, options.Content, nil)
response, err := cli.putRaw(ctx, apiPath, query, content, nil)
if err != nil {
return err
}
@ -53,13 +53,13 @@ func (cli *Client) CopyToContainer(ctx context.Context, options types.CopyToCont
return nil
}
// CopyFromContainer get the content from the container and return it as a Reader
// CopyFromContainer gets the content from the container and returns it as a Reader
// to manipulate it in the host. It's up to the caller to close the reader.
func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
func (cli *Client) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
query := make(url.Values, 1)
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
apiPath := fmt.Sprintf("/containers/%s/archive", containerID)
apiPath := fmt.Sprintf("/containers/%s/archive", container)
response, err := cli.get(ctx, apiPath, query, nil)
if err != nil {
return nil, types.ContainerPathStat{}, err

View File

@ -8,9 +8,9 @@ import (
)
// ContainerExecCreate creates a new exec configuration to run an exec process.
func (cli *Client) ContainerExecCreate(ctx context.Context, config types.ExecConfig) (types.ContainerExecCreateResponse, error) {
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.ContainerExecCreateResponse, error) {
var response types.ContainerExecCreateResponse
resp, err := cli.post(ctx, "/containers/"+config.Container+"/exec", nil, config, nil)
resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
if err != nil {
return response, err
}
@ -19,7 +19,7 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, config types.ExecCon
return response, err
}
// ContainerExecStart starts an exec process already create in the docker host.
// ContainerExecStart starts an exec process already created in the docker host.
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil)
ensureReaderClosed(resp)

View File

@ -8,7 +8,7 @@ import (
)
// ContainerExport retrieves the raw contents of a container
// and returns them as a io.ReadCloser. It's up to the caller
// and returns them as an io.ReadCloser. It's up to the caller
// to close the stream.
func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) {
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)

View File

@ -35,7 +35,8 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
}
if options.Filter.Len() > 0 {
filterJSON, err := filters.ToParam(options.Filter)
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filter)
if err != nil {
return nil, err
}

View File

@ -13,7 +13,7 @@ import (
// ContainerLogs returns the logs generated by a container in an io.ReadCloser.
// It's up to the caller to close the stream.
func (cli *Client) ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error) {
func (cli *Client) ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
query := url.Values{}
if options.ShowStdout {
query.Set("stdout", "1")
@ -40,7 +40,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, options types.ContainerLog
}
query.Set("tail", options.Tail)
resp, err := cli.get(ctx, "/containers/"+options.ContainerID+"/logs", query, nil)
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
if err != nil {
return nil, err
}

View File

@ -8,7 +8,7 @@ import (
)
// ContainerRemove kills and removes a container from the docker host.
func (cli *Client) ContainerRemove(ctx context.Context, options types.ContainerRemoveOptions) error {
func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error {
query := url.Values{}
if options.RemoveVolumes {
query.Set("v", "1")
@ -21,7 +21,7 @@ func (cli *Client) ContainerRemove(ctx context.Context, options types.ContainerR
query.Set("force", "1")
}
resp, err := cli.delete(ctx, "/containers/"+options.ContainerID, query, nil)
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
ensureReaderClosed(resp)
return err
}

View File

@ -9,13 +9,13 @@ import (
)
// ContainerResize changes the size of the tty for a container.
func (cli *Client) ContainerResize(ctx context.Context, options types.ResizeOptions) error {
return cli.resize(ctx, "/containers/"+options.ID, options.Height, options.Width)
func (cli *Client) ContainerResize(ctx context.Context, containerID string, options types.ResizeOptions) error {
return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width)
}
// ContainerExecResize changes the size of the tty for an exec process running inside a container.
func (cli *Client) ContainerExecResize(ctx context.Context, options types.ResizeOptions) error {
return cli.resize(ctx, "/exec/"+options.ID, options.Height, options.Width)
func (cli *Client) ContainerExecResize(ctx context.Context, execID string, options types.ResizeOptions) error {
return cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
}
func (cli *Client) resize(ctx context.Context, basePath string, height, width int) error {

View File

@ -8,7 +8,7 @@ import (
"github.com/docker/engine-api/types"
)
// ContainerWait pauses execution util a container is exits.
// ContainerWait pauses execution until a container exits.
// It returns the API status code as response of its readiness.
func (cli *Client) ContainerWait(ctx context.Context, containerID string) (int, error) {
resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", nil, nil, nil)

View File

@ -5,7 +5,7 @@ import (
"fmt"
)
// ErrConnectionFailed is a error raised when the connection between the client and the server failed.
// ErrConnectionFailed is an error raised when the connection between the client and the server failed.
var ErrConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
// imageNotFoundError implements an error returned when an image is not in the docker host.
@ -30,7 +30,7 @@ type containerNotFoundError struct {
containerID string
}
// Error returns a string representation of an containerNotFoundError
// Error returns a string representation of a containerNotFoundError
func (e containerNotFoundError) Error() string {
return fmt.Sprintf("Error: No such container: %s", e.containerID)
}
@ -47,7 +47,7 @@ type networkNotFoundError struct {
networkID string
}
// Error returns a string representation of an networkNotFoundError
// Error returns a string representation of a networkNotFoundError
func (e networkNotFoundError) Error() string {
return fmt.Sprintf("Error: No such network: %s", e.networkID)
}
@ -64,7 +64,7 @@ type volumeNotFoundError struct {
volumeID string
}
// Error returns a string representation of an networkNotFoundError
// Error returns a string representation of a networkNotFoundError
func (e volumeNotFoundError) Error() string {
return fmt.Sprintf("Error: No such volume: %s", e.volumeID)
}
@ -87,7 +87,7 @@ func (u unauthorizedError) Error() string {
}
// IsErrUnauthorized returns true if the error is caused
// when an the remote registry authentication fails
// when a remote registry authentication fails
func IsErrUnauthorized(err error) bool {
_, ok := err.(unauthorizedError)
return ok

View File

@ -46,8 +46,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", "tcp")
tlsConfig := cli.transport.TLSConfig()
conn, err := dial(cli.proto, cli.addr, tlsConfig)
conn, err := dial(cli.proto, cli.addr, cli.transport.TLSConfig())
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
@ -69,11 +68,11 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
defer clientconn.Close()
// Server hijacks the connection, error 'connection closed' expected
clientconn.Do(req)
_, err = clientconn.Do(req)
rwc, br := clientconn.Hijack()
return types.HijackedResponse{Conn: rwc, Reader: br}, nil
return types.HijackedResponse{Conn: rwc, Reader: br}, err
}
func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
@ -126,6 +125,21 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
tcpConn.SetKeepAlivePeriod(30 * time.Second)
}
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
hostname := addr[:colonPos]
// If no ServerName is set, infer the ServerName
// from the hostname we're connecting to.
if config.ServerName == "" {
// Make a copy to avoid polluting argument or default.
c := *config
c.ServerName = hostname
config = &c
}
conn := tls.Client(rawConn, config)
if timeout == 0 {

View File

@ -3,6 +3,7 @@ package client
import (
"encoding/base64"
"encoding/json"
"io"
"net/http"
"net/url"
"regexp"
@ -20,7 +21,7 @@ var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
// ImageBuild sends request to the daemon to build images.
// The Body in the response implement an io.ReadCloser and it's up to the caller to
// close it.
func (cli *Client) ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
query, err := imageBuildOptionsToQuery(options)
if err != nil {
return types.ImageBuildResponse{}, err
@ -34,7 +35,7 @@ func (cli *Client) ImageBuild(ctx context.Context, options types.ImageBuildOptio
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
headers.Set("Content-Type", "application/tar")
serverResp, err := cli.postRaw(ctx, "/build", query, options.Context, headers)
serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
if err != nil {
return types.ImageBuildResponse{}, err
}
@ -101,6 +102,11 @@ func imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, erro
}
query.Set("buildargs", string(buildArgsJSON))
labelsJSON, err := json.Marshal(options.Labels)
if err != nil {
return query, err
}
query.Set("labels", string(labelsJSON))
return query, nil
}

View File

@ -7,14 +7,20 @@ import (
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/reference"
)
// ImageCreate creates a new image based in the parent options.
// It returns the JSON content in the response body.
func (cli *Client) ImageCreate(ctx context.Context, options types.ImageCreateOptions) (io.ReadCloser, error) {
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
repository, tag, err := reference.Parse(parentReference)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", options.Parent)
query.Set("tag", options.Tag)
query.Set("fromImage", repository)
query.Set("tag", tag)
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if err != nil {
return nil, err

View File

@ -6,22 +6,30 @@ import (
"golang.org/x/net/context"
"github.com/docker/distribution/reference"
"github.com/docker/engine-api/types"
)
// ImageImport creates a new image based in the source options.
// It returns the JSON content in the response body.
func (cli *Client) ImageImport(ctx context.Context, options types.ImageImportOptions) (io.ReadCloser, error) {
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
if ref != "" {
//Check if the given image name can be resolved
if _, err := reference.ParseNamed(ref); err != nil {
return nil, err
}
}
query := url.Values{}
query.Set("fromSrc", options.SourceName)
query.Set("repo", options.RepositoryName)
query.Set("fromSrc", source.SourceName)
query.Set("repo", ref)
query.Set("tag", options.Tag)
query.Set("message", options.Message)
for _, change := range options.Changes {
query.Add("changes", change)
}
resp, err := cli.postRaw(ctx, "/images/create", query, options.Source, nil)
resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil)
if err != nil {
return nil, err
}

View File

@ -11,7 +11,7 @@ import (
"golang.org/x/net/context"
)
// ImageInspectWithRaw returns the image information and it's raw representation.
// ImageInspectWithRaw returns the image information and its raw representation.
func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string, getSize bool) (types.ImageInspect, []byte, error) {
query := url.Values{}
if getSize {

View File

@ -8,22 +8,32 @@ import (
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/reference"
)
// ImagePull request the docker host to pull an image from a remote registry.
// ImagePull requests the docker host to pull an image from a remote registry.
// It executes the privileged function if the operation is unauthorized
// and it tries one more time.
// It's up to the caller to handle the io.ReadCloser and close it properly.
func (cli *Client) ImagePull(ctx context.Context, options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
//
// FIXME(vdemeester): there is currently used in a few way in docker/docker
// - if not in trusted content, ref is used to pass the whole reference, and tag is empty
// - if in trusted content, ref is used to pass the reference name, and tag for the digest
func (cli *Client) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
repository, tag, err := reference.Parse(ref)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", options.ImageID)
if options.Tag != "" {
query.Set("tag", options.Tag)
query.Set("fromImage", repository)
if tag != "" {
query.Set("tag", tag)
}
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized {
newAuthHeader, privilegeErr := privilegeFunc()
newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil {
return nil, privilegeErr
}

View File

@ -1,30 +1,44 @@
package client
import (
"errors"
"io"
"net/http"
"net/url"
"golang.org/x/net/context"
distreference "github.com/docker/distribution/reference"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/reference"
)
// ImagePush request the docker host to push an image to a remote registry.
// ImagePush requests the docker host to push an image to a remote registry.
// It executes the privileged function if the operation is unauthorized
// and it tries one more time.
// It's up to the caller to handle the io.ReadCloser and close it properly.
func (cli *Client) ImagePush(ctx context.Context, options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
query := url.Values{}
query.Set("tag", options.Tag)
func (cli *Client) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
distributionRef, err := distreference.ParseNamed(ref)
if err != nil {
return nil, err
}
resp, err := cli.tryImagePush(ctx, options.ImageID, query, options.RegistryAuth)
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
return nil, errors.New("cannot push a digest reference")
}
tag := reference.GetTagFromNamedRef(distributionRef)
query := url.Values{}
query.Set("tag", tag)
resp, err := cli.tryImagePush(ctx, distributionRef.Name(), query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized {
newAuthHeader, privilegeErr := privilegeFunc()
newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil {
return nil, privilegeErr
}
resp, err = cli.tryImagePush(ctx, options.ImageID, query, newAuthHeader)
resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader)
}
if err != nil {
return nil, err

View File

@ -9,7 +9,7 @@ import (
)
// ImageRemove removes an image from the docker host.
func (cli *Client) ImageRemove(ctx context.Context, options types.ImageRemoveOptions) ([]types.ImageDelete, error) {
func (cli *Client) ImageRemove(ctx context.Context, imageID string, options types.ImageRemoveOptions) ([]types.ImageDelete, error) {
query := url.Values{}
if options.Force {
@ -19,7 +19,7 @@ func (cli *Client) ImageRemove(ctx context.Context, options types.ImageRemoveOpt
query.Set("noprune", "1")
}
resp, err := cli.delete(ctx, "/images/"+options.ImageID, query, nil)
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
if err != nil {
return nil, err
}

View File

@ -7,7 +7,7 @@ import (
"golang.org/x/net/context"
)
// ImageSave retrieves one or more images from the docker host as a io.ReadCloser.
// ImageSave retrieves one or more images from the docker host as an io.ReadCloser.
// It's up to the caller to store the images and close the stream.
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error) {
query := url.Values{

View File

@ -12,14 +12,14 @@ import (
// ImageSearch makes the docker host to search by a term in a remote registry.
// The list of results is not sorted in any fashion.
func (cli *Client) ImageSearch(ctx context.Context, options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error) {
func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) {
var results []registry.SearchResult
query := url.Values{}
query.Set("term", options.Term)
query.Set("term", term)
resp, err := cli.tryImageSearch(ctx, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized {
newAuthHeader, privilegeErr := privilegeFunc()
newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil {
return results, privilegeErr
}

View File

@ -1,22 +1,38 @@
package client
import (
"errors"
"fmt"
"net/url"
"github.com/docker/engine-api/types"
"golang.org/x/net/context"
distreference "github.com/docker/distribution/reference"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/reference"
)
// ImageTag tags an image in the docker host
func (cli *Client) ImageTag(ctx context.Context, options types.ImageTagOptions) error {
func (cli *Client) ImageTag(ctx context.Context, imageID, ref string, options types.ImageTagOptions) error {
distributionRef, err := distreference.ParseNamed(ref)
if err != nil {
return fmt.Errorf("Error parsing reference: %q is not a valid repository/tag", ref)
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
return errors.New("refusing to create a tag with a digest reference")
}
tag := reference.GetTagFromNamedRef(distributionRef)
query := url.Values{}
query.Set("repo", options.RepositoryName)
query.Set("tag", options.Tag)
query.Set("repo", distributionRef.Name())
query.Set("tag", tag)
if options.Force {
query.Set("force", "1")
}
resp, err := cli.post(ctx, "/images/"+options.ImageID+"/tag", query, nil, nil)
resp, err := cli.post(ctx, "/images/"+imageID+"/tag", query, nil, nil)
ensureReaderClosed(resp)
return err
}

View File

@ -15,59 +15,60 @@ import (
// APIClient is an interface that clients that talk with a docker server must implement.
type APIClient interface {
ClientVersion() string
ContainerAttach(ctx context.Context, options types.ContainerAttachOptions) (types.HijackedResponse, error)
ContainerCommit(ctx context.Context, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
ContainerDiff(ctx context.Context, ontainerID string) ([]types.ContainerChange, error)
ContainerDiff(ctx context.Context, container string) ([]types.ContainerChange, error)
ContainerExecAttach(ctx context.Context, execID string, config types.ExecConfig) (types.HijackedResponse, error)
ContainerExecCreate(ctx context.Context, config types.ExecConfig) (types.ContainerExecCreateResponse, error)
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.ContainerExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error)
ContainerExecResize(ctx context.Context, options types.ResizeOptions) error
ContainerExecResize(ctx context.Context, execID string, options types.ResizeOptions) error
ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error
ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error)
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (types.ContainerJSON, []byte, error)
ContainerKill(ctx context.Context, containerID, signal string) error
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
ContainerInspect(ctx context.Context, container string) (types.ContainerJSON, error)
ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (types.ContainerJSON, []byte, error)
ContainerKill(ctx context.Context, container, signal string) error
ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error)
ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error)
ContainerPause(ctx context.Context, containerID string) error
ContainerRemove(ctx context.Context, options types.ContainerRemoveOptions) error
ContainerRename(ctx context.Context, containerID, newContainerName string) error
ContainerResize(ctx context.Context, options types.ResizeOptions) error
ContainerRestart(ctx context.Context, containerID string, timeout int) error
ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error)
ContainerStats(ctx context.Context, containerID string, stream bool) (io.ReadCloser, error)
ContainerStart(ctx context.Context, containerID string) error
ContainerStop(ctx context.Context, containerID string, timeout int) error
ContainerTop(ctx context.Context, containerID string, arguments []string) (types.ContainerProcessList, error)
ContainerUnpause(ctx context.Context, containerID string) error
ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) error
ContainerWait(ctx context.Context, containerID string) (int, error)
CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(ctx context.Context, options types.CopyToContainerOptions) error
ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error)
ContainerPause(ctx context.Context, container string) error
ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
ContainerRename(ctx context.Context, container, newContainerName string) error
ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error
ContainerRestart(ctx context.Context, container string, timeout int) error
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
ContainerStart(ctx context.Context, container string) error
ContainerStop(ctx context.Context, container string, timeout int) error
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
ContainerUnpause(ctx context.Context, container string) error
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) error
ContainerWait(ctx context.Context, container string) (int, error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error
Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error)
ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
ImageCreate(ctx context.Context, options types.ImageCreateOptions) (io.ReadCloser, error)
ImageHistory(ctx context.Context, imageID string) ([]types.ImageHistory, error)
ImageImport(ctx context.Context, options types.ImageImportOptions) (io.ReadCloser, error)
ImageInspectWithRaw(ctx context.Context, imageID string, getSize bool) (types.ImageInspect, []byte, error)
ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
ImageHistory(ctx context.Context, image string) ([]types.ImageHistory, error)
ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
ImageInspectWithRaw(ctx context.Context, image string, getSize bool) (types.ImageInspect, []byte, error)
ImageList(ctx context.Context, options types.ImageListOptions) ([]types.Image, error)
ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error)
ImagePull(ctx context.Context, options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImagePush(ctx context.Context, options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImageRemove(ctx context.Context, options types.ImageRemoveOptions) ([]types.ImageDelete, error)
ImageSearch(ctx context.Context, options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error)
ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error)
ImageTag(ctx context.Context, options types.ImageTagOptions) error
ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error)
ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error)
ImageRemove(ctx context.Context, image string, options types.ImageRemoveOptions) ([]types.ImageDelete, error)
ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error)
ImageSave(ctx context.Context, images []string) (io.ReadCloser, error)
ImageTag(ctx context.Context, image, ref string, options types.ImageTagOptions) error
Info(ctx context.Context) (types.Info, error)
NetworkConnect(ctx context.Context, networkID, containerID string, config *network.EndpointSettings) error
NetworkCreate(ctx context.Context, options types.NetworkCreate) (types.NetworkCreateResponse, error)
NetworkDisconnect(ctx context.Context, networkID, containerID string, force bool) error
NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
NetworkInspect(ctx context.Context, networkID string) (types.NetworkResource, error)
NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
NetworkRemove(ctx context.Context, networkID string) error
RegistryLogin(ctx context.Context, auth types.AuthConfig) (types.AuthResponse, error)
ServerVersion(ctx context.Context) (types.Version, error)
UpdateClientVersion(v string)
VolumeCreate(ctx context.Context, options types.VolumeCreateRequest) (types.Volume, error)
VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
VolumeList(ctx context.Context, filter filters.Args) (types.VolumesListResponse, error)

View File

@ -8,9 +8,13 @@ import (
)
// NetworkCreate creates a new network in the docker host.
func (cli *Client) NetworkCreate(ctx context.Context, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
func (cli *Client) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
networkCreateRequest := types.NetworkCreateRequest{
NetworkCreate: options,
Name: name,
}
var response types.NetworkCreateResponse
serverResp, err := cli.post(ctx, "/networks/create", nil, options, nil)
serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil)
if err != nil {
return response, err
}

View File

@ -1,9 +0,0 @@
package client
// RequestPrivilegeFunc is a function interface that
// clients can supply to retry operations after
// getting an authorization error.
// This function returns the registry authentication
// header value in base 64 format, or an error
// if the privilege request fails.
type RequestPrivilegeFunc func() (string, error)

View File

@ -56,12 +56,14 @@ func (cli *Client) delete(ctx context.Context, path string, query url.Values, he
}
func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
body, err := encodeData(obj)
if err != nil {
return nil, err
}
var body io.Reader
if body != nil {
if obj != nil {
var err error
body, err = encodeData(obj)
if err != nil {
return nil, err
}
if headers == nil {
headers = make(map[string][]string)
}
@ -83,6 +85,11 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q
}
req, err := cli.newRequest(method, path, query, body, headers)
if cli.proto == "unix" || cli.proto == "npipe" {
// For local communications, it doesn't matter what the host is. We just
// need a valid and meaningful host name. (See #189)
req.Host = "docker"
}
req.URL.Host = cli.addr
req.URL.Scheme = cli.transport.Scheme()

View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -4,7 +4,6 @@ package transport
import (
"fmt"
"net/http"
"strings"
"github.com/docker/go-connections/sockets"
)
@ -35,10 +34,6 @@ func NewTransportWithHTTP(proto, addr string, client *http.Client) (Client, erro
}
}
if transport.TLSClientConfig != nil && transport.TLSClientConfig.ServerName == "" {
transport.TLSClientConfig.ServerName = hostname(addr)
}
return &apiTransport{
Client: client,
tlsInfo: &tlsInfo{transport.TLSClientConfig},
@ -59,12 +54,4 @@ func defaultTransport(proto, addr string) *http.Transport {
return tr
}
func hostname(addr string) string {
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
return addr
}
return addr[:colonPos]
}
var _ Client = &apiTransport{}

View File

@ -2,7 +2,7 @@ package blkiodev
import "fmt"
// WeightDevice is a structure that hold device:weight pair
// WeightDevice is a structure that holds device:weight pair
type WeightDevice struct {
Path string
Weight uint16
@ -12,7 +12,7 @@ func (w *WeightDevice) String() string {
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
}
// ThrottleDevice is a structure that hold device:rate_per_second pair
// ThrottleDevice is a structure that holds device:rate_per_second pair
type ThrottleDevice struct {
Path string
Rate uint64

View File

@ -12,24 +12,21 @@ import (
// ContainerAttachOptions holds parameters to attach to a container.
type ContainerAttachOptions struct {
ContainerID string
Stream bool
Stdin bool
Stdout bool
Stderr bool
DetachKeys string
Stream bool
Stdin bool
Stdout bool
Stderr bool
DetachKeys string
}
// ContainerCommitOptions holds parameters to commit changes into a container.
type ContainerCommitOptions struct {
ContainerID string
RepositoryName string
Tag string
Comment string
Author string
Changes []string
Pause bool
Config *container.Config
Reference string
Comment string
Author string
Changes []string
Pause bool
Config *container.Config
}
// ContainerExecInspect holds information returned by exec inspect.
@ -54,18 +51,16 @@ type ContainerListOptions struct {
// ContainerLogsOptions holds parameters to filter logs with.
type ContainerLogsOptions struct {
ContainerID string
ShowStdout bool
ShowStderr bool
Since string
Timestamps bool
Follow bool
Tail string
ShowStdout bool
ShowStderr bool
Since string
Timestamps bool
Follow bool
Tail string
}
// ContainerRemoveOptions holds parameters to remove containers.
type ContainerRemoveOptions struct {
ContainerID string
RemoveVolumes bool
RemoveLinks bool
Force bool
@ -74,9 +69,6 @@ type ContainerRemoveOptions struct {
// CopyToContainerOptions holds information
// about files to copy into a container
type CopyToContainerOptions struct {
ContainerID string
Path string
Content io.Reader
AllowOverwriteDirWithFile bool
}
@ -103,7 +95,7 @@ func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// CloseWriter is an interface that implement structs
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
CloseWrite() error
@ -142,6 +134,7 @@ type ImageBuildOptions struct {
BuildArgs map[string]string
AuthConfigs map[string]AuthConfig
Context io.Reader
Labels map[string]string
}
// ImageBuildResponse holds information
@ -154,19 +147,20 @@ type ImageBuildResponse struct {
// ImageCreateOptions holds information to create images.
type ImageCreateOptions struct {
Parent string // Parent is the name of the image to pull
Tag string // Tag is the name to tag this image with
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
}
// ImageImportSource holds source information for ImageImport
type ImageImportSource struct {
Source io.Reader // Source is the data to send to the server to create this image from (mutually exclusive with SourceName)
SourceName string // SourceName is the name of the image to pull (mutually exclusive with Source)
}
// ImageImportOptions holds information to import images from the client host.
type ImageImportOptions struct {
Source io.Reader // Source is the data to send to the server to create this image from (mutually exclusive with SourceName)
SourceName string // SourceName is the name of the image to pull (mutually exclusive with Source)
RepositoryName string // RepositoryName is the name of the repository to import this image into
Message string // Message is the message to tag the image with
Tag string // Tag is the name to tag this image with
Changes []string // Changes are the raw changes to apply to this image
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
Message string // Message is the message to tag the image with
Changes []string // Changes are the raw changes to apply to this image
}
// ImageListOptions holds parameters to filter the list of images with.
@ -184,40 +178,42 @@ type ImageLoadResponse struct {
// ImagePullOptions holds information to pull images.
type ImagePullOptions struct {
ImageID string // ImageID is the name of the image to pull
Tag string // Tag is the name of the tag to be pulled
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
PrivilegeFunc RequestPrivilegeFunc
}
// RequestPrivilegeFunc is a function interface that
// clients can supply to retry operations after
// getting an authorization error.
// This function returns the registry authentication
// header value in base 64 format, or an error
// if the privilege request fails.
type RequestPrivilegeFunc func() (string, error)
//ImagePushOptions holds information to push images.
type ImagePushOptions ImagePullOptions
// ImageRemoveOptions holds parameters to remove images.
type ImageRemoveOptions struct {
ImageID string
Force bool
PruneChildren bool
}
// ImageSearchOptions holds parameters to search images with.
type ImageSearchOptions struct {
Term string
RegistryAuth string
RegistryAuth string
PrivilegeFunc RequestPrivilegeFunc
}
// ImageTagOptions holds parameters to tag an image
type ImageTagOptions struct {
ImageID string
RepositoryName string
Tag string
Force bool
Force bool
}
// ResizeOptions holds parameters to resize a tty.
// It can be used to resize container ttys and
// exec process ttys too.
type ResizeOptions struct {
ID string
Height int
Width int
}
@ -228,7 +224,7 @@ type VersionResponse struct {
Server *Version
}
// ServerOK return true when the client could connect to the docker server
// ServerOK returns true when the client could connect to the docker server
// and parse the information received. It returns false otherwise.
func (v VersionResponse) ServerOK() bool {
return v.Server != nil

View File

@ -38,13 +38,12 @@ type ContainerCommitConfig struct {
Config *container.Config
}
// ExecConfig is a small subset of the Config struct that hold the configuration
// ExecConfig is a small subset of the Config struct that holds the configuration
// for the exec feature of docker.
type ExecConfig struct {
User string // User that will run the command
Privileged bool // Is the container in privileged mode
Tty bool // Attach standard streams to a tty.
Container string // Name of the container (to execute in)
AttachStdin bool // Attach the standard input, makes possible user interaction
AttachStderr bool // Attach the standard output
AttachStdout bool // Attach the standard error

View File

@ -19,7 +19,6 @@ type Config struct {
AttachStdout bool // Attach the standard output
AttachStderr bool // Attach the standard error
ExposedPorts map[nat.Port]struct{} `json:",omitempty"` // List of exposed ports
PublishService string `json:",omitempty"` // Name of the network service exposed by the container
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.

View File

@ -25,7 +25,7 @@ func (i Isolation) IsDefault() bool {
// IpcMode represents the container ipc stack.
type IpcMode string
// IsPrivate indicates whether the container uses it's private ipc stack.
// IsPrivate indicates whether the container uses its private ipc stack.
func (n IpcMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
@ -89,14 +89,16 @@ func (n UsernsMode) Valid() bool {
return true
}
// Cgroup Spec represents the cgroup to use for the container.
// CgroupSpec represents the cgroup to use for the container.
type CgroupSpec string
// IsContainer indicates whether the container is using another container cgroup
func (c CgroupSpec) IsContainer() bool {
parts := strings.SplitN(string(c), ":", 2)
return len(parts) > 1 && parts[0] == "container"
}
// Valid indicates whether the cgroup spec is valid.
func (c CgroupSpec) Valid() bool {
return c.IsContainer() || c == ""
}
@ -113,7 +115,7 @@ func (c CgroupSpec) Container() string {
// UTSMode represents the UTS namespace of the container.
type UTSMode string
// IsPrivate indicates whether the container uses it's private UTS namespace.
// IsPrivate indicates whether the container uses its private UTS namespace.
func (n UTSMode) IsPrivate() bool {
return !(n.IsHost())
}
@ -137,7 +139,7 @@ func (n UTSMode) Valid() bool {
// PidMode represents the pid stack of the container.
type PidMode string
// IsPrivate indicates whether the container uses it's private pid stack.
// IsPrivate indicates whether the container uses its private pid stack.
func (n PidMode) IsPrivate() bool {
return !(n.IsHost())
}
@ -184,7 +186,7 @@ func (rp *RestartPolicy) IsAlways() bool {
}
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
// This means the contain will automatically restart of exiting with a non-zero exit status.
// This means the container will automatically restart of exiting with a non-zero exit status.
func (rp *RestartPolicy) IsOnFailure() bool {
return rp.Name == "on-failure"
}
@ -236,11 +238,11 @@ type Resources struct {
Ulimits []*units.Ulimit // List of ulimits to be set in the container
// Applicable to Windows
CPUCount int64 `json:"CpuCount"` // CPU count
CPUPercent int64 `json:"CpuPercent"` // CPU percent
BlkioIOps uint64 // Maximum IOps for the container system drive
BlkioBps uint64 // Maximum Bytes per second for the container system drive
SandboxSize uint64 // System drive will be expanded to at least this size (in bytes)
CPUCount int64 `json:"CpuCount"` // CPU count
CPUPercent int64 `json:"CpuPercent"` // CPU percent
IOMaximumIOps uint64 // Maximum IOps for the container system drive
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
NetworkMaximumBandwidth uint64 // Maximum bandwidth of the network endpoint in bytes per second
}
// UpdateConfig holds the mutable attributes of a Container.

View File

@ -32,7 +32,7 @@ func (n NetworkMode) IsHost() bool {
return false
}
// IsPrivate indicates whether container uses it's private network stack.
// IsPrivate indicates whether container uses its private network stack.
func (n NetworkMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}

View File

@ -8,12 +8,14 @@ import (
"fmt"
"regexp"
"strings"
"github.com/docker/engine-api/types/versions"
)
// Args stores filter arguments as map key:{map key: bool}.
// It contains a aggregation of the map of arguments (which are in the form
// of -f 'key=value') based on the key, and store values for the same key
// in an map with string keys and boolean values.
// It contains an aggregation of the map of arguments (which are in the form
// of -f 'key=value') based on the key, and stores values for the same key
// in a map with string keys and boolean values.
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
// the args will be {"image.name":{"ubuntu":true},"label":{"label1=1":true,"label2=2":true}}
type Args struct {
@ -54,7 +56,7 @@ func ParseFlag(arg string, prev Args) (Args, error) {
// ErrBadFormat is an error returned in case of bad format for a filter.
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
// ToParam packs the Args into an string for easy transport from client to server.
// ToParam packs the Args into a string for easy transport from client to server.
func ToParam(a Args) (string, error) {
// this way we don't URL encode {}, just empty space
if a.Len() == 0 {
@ -68,6 +70,28 @@ func ToParam(a Args) (string, error) {
return string(buf), nil
}
// ToParamWithVersion packs the Args into a string for easy transport from client to server.
// The generated string will depend on the specified version (corresponding to the API version).
func ToParamWithVersion(version string, a Args) (string, error) {
// this way we don't URL encode {}, just empty space
if a.Len() == 0 {
return "", nil
}
// for daemons older than v1.10, filter must be of the form map[string][]string
buf := []byte{}
err := errors.New("")
if version != "" && versions.LessThan(version, "1.22") {
buf, err = json.Marshal(convertArgsToSlice(a.fields))
} else {
buf, err = json.Marshal(a.fields)
}
if err != nil {
return "", err
}
return string(buf), nil
}
// FromParam unpacks the filter Args.
func FromParam(p string) (Args, error) {
if len(p) == 0 {
@ -190,7 +214,7 @@ func (filters Args) ExactMatch(field, source string) bool {
return true
}
// try to march full name value to avoid O(N) regular expression matching
// try to match full name value to avoid O(N) regular expression matching
if fieldValues[source] {
return true
}
@ -255,3 +279,17 @@ func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
}
return m
}
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
m := map[string][]string{}
for k, v := range f {
values := []string{}
for kk := range v {
if v[kk] {
values = append(values, kk)
}
}
m[k] = values
}
return m
}

View File

@ -0,0 +1,32 @@
package reference
import (
distreference "github.com/docker/distribution/reference"
)
// Parse parses the given references and returns the repository and
// tag (if present) from it. If there is an error during parsing, it will
// return an error.
func Parse(ref string) (string, string, error) {
distributionRef, err := distreference.ParseNamed(ref)
if err != nil {
return "", "", err
}
tag := GetTagFromNamedRef(distributionRef)
return distributionRef.Name(), tag, nil
}
// GetTagFromNamedRef returns a tag from the specified reference.
// This function is necessary as long as the docker "server" api makes the distinction between repository
// and tags.
func GetTagFromNamedRef(ref distreference.Named) string {
var tag string
switch x := ref.(type) {
case distreference.Digested:
tag = x.Digest().String()
case distreference.NamedTagged:
tag = x.Tag()
}
return tag
}

View File

@ -82,7 +82,7 @@ type SearchResult struct {
IsOfficial bool `json:"is_official"`
// Name is the name of the repository
Name string `json:"name"`
// IsOfficial indicates whether the result is trusted
// IsTrusted indicates whether the result is trusted
IsTrusted bool `json:"is_trusted"`
// IsAutomated indicates whether the result is automated
IsAutomated bool `json:"is_automated"`

View File

@ -8,7 +8,7 @@ import "time"
type ThrottlingData struct {
// Number of periods with throttling active
Periods uint64 `json:"periods"`
// Number of periods when the container hit its throttling limit.
// Number of periods when the container hits its throttling limit.
ThrottledPeriods uint64 `json:"throttled_periods"`
// Aggregate time the container was throttled for in nanoseconds.
ThrottledTime uint64 `json:"throttled_time"`
@ -91,6 +91,9 @@ type NetworkStats struct {
type PidsStats struct {
// Current is the number of pids in the cgroup
Current uint64 `json:"current,omitempty"`
// Limit is the hard limit on the number of pids in the cgroup.
// A "Limit" of 0 means that there is no limit.
Limit uint64 `json:"limit,omitempty"`
}
// Stats is Ultimate struct aggregating all types of stats of one container

View File

@ -103,6 +103,13 @@ type GraphDriverData struct {
Data map[string]string
}
// RootFS returns Image's RootFS description including the layer IDs.
type RootFS struct {
Type string
Layers []string `json:",omitempty"`
BaseLayer string `json:",omitempty"`
}
// ImageInspect contains response of Remote API:
// GET "/images/{name:.*}/json"
type ImageInspect struct {
@ -122,6 +129,7 @@ type ImageInspect struct {
Size int64
VirtualSize int64
GraphDriver GraphDriverData
RootFS RootFS
}
// Port stores open ports info of container
@ -243,6 +251,7 @@ type Info struct {
ServerVersion string
ClusterStore string
ClusterAdvertise string
SecurityOptions []string
}
// PluginsInfo is a temp struct holding Plugins name
@ -281,6 +290,18 @@ type ContainerState struct {
FinishedAt string
}
// ContainerNode stores information about the node that a container
// is running on. It's only available in Docker Swarm
type ContainerNode struct {
ID string
IPAddress string `json:"IP"`
Addr string
Name string
Cpus int
Memory int
Labels map[string]string
}
// ContainerJSONBase contains response of Remote API:
// GET "/containers/{name:.*}/json"
type ContainerJSONBase struct {
@ -294,6 +315,7 @@ type ContainerJSONBase struct {
HostnamePath string
HostsPath string
LogPath string
Node *ContainerNode `json:",omitempty"`
Name string
RestartCount int
Driver string
@ -368,9 +390,11 @@ type MountPoint struct {
// Volume represents the configuration of a volume for the remote API
type Volume struct {
Name string // Name is the name of the volume
Driver string // Driver is the Driver name used to create the volume
Mountpoint string // Mountpoint is the location on disk of the volume
Name string // Name is the name of the volume
Driver string // Driver is the Driver name used to create the volume
Mountpoint string // Mountpoint is the location on disk of the volume
Status map[string]interface{} `json:",omitempty"` // Status provides low-level status information about the volume
Labels map[string]string // Labels is metadata specific to the volume
}
// VolumesListResponse contains the response for the remote API:
@ -386,6 +410,7 @@ type VolumeCreateRequest struct {
Name string // Name is the requested name of the volume
Driver string // Driver is the name of the driver that should be used to create the volume
DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
Labels map[string]string // Labels holds metadata specific to the volume being created.
}
// NetworkResource is the body of the "get network" http response message
@ -399,6 +424,7 @@ type NetworkResource struct {
Internal bool
Containers map[string]EndpointResource
Options map[string]string
Labels map[string]string
}
// EndpointResource contains network resources allocated and used for a container in a network
@ -412,13 +438,19 @@ type EndpointResource struct {
// NetworkCreate is the expected body of the "create network" http request message
type NetworkCreate struct {
Name string
CheckDuplicate bool
Driver string
EnableIPv6 bool
IPAM network.IPAM
Internal bool
Options map[string]string
Labels map[string]string
}
// NetworkCreateRequest is the request message sent to the server for network create call.
type NetworkCreateRequest struct {
NetworkCreate
Name string
}
// NetworkCreateResponse is the response message sent by the server for network create call

View File

@ -0,0 +1,62 @@
package versions
import (
"strconv"
"strings"
)
// compare compares two version strings
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
func compare(v1, v2 string) int {
var (
currTab = strings.Split(v1, ".")
otherTab = strings.Split(v2, ".")
)
max := len(currTab)
if len(otherTab) > max {
max = len(otherTab)
}
for i := 0; i < max; i++ {
var currInt, otherInt int
if len(currTab) > i {
currInt, _ = strconv.Atoi(currTab[i])
}
if len(otherTab) > i {
otherInt, _ = strconv.Atoi(otherTab[i])
}
if currInt > otherInt {
return 1
}
if otherInt > currInt {
return -1
}
}
return 0
}
// LessThan checks if a version is less than another
func LessThan(v, other string) bool {
return compare(v, other) == -1
}
// LessThanOrEqualTo checks if a version is less than or equal to another
func LessThanOrEqualTo(v, other string) bool {
return compare(v, other) <= 0
}
// GreaterThan checks if a version is greater than another
func GreaterThan(v, other string) bool {
return compare(v, other) == 1
}
// GreaterThanOrEqualTo checks if a version is greater than or equal to another
func GreaterThanOrEqualTo(v, other string) bool {
return compare(v, other) >= 0
}
// Equal checks if a version is equal to another
func Equal(v, other string) bool {
return compare(v, other) == 0
}

View File

@ -27,7 +27,7 @@ type ContainerConfig struct {
VolumeDriver string
}
// StatsJSON is a backcompatibility struct used in Stats for API prior to 1.21
// StatsJSON is a backcompatibility struct used in Stats for APIs prior to 1.21
type StatsJSON struct {
types.Stats
Network types.NetworkStats `json:"network,omitempty"`

View File

@ -95,13 +95,4 @@ if ! _out="$(diff -Naupr --ignore-matching-lines='^\s*\"GoVersion\":' --ignore-m
exit 1
fi
# Godeps/_workstapces/src/github.com/fsouza/go-dockerclient/testing/data/symlink'
# is an intentionally broken symlink. Linux can use --no-dereference. OS X cannot.
# So we --exclude='symlink' so diff -r doesn't die following a bad symlink.
if ! _out="$(diff -Naupr --exclude='symlink' ${KUBE_ROOT}/Godeps/_workspace/src ${_kubetmp}/Godeps/_workspace/src)"; then
echo "Your godeps changes are not reproducible"
echo "${_out}"
exit 1
fi
# ex: ts=2 sw=2 et filetype=sh

View File

@ -23,7 +23,6 @@ import (
"strconv"
"testing"
docker "github.com/fsouza/go-dockerclient"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/testapi"
@ -191,21 +190,6 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
*j = t
}
},
func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) {
// This is necessary because keys with nil values get omitted.
// TODO: Is this a bug?
pb[docker.Port(c.RandString())] = []docker.PortBinding{
{c.RandString(), c.RandString()},
{c.RandString(), c.RandString()},
}
},
func(pm map[string]docker.PortMapping, c fuzz.Continue) {
// This is necessary because keys with nil values get omitted.
// TODO: Is this a bug?
pm[c.RandString()] = docker.PortMapping{
c.RandString(): c.RandString(),
}
},
func(q *api.ResourceRequirements, c fuzz.Continue) {
randomQuantity := func() resource.Quantity {
var q resource.Quantity

View File

@ -92,7 +92,7 @@ func SetDefaults_ContainerPort(obj *ContainerPort) {
func SetDefaults_Container(obj *Container) {
if obj.ImagePullPolicy == "" {
// Ignore error and assume it has been validated elsewhere
_, tag, _ := parsers.ParseImageName(obj.Image)
_, tag, _, _ := parsers.ParseImageName(obj.Image)
// Check image tag

View File

@ -24,6 +24,7 @@ import (
"strconv"
"strings"
dockerref "github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/jsonmessage"
dockerapi "github.com/docker/engine-api/client"
dockertypes "github.com/docker/engine-api/types"
@ -55,7 +56,7 @@ const (
minQuotaPerod = 1000
)
// DockerInterface is an abstract interface for testability. It abstracts the interface of docker.Client.
// DockerInterface is an abstract interface for testability. It abstracts the interface of docker client.
type DockerInterface interface {
ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error)
InspectContainer(id string) (*dockertypes.ContainerJSON, error)
@ -144,9 +145,28 @@ func filterHTTPError(err error, image string) error {
}
}
// applyDefaultImageTag parses a docker image string, if it doesn't contain any tag or digest,
// a default tag will be applied.
func applyDefaultImageTag(image string) (string, error) {
named, err := dockerref.ParseNamed(image)
if err != nil {
return "", fmt.Errorf("couldn't parse image reference %q: %v", image, err)
}
_, isTagged := named.(dockerref.Tagged)
_, isDigested := named.(dockerref.Digested)
if !isTagged && !isDigested {
named, err := dockerref.WithTag(named, parsers.DefaultImageTag)
if err != nil {
return "", fmt.Errorf("failed to apply default image tag %q: %v", image, err)
}
image = named.String()
}
return image, nil
}
func (p dockerPuller) Pull(image string, secrets []api.Secret) error {
// If no tag was specified, use the default "latest".
imageID, tag, err := parsers.ParseImageName(image)
// If the image contains no tag or digest, a default tag should be applied.
image, err := applyDefaultImageTag(image)
if err != nil {
return err
}
@ -156,15 +176,14 @@ func (p dockerPuller) Pull(image string, secrets []api.Secret) error {
return err
}
opts := dockertypes.ImagePullOptions{
Tag: tag,
}
// The only used image pull option RegistryAuth will be set in kube_docker_client
opts := dockertypes.ImagePullOptions{}
creds, haveCredentials := keyring.Lookup(imageID)
creds, haveCredentials := keyring.Lookup(image)
if !haveCredentials {
glog.V(1).Infof("Pulling image %s without credentials", image)
err := p.client.PullImage(imageID, dockertypes.AuthConfig{}, opts)
err := p.client.PullImage(image, dockertypes.AuthConfig{}, opts)
if err == nil {
// Sometimes PullImage failed with no error returned.
exist, ierr := p.IsImagePresent(image)
@ -191,7 +210,7 @@ func (p dockerPuller) Pull(image string, secrets []api.Secret) error {
var pullErrs []error
for _, currentCreds := range creds {
err = p.client.PullImage(imageID, credentialprovider.LazyProvide(currentCreds), opts)
err = p.client.PullImage(image, credentialprovider.LazyProvide(currentCreds), opts)
// If there was no error, return success
if err == nil {
return nil

View File

@ -40,7 +40,6 @@ import (
nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
"k8s.io/kubernetes/pkg/types"
hashutil "k8s.io/kubernetes/pkg/util/hash"
"k8s.io/kubernetes/pkg/util/parsers"
)
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) {
@ -156,26 +155,20 @@ func TestContainerNaming(t *testing.T) {
}
}
func TestParseImageName(t *testing.T) {
tests := []struct {
imageName string
name string
tag string
func TestApplyDefaultImageTag(t *testing.T) {
for _, testCase := range []struct {
Input string
Output string
}{
{"ubuntu", "ubuntu", "latest"},
{"ubuntu:2342", "ubuntu", "2342"},
{"ubuntu:latest", "ubuntu", "latest"},
{"foo/bar:445566", "foo/bar", "445566"},
{"registry.example.com:5000/foobar", "registry.example.com:5000/foobar", "latest"},
{"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar", "5342"},
{"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar", "latest"},
}
for _, test := range tests {
name, tag, err := parsers.ParseImageName(test.imageName)
{Input: "root", Output: "root:latest"},
{Input: "root:tag", Output: "root:tag"},
{Input: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Output: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
} {
image, err := applyDefaultImageTag(testCase.Input)
if err != nil {
t.Errorf("ParseImageName(%s) failed: %v", test.imageName, err)
} else if name != test.name || tag != test.tag {
t.Errorf("Expected name/tag: %s/%s, got %s/%s", test.name, test.tag, name, tag)
t.Errorf("applyDefaultTag(%s) failed: %v", testCase.Input, err)
} else if image != testCase.Output {
t.Errorf("Expected image reference: %q, got %q", testCase.Output, image)
}
}
}

View File

@ -422,14 +422,14 @@ func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions
// PullImage is a test-spy implementation of DockerInterface.PullImage.
// It adds an entry "pull" to the internal method call record.
func (f *FakeDockerClient) PullImage(imageID string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
f.Lock()
defer f.Unlock()
f.called = append(f.called, "pull")
err := f.popError("pull")
if err == nil {
authJson, _ := json.Marshal(auth)
f.pulled = append(f.pulled, fmt.Sprintf("%s:%s using %s", imageID, opts.Tag, string(authJson)))
f.pulled = append(f.pulled, fmt.Sprintf("%s using %s", image, string(authJson)))
}
return err
}

View File

@ -29,7 +29,6 @@ import (
dockerstdcopy "github.com/docker/docker/pkg/stdcopy"
dockerapi "github.com/docker/engine-api/client"
dockertypes "github.com/docker/engine-api/types"
dockerfilters "github.com/docker/engine-api/types/filters"
"golang.org/x/net/context"
)
@ -70,26 +69,6 @@ func getDefaultContext() context.Context {
return context.Background()
}
// convertType converts between different types with the same json format.
func convertType(src interface{}, dst interface{}) error {
data, err := json.Marshal(src)
if err != nil {
return err
}
return json.Unmarshal(data, dst)
}
// convertFilters converts filters to the filter type in engine-api.
func convertFilters(filters map[string][]string) dockerfilters.Args {
args := dockerfilters.NewArgs()
for name, fields := range filters {
for _, field := range fields {
args.Add(name, field)
}
}
return args
}
func (k *kubeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
containers, err := k.client.ContainerList(getDefaultContext(), options)
if err != nil {
@ -136,8 +115,7 @@ func (d *kubeDockerClient) StopContainer(id string, timeout int) error {
}
func (d *kubeDockerClient) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error {
opts.ContainerID = id
return d.client.ContainerRemove(getDefaultContext(), opts)
return d.client.ContainerRemove(getDefaultContext(), id, opts)
}
func (d *kubeDockerClient) InspectImage(image string) (*dockertypes.ImageInspect, error) {
@ -177,9 +155,8 @@ func (d *kubeDockerClient) PullImage(image string, auth dockertypes.AuthConfig,
if err != nil {
return err
}
opts.ImageID = image
opts.RegistryAuth = base64Auth
resp, err := d.client.ImagePull(getDefaultContext(), opts, nil)
resp, err := d.client.ImagePull(getDefaultContext(), image, opts)
if err != nil {
return err
}
@ -203,12 +180,11 @@ func (d *kubeDockerClient) PullImage(image string, auth dockertypes.AuthConfig,
}
func (d *kubeDockerClient) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) {
return d.client.ImageRemove(getDefaultContext(), dockertypes.ImageRemoveOptions{ImageID: image})
return d.client.ImageRemove(getDefaultContext(), image, opts)
}
func (d *kubeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
opts.ContainerID = id
resp, err := d.client.ContainerLogs(getDefaultContext(), opts)
resp, err := d.client.ContainerLogs(getDefaultContext(), id, opts)
if err != nil {
return err
}
@ -234,8 +210,7 @@ func (d *kubeDockerClient) Info() (*dockertypes.Info, error) {
// TODO(random-liu): Add unit test for exec and attach functions, just like what go-dockerclient did.
func (d *kubeDockerClient) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error) {
opts.Container = id
resp, err := d.client.ContainerExecCreate(getDefaultContext(), opts)
resp, err := d.client.ContainerExecCreate(getDefaultContext(), id, opts)
if err != nil {
return nil, err
}
@ -266,8 +241,7 @@ func (d *kubeDockerClient) InspectExec(id string) (*dockertypes.ContainerExecIns
}
func (d *kubeDockerClient) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error {
opts.ContainerID = id
resp, err := d.client.ContainerAttach(getDefaultContext(), opts)
resp, err := d.client.ContainerAttach(getDefaultContext(), id, opts)
if err != nil {
return err
}

View File

@ -33,6 +33,7 @@ import (
dockertypes "github.com/docker/engine-api/types"
dockercontainer "github.com/docker/engine-api/types/container"
dockerstrslice "github.com/docker/engine-api/types/strslice"
dockerversion "github.com/docker/engine-api/types/versions"
dockernat "github.com/docker/go-connections/nat"
"github.com/golang/glog"
cadvisorapi "github.com/google/cadvisor/info/v1"
@ -894,40 +895,12 @@ func (v dockerVersion) String() string {
}
func (v dockerVersion) Compare(other string) (int, error) {
return compare(string(v), other), nil
}
// compare is copied from engine-api, it compares two version strings, returns -1 if
// v1 < v2, 1 if v1 > v2, 0 otherwise.
// TODO(random-liu): Leveraging the version comparison in engine-api after bumping up
// the engine-api version. See #24076
func compare(v1, v2 string) int {
var (
currTab = strings.Split(v1, ".")
otherTab = strings.Split(v2, ".")
)
max := len(currTab)
if len(otherTab) > max {
max = len(otherTab)
if dockerversion.LessThan(string(v), other) {
return -1, nil
} else if dockerversion.GreaterThan(string(v), other) {
return 1, nil
}
for i := 0; i < max; i++ {
var currInt, otherInt int
if len(currTab) > i {
currInt, _ = strconv.Atoi(currTab[i])
}
if len(otherTab) > i {
otherInt, _ = strconv.Atoi(otherTab[i])
}
if currInt > otherInt {
return 1
}
if otherInt > currInt {
return -1
}
}
return 0
return 0, nil
}
func (dm *DockerManager) Type() string {

View File

@ -1415,7 +1415,7 @@ func (kl *Kubelet) getServiceEnvVarMap(ns string) (map[string]string, error) {
// Make the environment variables for a pod in the given namespace.
func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Container, podIP string) ([]kubecontainer.EnvVar, error) {
var result []kubecontainer.EnvVar
// Note: These are added to the docker.Config, but are not included in the checksum computed
// Note: These are added to the docker Config, but are not included in the checksum computed
// by dockertools.BuildDockerName(...). That way, we can still determine whether an
// api.Container is already running by its hash. (We don't want to restart a container just
// because some service changed.)

View File

@ -47,7 +47,7 @@ func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Sec
img := image.Image
// TODO(yifan): The credential operation is a copy from dockertools package,
// Need to resolve the code duplication.
repoToPull, _, err := parsers.ParseImageName(img)
repoToPull, _, _, err := parsers.ParseImageName(img)
if err != nil {
return err
}
@ -142,7 +142,7 @@ func (s sortByImportTime) Less(i, j int) bool { return s[i].ImportTimestamp < s[
// will return the result reversely sorted by the import time, so that the latest
// image comes first.
func (r *Runtime) listImages(image string, detail bool) ([]*rktapi.Image, error) {
repoToPull, tag, err := parsers.ParseImageName(image)
repoToPull, tag, _, err := parsers.ParseImageName(image)
if err != nil {
return nil, err
}

View File

@ -19,37 +19,36 @@ package parsers
import (
"fmt"
"github.com/docker/distribution/reference"
dockerref "github.com/docker/distribution/reference"
)
const (
defaultImageTag = "latest"
DefaultImageTag = "latest"
)
// parseImageName parses a docker image string into two parts: repo and tag.
// If tag is empty, return the defaultImageTag.
func ParseImageName(image string) (string, string, error) {
named, err := reference.ParseNamed(image)
// ParseImageName parses a docker image string into three parts: repo, tag and digest.
// If both tag and digest are empty, a default image tag will be returned.
func ParseImageName(image string) (string, string, string, error) {
named, err := dockerref.ParseNamed(image)
if err != nil {
return "", "", fmt.Errorf("couldn't parse image name: %v", err)
return "", "", "", fmt.Errorf("couldn't parse image name: %v", err)
}
repoToPull := named.Name()
var tag string
var tag, digest string
tagged, ok := named.(reference.Tagged)
tagged, ok := named.(dockerref.Tagged)
if ok {
tag = tagged.Tag()
}
digested, ok := named.(reference.Digested)
digested, ok := named.(dockerref.Digested)
if ok {
tag = digested.Digest().String()
digest = digested.Digest().String()
}
// If no tag was specified, use the default "latest".
if len(tag) == 0 {
tag = defaultImageTag
if len(tag) == 0 && len(digest) == 0 {
tag = DefaultImageTag
}
return repoToPull, tag, nil
return repoToPull, tag, digest, nil
}

View File

@ -24,26 +24,28 @@ import (
// https://github.com/docker/docker/commit/4352da7803d182a6013a5238ce20a7c749db979a
func TestParseImageName(t *testing.T) {
testCases := []struct {
Input string
Repo string
Image string
Input string
Repo string
Tag string
Digest string
}{
{Input: "root", Repo: "root", Image: "latest"},
{Input: "root:tag", Repo: "root", Image: "tag"},
{Input: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "root", Image: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "user/repo", Repo: "user/repo", Image: "latest"},
{Input: "user/repo:tag", Repo: "user/repo", Image: "tag"},
{Input: "user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "user/repo", Image: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "url:5000/repo", Repo: "url:5000/repo", Image: "latest"},
{Input: "url:5000/repo:tag", Repo: "url:5000/repo", Image: "tag"},
{Input: "url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "url:5000/repo", Image: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "root", Repo: "root", Tag: "latest"},
{Input: "root:tag", Repo: "root", Tag: "tag"},
{Input: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "root", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "user/repo", Repo: "user/repo", Tag: "latest"},
{Input: "user/repo:tag", Repo: "user/repo", Tag: "tag"},
{Input: "user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "user/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "url:5000/repo", Repo: "url:5000/repo", Tag: "latest"},
{Input: "url:5000/repo:tag", Repo: "url:5000/repo", Tag: "tag"},
{Input: "url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "url:5000/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
}
for _, testCase := range testCases {
repo, image, err := ParseImageName(testCase.Input)
repo, tag, digest, err := ParseImageName(testCase.Input)
if err != nil {
t.Errorf("ParseImageName(%s) failed: %v", testCase.Input, err)
} else if repo != testCase.Repo || image != testCase.Image {
t.Errorf("Expected repo: '%s' and image: '%s', got '%s' and '%s'", "root", "", repo, image)
} else if repo != testCase.Repo || tag != testCase.Tag || digest != testCase.Digest {
t.Errorf("Expected repo: %q, tag: %q and digest: %q, got %q, %q and %q", testCase.Repo, testCase.Tag, testCase.Digest,
repo, tag, digest)
}
}
}