diff --git a/auth.go b/auth.go index 022d3dd..8a230bf 100644 --- a/auth.go +++ b/auth.go @@ -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()) +} diff --git a/dockerclient.go b/dockerclient.go index fe2a648..23cb215 100644 --- a/dockerclient.go +++ b/dockerclient.go @@ -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) { + } + 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) diff --git a/interface.go b/interface.go index f3d3f14..0042763 100644 --- a/interface.go +++ b/interface.go @@ -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) @@ -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) diff --git a/types.go b/types.go index 111d45b..fba9283 100644 --- a/types.go +++ b/types.go @@ -2,6 +2,7 @@ package dockerclient import ( "fmt" + "io" "time" "github.com/docker/docker/pkg/units" @@ -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