Skip to content

Commit

Permalink
improve build
Browse files Browse the repository at this point in the history
Signed-off-by: Xian Chaobo <[email protected]>
  • Loading branch information
jimmyxian committed Aug 4, 2015
1 parent acdb67b commit a3cf5b9
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 44 additions & 25 deletions api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,31 +620,6 @@ func proxyRandom(c *context, w http.ResponseWriter, r *http.Request) {

}

// Proxy a request to a random node and force refresh
func proxyRandomAndForceRefresh(c *context, w http.ResponseWriter, r *http.Request) {
engine, err := c.cluster.RANDOMENGINE()
if err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
}

if engine == nil {
httpError(w, "no node available in the cluster", http.StatusInternalServerError)
return
}

cb := func(resp *http.Response) {
if resp.StatusCode == http.StatusOK {
engine.RefreshImages()
}
}

if err := proxyAsync(c.tlsConfig, engine.Addr, w, r, cb); err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
}

}

// POST /commit
func postCommit(c *context, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
Expand Down Expand Up @@ -678,6 +653,50 @@ func postCommit(c *context, w http.ResponseWriter, r *http.Request) {
}
}

// POST /build
func postBuild(c *context, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
}

buildImage := &dockerclient.BuildImage{
DockerfileName: r.Form.Get("dockerfile"),
RepoName: r.Form.Get("t"),
RemoteURL: r.Form.Get("remote"),
NoCache: boolValue(r, "nocache"),
Pull: boolValue(r, "pull"),
Remove: boolValue(r, "rm"),
ForceRemove: boolValue(r, "forcerm"),
SuppressOutput: boolValue(r, "q"),
Memory: int64ValueOrZero(r, "memory"),
MemorySwap: int64ValueOrZero(r, "memswap"),
CpuShares: int64ValueOrZero(r, "cpushares"),
CpuPeriod: int64ValueOrZero(r, "cpuperiod"),
CpuQuota: int64ValueOrZero(r, "cpuquota"),
CpuSetCpus: r.Form.Get("cpusetcpus"),
CpuSetMems: r.Form.Get("cpusetmems"),
CgroupParent: r.Form.Get("cgroupparent"),
Context: r.Body,
}

authEncoded := r.Header.Get("X-Registry-Auth")
if authEncoded != "" {
buf, err := base64.URLEncoding.DecodeString(r.Header.Get("X-Registry-Auth"))
if err == nil {
json.Unmarshal(buf, &buildImage.Config)
}
}

w.Header().Set("Content-Type", "application/json")
wf := NewWriteFlusher(w)

err := c.cluster.BuildImage(buildImage, wf)
if err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
}
}

// POST /containers/{name:.*}/rename
func postRenameContainer(c *context, w http.ResponseWriter, r *http.Request) {
_, container, err := getContainerFromVars(c, mux.Vars(r))
Expand Down
2 changes: 1 addition & 1 deletion api/primary.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var routes = map[string]map[string]handler{
"POST": {
"/auth": proxyRandom,
"/commit": postCommit,
"/build": proxyRandomAndForceRefresh,
"/build": postBuild,
"/images/create": postImagesCreate,
"/images/load": postImagesLoad,
"/images/{name:.*}/push": proxyImageTagOptional,
Expand Down
8 changes: 8 additions & 0 deletions api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,11 @@ func intValueOrZero(r *http.Request, k string) int {
}
return val
}

func int64ValueOrZero(r *http.Request, k string) int64 {
val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
if err != nil {
return 0
}
return val
}
21 changes: 21 additions & 0 deletions api/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,24 @@ func TestIntValueOrZero(t *testing.T) {
}
}
}

func TestInti64ValueOrZero(t *testing.T) {
cases := map[string]int64{
"": 0,
"asdf": 0,
"0": 0,
"1": 1,
}

for c, e := range cases {
v := url.Values{}
v.Set("test", c)
r, _ := http.NewRequest("POST", "", nil)
r.Form = v

a := int64ValueOrZero(r, "test")
if a != e {
t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
}
}
}
3 changes: 3 additions & 0 deletions cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,7 @@ type Cluster interface {

// RenameContainer rename a container
RenameContainer(container *Container, newName string) error

// BuildImage build an image
BuildImage(*dockerclient.BuildImage, io.Writer) error
}
8 changes: 7 additions & 1 deletion cluster/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (e *Engine) RemoveImage(image *Image, name string) ([]*dockerclient.ImageDe

// RefreshImages refreshes the list of images on the engine.
func (e *Engine) RefreshImages() error {
images, err := e.client.ListImages()
images, err := e.client.ListImages(false)
if err != nil {
return err
}
Expand Down Expand Up @@ -610,3 +610,9 @@ func (e *Engine) RenameContainer(container *Container, newName string) error {
// refresh container
return e.refreshContainer(container.Id, true)
}

// BuildImage build an image
func (e *Engine) BuildImage(buildImage *dockerclient.BuildImage) (io.ReadCloser, error) {

return e.client.BuildImage(buildImage)
}
16 changes: 8 additions & 8 deletions cluster/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestEngineCpusMemory(t *testing.T) {
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil)
client.On("ListImages").Return([]*dockerclient.Image{}, nil)
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()

assert.NoError(t, engine.ConnectWithClient(client))
Expand All @@ -85,7 +85,7 @@ func TestEngineSpecs(t *testing.T) {
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil)
client.On("ListImages").Return([]*dockerclient.Image{}, nil)
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()

assert.NoError(t, engine.ConnectWithClient(client))
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestEngineState(t *testing.T) {

// The client will return one container at first, then a second one will appear.
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{{Id: "one"}}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil).Once()
client.On("InspectContainer", "one").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: 100}}, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf("{%q:[%q]}", "id", "two")).Return([]dockerclient.Container{{Id: "two"}}, nil).Once()
client.On("InspectContainer", "two").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: 100}}, nil).Once()
Expand Down Expand Up @@ -159,7 +159,7 @@ func TestCreateContainer(t *testing.T) {
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil).Once()
assert.NoError(t, engine.ConnectWithClient(client))
assert.True(t, engine.isConnected())

Expand All @@ -172,7 +172,7 @@ func TestCreateContainer(t *testing.T) {
id := "id1"
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil).Once()
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
container, err := engine.Create(config, name, false)
assert.Nil(t, err)
Expand All @@ -195,7 +195,7 @@ func TestCreateContainer(t *testing.T) {
client.On("CreateContainer", &mockConfig, name).Return("", dockerclient.ErrNotFound).Once()
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil).Once()
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
container, err = engine.Create(config, name, true)
assert.Nil(t, err)
Expand Down Expand Up @@ -241,7 +241,7 @@ func TestUsedCpus(t *testing.T) {
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{{Id: "test"}}, nil).Once()
client.On("InspectContainer", "test").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: cpuShares}}, nil).Once()
engine.ConnectWithClient(client)
Expand All @@ -268,7 +268,7 @@ func TestContainerRemovedDuringRefresh(t *testing.T) {

client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListImages").Return([]*dockerclient.Image{}, nil)
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{container1, container2}, nil)
client.On("InspectContainer", "c1").Return(info1, errors.New("Not found"))
Expand Down
28 changes: 28 additions & 0 deletions cluster/mesos/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,31 @@ func (c *Cluster) RANDOMENGINE() (*cluster.Engine, error) {
}
return nil, nil
}

// BuildImage build an image
func (c *Cluster) BuildImage(buildImage *dockerclient.BuildImage, out io.Writer) error {
c.scheduler.Lock()

// get an engine
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{
CpuShares: buildImage.CpuShares,
Memory: buildImage.Memory,
}}
n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config)
c.scheduler.Unlock()
if err != nil {
return err
}

reader, err := c.slaves[n.ID].engine.BuildImage(buildImage)
if err != nil {
return err
}

if _, err := io.Copy(out, reader); err != nil {
return err
}

c.slaves[n.ID].engine.RefreshImages()
return nil
}
28 changes: 28 additions & 0 deletions cluster/swarm/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,3 +598,31 @@ func (c *Cluster) RenameContainer(container *cluster.Container, newName string)
st.Name = newName
return c.store.Replace(container.Id, st)
}

// BuildImage build an image
func (c *Cluster) BuildImage(buildImage *dockerclient.BuildImage, out io.Writer) error {
c.scheduler.Lock()

// get an engine
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{
CpuShares: buildImage.CpuShares,
Memory: buildImage.Memory,
}}
n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config)
c.scheduler.Unlock()
if err != nil {
return err
}

reader, err := c.engines[n.ID].BuildImage(buildImage)
if err != nil {
return err
}

if _, err := io.Copy(out, reader); err != nil {
return err
}

c.engines[n.ID].RefreshImages()
return nil
}
4 changes: 2 additions & 2 deletions cluster/swarm/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestImportImage(t *testing.T) {
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil)
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil)

// connect client
engine.ConnectWithClient(client)
Expand Down Expand Up @@ -175,7 +175,7 @@ func TestLoadImage(t *testing.T) {
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil).Once()
client.On("ListImages").Return([]*dockerclient.Image{}, nil)
client.On("ListImages", false).Return([]*dockerclient.Image{}, nil)

// connect client
engine.ConnectWithClient(client)
Expand Down
13 changes: 13 additions & 0 deletions test/integration/api/build.bats
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,17 @@ function teardown() {
stop_docker
}

@test "docker build" {
start_docker 2
swarm_manage

run docker_swarm images -q
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 0 ]

docker_swarm build -t test $TESTDATA/build

run docker_swarm images -q
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 1 ]
}

0 comments on commit a3cf5b9

Please sign in to comment.