Skip to content

Commit

Permalink
Merge pull request #18 from screwdriver-cd/buildfromid
Browse files Browse the repository at this point in the history
Started screwdriver.API.BuildFromID
  • Loading branch information
jer authored Aug 4, 2016
2 parents f32289d + 4cb2caa commit 4bf433e
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 48 deletions.
72 changes: 46 additions & 26 deletions launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,59 @@ package main
import (
"fmt"
"os"
"os/exec"
)

type shellOutInterface interface {
run(string, ...string) ([]byte, error)
}

var shellOut shellOutInterface

type shellOutImpl struct{}
"github.com/screwdriver-cd/launcher/screwdriver"
"github.com/urfave/cli"
)

func init() {
shellOut = shellOutImpl{}
}
// VERSION gets set by the build script via the LDFLAGS
var VERSION string

func (s shellOutImpl) run(cmd string, args ...string) (out []byte, err error) {
return exec.Command(cmd, args...).CombinedOutput()
}
func launch(api screwdriver.API, buildID string) error {
_, err := api.BuildFromID(buildID)
if err != nil {
return fmt.Errorf("fetching build ID %q: %v", buildID, err)
}

func runLauncher(args ...string) (out []byte, err error) {
cmdName := "/opt/screwdriver/launch.sh"
return shellOut.run(cmdName, args...)
return nil
}

func main() {
var out []byte
var err error
app := cli.NewApp()
app.Name = "launcher"
app.Usage = "launch Screwdriver jobs"
if VERSION == "" {
VERSION = "0.0.0"
}
app.Version = VERSION

app.Flags = []cli.Flag{
cli.StringFlag{
Name: "api-url",
Usage: "set the API URL for Screwrdriver",
},
cli.StringFlag{
Name: "tokenfile",
Usage: "set the JWT used for accessing Screwdriver's API",
},
}

fmt.Println("Launching the launcher...")
if out, err = runLauncher(os.Args[1:]...); err != nil {
fmt.Fprintln(os.Stderr, "There was an error shelling out to the launcher: ", err)
os.Exit(1)
app.Action = func(c *cli.Context) error {
url := c.String("api-url")
token := "odsjfadfg"
buildID := c.Args()[0]

api, err := screwdriver.New(url, token)
if err != nil {
fmt.Printf("creating Scredriver API %v: %v", buildID, err)
}

if err = launch(api, buildID); err != nil {
fmt.Printf("Error running launcher: %v", err)
os.Exit(1)
}
return nil
}
fmt.Println(string(out))
fmt.Println("Launched successfully.")
app.Run(os.Args)

}
59 changes: 37 additions & 22 deletions launch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,54 @@ import (
"fmt"
"strings"
"testing"

"github.com/screwdriver-cd/launcher/screwdriver"
)

var shellOutSetup map[string]string
type NewAPI func(buildID string, token string) (screwdriver.API, error)
type FakeAPI screwdriver.API
type FakeBuild screwdriver.Build

type shellOutMock struct{}
type MockAPI struct {
buildFromID func(string) (screwdriver.Build, error)
}

func (s shellOutMock) run(cmd string, args ...string) (out []byte, err error) {
cmdslice := []string{cmd}
cmdslice = append(cmdslice, args...)
func (f MockAPI) BuildFromID(buildID string) (screwdriver.Build, error) {
if f.buildFromID != nil {
return f.buildFromID(buildID)
}
return screwdriver.Build(FakeBuild{}), nil
}

cmd = strings.Join(cmdslice, " ")
output := shellOutSetup[cmd]
if output == "" {
return nil, fmt.Errorf("Error: %s not mocked", cmd)
func TestBuildFromId(t *testing.T) {
testID := "TESTID"
api := MockAPI{
buildFromID: func(buildID string) (screwdriver.Build, error) {
if buildID != testID {
t.Errorf("buildID == %v, want %v", buildID, testID)
}
return screwdriver.Build(FakeBuild{}), nil
},
}

return []byte(output), nil
launch(screwdriver.API(api), testID)
}

func Test(t *testing.T) {
shellOut = shellOutMock{}

shellOutSetup = map[string]string{
"/opt/screwdriver/launch.sh abc": "abc",
func TestBuildFromIdError(t *testing.T) {
api := MockAPI{
buildFromID: func(buildID string) (screwdriver.Build, error) {
err := fmt.Errorf("testing error returns")
return screwdriver.Build(FakeBuild{}), err
},
}

output, err := runLauncher("abc")
if string(output) != shellOutSetup["/opt/screwdriver/launch.sh abc"] {
errmsg := fmt.Sprintf("Expected %s, got %s", shellOutSetup["echo"], output)
t.Error(errmsg)
err := launch(screwdriver.API(api), "shoulderror")
if err == nil {
t.Errorf("err should not be nil")
}
if err != nil {
errmsg := fmt.Sprintf("Error running command: %s", err)
t.Error(errmsg)

expected := `fetching build ID "shoulderror"`
if !strings.Contains(err.Error(), expected) {
t.Errorf("err == %q, want %q", err, expected)
}
}
70 changes: 70 additions & 0 deletions screwdriver/screwdriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package screwdriver

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)

// API is a Screwdriver API endpoint
type API interface {
BuildFromID(buildID string) (Build, error)
}

type api struct {
url string
token string
client *http.Client
}

// New returns a new API object
func New(url, token string) (API, error) {
api := api{
url,
token,
&http.Client{},
}
return API(api), nil
}

// Build is a Screwdriver Build
type Build struct {
ID string `json:"id"`
JobID string `json:"jobId"`
}

func (a api) get(url string) ([]byte, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("Generating request to Screwdriver: %v", err)
}
token := fmt.Sprintf("Bearer %s", a.token)
req.Header.Set("Authorization", token)

response, err := a.client.Do(req)
if err != nil {
return nil, fmt.Errorf("Reading response from Screwdriver: %v", err)
}

body, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("Reading response Body from Screwdriver: %v", err)
}
return body, nil
}

// BuildFromID fetches and returns a Build object from its ID
func (a api) BuildFromID(buildID string) (build Build, err error) {
url := fmt.Sprintf("%s/builds/%s", a.url, buildID)
body, err := a.get(url)
if err != nil {
return build, fmt.Errorf("Reading response Body from Screwdriver: %v", err)
}

err = json.Unmarshal(body, &build)
if err != nil {
return build, fmt.Errorf("Parsing JSON response %q: %v", body, err)
}
return build, nil
}
68 changes: 68 additions & 0 deletions screwdriver/screwdriver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package screwdriver

import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)

func makeFakeHTTPClient(code int, body string, validator func(req *http.Request)) *http.Client {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
validator(r)
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, body)
}))

transport := &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse(server.URL)
},
}

return &http.Client{Transport: transport}
}

func validateHeader(t *testing.T, key, value string) func(r *http.Request) {
return func(r *http.Request) {
headers, ok := r.Header[key]
if !ok {
t.Fatalf("No %s header sent in Screwdriver request", key)
}
header := headers[0]
if header != value {
t.Errorf("%s header = %q, want %q", key, header, value)
}
}
}

func TestFromBuildId(t *testing.T) {
want := Build{
ID: "testId",
JobID: "testJob",
}
json, err := json.Marshal(want)
if err != nil {
t.Fatalf("Unable to Marshal JSON for test: %v", err)
}

wantToken := "faketoken"
wantTokenHeader := fmt.Sprintf("Bearer %s", wantToken)

validatorFunc := validateHeader(t, "Authorization", wantTokenHeader)
http := makeFakeHTTPClient(200, string(json), validatorFunc)

testAPI := api{"http://fakeurl", wantToken, http}
build, err := testAPI.BuildFromID(want.ID)

if err != nil {
t.Errorf("Unexpected error from BuildFromID: %v", err)
}

if build != want {
t.Errorf("build == %#v, want %#v", build, want)
}
}

0 comments on commit 4bf433e

Please sign in to comment.