Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

Push image #97

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ func (c *AuthConfig) encode() string {
json.NewEncoder(&buf).Encode(c)
return base64.URLEncoding.EncodeToString(buf.Bytes())
}

// ConfigFile holds parameters for authenticating during a BuildImage request
type ConfigFile struct {
Configs map[string]AuthConfig `json:"configs,omitempty"`
rootPath string
}

// encode the configuration struct into base64 for the X-Registry-Config header
func (c *ConfigFile) encode() string {
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(c)
return base64.URLEncoding.EncodeToString(buf.Bytes())
}
77 changes: 77 additions & 0 deletions dockerclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,83 @@ func (client *DockerClient) Version() (*Version, error) {
return version, nil
}

func (client *DockerClient) BuildImage(image BuildImage, config *ConfigFile) error {
v := url.Values{}
if image.DockerfilePath != "" {
v.Set("dockerfile", image.DockerfilePath)
}
if image.Name != "" {
v.Set("t", image.Name)
}
if image.Remote != "" {
v.Set("remote", image.Remote)
}
if image.NoCache {
v.Set("nocache", "1")
}
if image.Pull {
v.Set("pull", "1")
}
if image.Remove {
v.Set("rm", "1")
} else {
v.Set("rm", "0")
}
if image.ForceRemove {
v.Set("forcerm", "1")
}
v.Set("q", "1")
uri := fmt.Sprintf("/%s/build?%s", APIVersion, v.Encode())

req, err := http.NewRequest("POST", client.URL.String()+uri, image.Tarfile)
if config != nil {
req.Header.Add("X-Registry-Config", config.encode())
}
req.Header.Set("Content-Type", "application/tar")
resp, err := client.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
var finalObj map[string]interface{}
for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
}
if err != io.EOF {
return err
}
if err, ok := finalObj["error"]; ok {
return fmt.Errorf("%v", err)
}
return nil
}

func (client *DockerClient) PushImage(name string, tag string, auth *AuthConfig) error {
v := url.Values{}
if tag != "" {
v.Set("tag", tag)
}
uri := fmt.Sprintf("/%s/images/%s/push?%s", APIVersion, url.QueryEscape(name), v.Encode())
req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
if auth != nil {
req.Header.Add("X-Registry-Auth", auth.encode())
}
resp, err := client.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
var finalObj map[string]interface{}
for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this meant to go after line 515?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. this loop gets the final object in the json stream

if err != io.EOF {
return err
}
if err, ok := finalObj["error"]; ok {
return fmt.Errorf("%v", err)
}
return nil
}

func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
v := url.Values{}
v.Set("fromImage", name)
Expand Down
2 changes: 2 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Client interface {
Info() (*Info, error)
ListContainers(all, size bool, filters string) ([]Container, error)
InspectContainer(id string) (*ContainerInfo, error)
BuildImage(image BuildImage, config *ConfigFile) error
InspectImage(id string) (*ImageInfo, error)
CreateContainer(config *ContainerConfig, name string) (string, error)
ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error)
Expand All @@ -33,6 +34,7 @@ type Client interface {
TagImage(nameOrID string, repo string, tag string, force bool) error
Version() (*Version, error)
PullImage(name string, auth *AuthConfig) error
PushImage(name string, tag string, auth *AuthConfig) error
LoadImage(reader io.Reader) error
RemoveContainer(id string, force, volumes bool) error
ListImages() ([]*Image, error)
Expand Down
12 changes: 12 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dockerclient

import (
"fmt"
"io"
"time"

"github.com/docker/docker/pkg/units"
Expand Down Expand Up @@ -250,6 +251,17 @@ type Image struct {
VirtualSize int64
}

type BuildImage struct {
Name string
Remote string
DockerfilePath string
Tarfile io.Reader
NoCache bool
Pull bool
Remove bool
ForceRemove bool
}

// Info is the struct returned by /info
// The API is currently in flux, so Debug, MemoryLimit, SwapLimit, and
// IPv4Forwarding are interfaces because in docker 1.6.1 they are 0 or 1 but in
Expand Down