Skip to content

Commit b28e2df

Browse files
committed
Merge pull request #4062 from Joshua-Anderson/add-builds
feat(client-go): add builds endpoint
2 parents 1755334 + 25e9ecf commit b28e2df

6 files changed

Lines changed: 425 additions & 0 deletions

File tree

client-go/cmd/builds.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"gopkg.in/yaml.v2"
7+
8+
"github.com/deis/deis/client-go/controller/models/builds"
9+
)
10+
11+
// BuildsList lists an app's builds.
12+
func BuildsList(appID string) error {
13+
c, appID, err := load(appID)
14+
15+
if err != nil {
16+
return err
17+
}
18+
19+
builds, err := builds.List(c, appID)
20+
21+
if err != nil {
22+
return err
23+
}
24+
25+
fmt.Printf("=== %s Builds\n", appID)
26+
27+
for _, build := range builds {
28+
fmt.Println(build.UUID, build.Created)
29+
}
30+
return nil
31+
}
32+
33+
// BuildsCreate creates a build for an app.
34+
func BuildsCreate(appID, image, procfile string) error {
35+
c, appID, err := load(appID)
36+
37+
if err != nil {
38+
return err
39+
}
40+
41+
var procfileMap map[string]string
42+
43+
if procfile != "" {
44+
err = yaml.Unmarshal([]byte(procfile), &procfileMap)
45+
46+
if err != nil {
47+
return err
48+
}
49+
}
50+
51+
fmt.Print("Creating build... ")
52+
quit := progress()
53+
_, err = builds.New(c, appID, image, procfileMap)
54+
quit <- true
55+
<-quit
56+
57+
if err != nil {
58+
return err
59+
}
60+
61+
fmt.Println("done")
62+
63+
return nil
64+
}

client-go/controller/api/builds.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package api
2+
3+
// Build is the structure of the build object.
4+
type Build struct {
5+
App string `json:"app"`
6+
Created string `json:"created"`
7+
Dockerfile string `json:"dockerfile,omitempty"`
8+
Image string `json:"image,omitempty"`
9+
Owner string `json:"owner"`
10+
Procfile map[string]string `json:"procfile"`
11+
Sha string `json:"sha,omitempty"`
12+
Updated string `json:"updated"`
13+
UUID string `json:"uuid"`
14+
}
15+
16+
// Builds is the structure of GET /v1/apps/<app id>/builds/.
17+
type Builds struct {
18+
Count int `json:"count"`
19+
Next int `json:"next"`
20+
Previous int `json:"previous"`
21+
Builds []Build `json:"results"`
22+
}
23+
24+
// CreateBuildRequest is the structure of POST /v1/apps/<app id>/builds/.
25+
type CreateBuildRequest struct {
26+
Image string `json:"image"`
27+
Procfile map[string]string `json:"procfile,omitempty"`
28+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package builds
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/deis/deis/client-go/controller/api"
9+
"github.com/deis/deis/client-go/controller/client"
10+
)
11+
12+
// List lists an app's builds.
13+
func List(c *client.Client, appID string) ([]api.Build, error) {
14+
u := fmt.Sprintf("/v1/apps/%s/builds/", appID)
15+
body, status, err := c.BasicRequest("GET", u, nil)
16+
17+
if err != nil {
18+
return []api.Build{}, err
19+
}
20+
21+
if status != 200 {
22+
return []api.Build{}, errors.New(body)
23+
}
24+
25+
builds := api.Builds{}
26+
if err = json.Unmarshal([]byte(body), &builds); err != nil {
27+
return []api.Build{}, err
28+
}
29+
30+
return builds.Builds, nil
31+
}
32+
33+
// New creates a build for an app.
34+
func New(c *client.Client, appID string, image string,
35+
procfile map[string]string) (api.Build, error) {
36+
37+
u := fmt.Sprintf("/v1/apps/%s/builds/", appID)
38+
39+
req := api.CreateBuildRequest{Image: image, Procfile: procfile}
40+
41+
body, err := json.Marshal(req)
42+
43+
if err != nil {
44+
return api.Build{}, err
45+
}
46+
47+
resBody, status, err := c.BasicRequest("POST", u, body)
48+
49+
if err != nil {
50+
return api.Build{}, err
51+
}
52+
53+
if status != 201 {
54+
return api.Build{}, errors.New(resBody)
55+
}
56+
57+
build := api.Build{}
58+
if err = json.Unmarshal([]byte(resBody), &build); err != nil {
59+
return api.Build{}, err
60+
}
61+
62+
return build, nil
63+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package builds
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
"net/http/httptest"
8+
"net/url"
9+
"reflect"
10+
"testing"
11+
12+
"github.com/deis/deis/client-go/controller/api"
13+
"github.com/deis/deis/client-go/controller/client"
14+
"github.com/deis/deis/version"
15+
)
16+
17+
const buildsFixture string = `
18+
{
19+
"count": 1,
20+
"next": null,
21+
"previous": null,
22+
"results": [
23+
{
24+
"app": "example-go",
25+
"created": "2014-01-01T00:00:00UTC",
26+
"dockerfile": "FROM deis/slugrunner RUN mkdir -p /app WORKDIR /app ENTRYPOINT [\"/runner/init\"] ADD slug.tgz /app ENV GIT_SHA 060da68f654e75fac06dbedd1995d5f8ad9084db",
27+
"image": "example-go",
28+
"owner": "test",
29+
"procfile": {
30+
"web": "example-go"
31+
},
32+
"sha": "060da68f",
33+
"updated": "2014-01-01T00:00:00UTC",
34+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
35+
}
36+
]
37+
}`
38+
39+
const buildFixture string = `
40+
{
41+
"app": "example-go",
42+
"created": "2014-01-01T00:00:00UTC",
43+
"dockerfile": "",
44+
"image": "deis/example-go:latest",
45+
"owner": "test",
46+
"procfile": {
47+
"web": "example-go"
48+
},
49+
"sha": "",
50+
"updated": "2014-01-01T00:00:00UTC",
51+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
52+
}`
53+
54+
const buildExpected string = `{"image":"deis/example-go","procfile":{"web":"example-go"}}`
55+
56+
type fakeHTTPServer struct{}
57+
58+
func (fakeHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
59+
res.Header().Add("DEIS_API_VERSION", version.APIVersion)
60+
61+
if req.URL.Path == "/v1/apps/example-go/builds/" && req.Method == "GET" {
62+
res.Write([]byte(buildsFixture))
63+
return
64+
}
65+
66+
if req.URL.Path == "/v1/apps/example-go/builds/" && req.Method == "POST" {
67+
body, err := ioutil.ReadAll(req.Body)
68+
69+
if err != nil {
70+
fmt.Println(err)
71+
res.WriteHeader(http.StatusInternalServerError)
72+
res.Write(nil)
73+
}
74+
75+
if string(body) != buildExpected {
76+
fmt.Printf("Expected '%s', Got '%s'\n", buildExpected, body)
77+
res.WriteHeader(http.StatusInternalServerError)
78+
res.Write(nil)
79+
return
80+
}
81+
82+
res.WriteHeader(http.StatusCreated)
83+
res.Write([]byte(buildFixture))
84+
return
85+
}
86+
87+
fmt.Printf("Unrecognized URL %s\n", req.URL)
88+
res.WriteHeader(http.StatusNotFound)
89+
res.Write(nil)
90+
}
91+
92+
func TestBuildsList(t *testing.T) {
93+
t.Parallel()
94+
95+
expected := []api.Build{
96+
api.Build{
97+
App: "example-go",
98+
Created: "2014-01-01T00:00:00UTC",
99+
Dockerfile: "FROM deis/slugrunner RUN mkdir -p /app WORKDIR /app ENTRYPOINT [\"/runner/init\"] ADD slug.tgz /app ENV GIT_SHA 060da68f654e75fac06dbedd1995d5f8ad9084db",
100+
Image: "example-go",
101+
Owner: "test",
102+
Procfile: map[string]string{
103+
"web": "example-go",
104+
},
105+
Sha: "060da68f",
106+
Updated: "2014-01-01T00:00:00UTC",
107+
UUID: "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
108+
},
109+
}
110+
111+
handler := fakeHTTPServer{}
112+
server := httptest.NewServer(handler)
113+
defer server.Close()
114+
115+
u, err := url.Parse(server.URL)
116+
117+
if err != nil {
118+
t.Fatal(err)
119+
}
120+
121+
httpClient := client.CreateHTTPClient(false)
122+
123+
client := client.Client{HTTPClient: httpClient, ControllerURL: *u, Token: "abc"}
124+
125+
actual, err := List(&client, "example-go")
126+
127+
if err != nil {
128+
t.Fatal(err)
129+
}
130+
131+
if !reflect.DeepEqual(expected, actual) {
132+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
133+
}
134+
}
135+
136+
func TestBuildCreate(t *testing.T) {
137+
t.Parallel()
138+
139+
expected := api.Build{
140+
App: "example-go",
141+
Created: "2014-01-01T00:00:00UTC",
142+
Image: "deis/example-go:latest",
143+
Owner: "test",
144+
Procfile: map[string]string{
145+
"web": "example-go",
146+
},
147+
Updated: "2014-01-01T00:00:00UTC",
148+
UUID: "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
149+
}
150+
151+
handler := fakeHTTPServer{}
152+
server := httptest.NewServer(handler)
153+
defer server.Close()
154+
155+
u, err := url.Parse(server.URL)
156+
157+
if err != nil {
158+
t.Fatal(err)
159+
}
160+
161+
httpClient := client.CreateHTTPClient(false)
162+
163+
client := client.Client{HTTPClient: httpClient, ControllerURL: *u, Token: "abc"}
164+
165+
procfile := map[string]string{
166+
"web": "example-go",
167+
}
168+
169+
actual, err := New(&client, "example-go", "deis/example-go", procfile)
170+
171+
if err != nil {
172+
t.Fatal(err)
173+
}
174+
175+
if !reflect.DeepEqual(expected, actual) {
176+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
177+
}
178+
}

client-go/deis.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Use 'git push deis master' to deploy to an application.
8585
err = parser.Apps(argv)
8686
case "domains":
8787
err = parser.Domains(argv)
88+
case "builds":
89+
err = parser.Builds(argv)
8890
case "keys":
8991
err = parser.Keys(argv)
9092
case "users":

0 commit comments

Comments
 (0)