Skip to content

Commit

Permalink
Merge pull request #30 from screwdriver-cd/validator
Browse files Browse the repository at this point in the history
Added Screwdriver YAML parsing via the /validator endpoint
  • Loading branch information
stjohnjohnson authored Aug 5, 2016
2 parents 6e59496 + 39a55ea commit 88b9b67
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 16 deletions.
106 changes: 90 additions & 16 deletions screwdriver/screwdriver.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package screwdriver

import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
Expand Down Expand Up @@ -42,12 +44,30 @@ func New(url, token string) (API, error) {
return API(api), nil
}

// Validator is a Screwdriver Validator payload.
type Validator struct {
Yaml string `json:"yaml"`
}

// Pipeline is a Screwdriver Pipeline definition
type Pipeline struct {
ID string `json:"id"`
ScmURL string `json:"scmUrl"`
}

// PipelineDef contains the step definitions and jobs for a Pipeline
type PipelineDef struct {
Jobs map[string][]JobDef `json:"jobs"`
Workflow []string `json:"workflow"`
}

// JobDef contains the step and environment definitions of a single Job
type JobDef struct {
Image string `json:"image"`
Steps map[string]string `json:"steps"`
Environment map[string]string `json:"environment"`
}

// Job is a Screwdriver Job
type Job struct {
ID string `json:"id"`
Expand All @@ -66,35 +86,58 @@ func (a api) makeURL(path string) (*url.URL, error) {
return url.Parse(fullpath)
}

func tokenHeader(token string) string {
return fmt.Sprintf("Bearer %s", token)
}

func handleResponse(res *http.Response) ([]byte, error) {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("reading response Body from Screwdriver: %v", err)
}

if res.StatusCode/100 != 2 {
var err SDError
parserr := json.Unmarshal(body, &err)
if parserr != nil {
return nil, fmt.Errorf("unparseable error response from Screwdriver: %v", parserr)
}
return nil, err
}
return body, nil
}

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

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

body, err := ioutil.ReadAll(response.Body)
return handleResponse(res)
}

func (a api) post(url *url.URL, bodyType string, payload io.Reader) ([]byte, error) {
req, err := http.NewRequest("POST", url.String(), payload)
if err != nil {
return nil, fmt.Errorf("Reading response Body from Screwdriver: %v", err)
return nil, fmt.Errorf("generating request to Screwdriver: %v", err)
}
req.Header.Set("Content-Type", bodyType)
req.Header.Set("Authorization", tokenHeader(a.token))

if response.StatusCode/100 != 2 {
var err SDError
parserr := json.Unmarshal(body, &err)
if parserr != nil {
return nil, fmt.Errorf("Unparseable error response from Screwdriver: %v", parserr)
}
return nil, err
res, err := a.client.Do(req)
if err != nil {
return nil, fmt.Errorf("posting to Screwdriver endpoint %v: %v", url, err)
}
defer res.Body.Close()

return body, nil
return handleResponse(res)
}

// BuildFromID fetches and returns a Build object from its ID
Expand Down Expand Up @@ -149,3 +192,34 @@ func (a api) PipelineFromID(pipelineID string) (pipeline Pipeline, err error) {
}
return pipeline, nil
}

func (a api) PipelineDefFromYaml(yaml io.Reader) (PipelineDef, error) {
u, err := a.makeURL("/validator")
if err != nil {
return PipelineDef{}, err
}

y, err := ioutil.ReadAll(yaml)
if err != nil {
return PipelineDef{}, fmt.Errorf("reading Screwdriver YAML: %v", err)
}

v := Validator{string(y)}
payload, err := json.Marshal(v)
if err != nil {
return PipelineDef{}, fmt.Errorf("marshaling JSON for Validator: %v", err)
}

res, err := a.post(u, "application/json", bytes.NewReader(payload))
if err != nil {
return PipelineDef{}, fmt.Errorf("posting to Validator: %v", err)
}

var pipelineDef PipelineDef
err = json.Unmarshal(res, &pipelineDef)
if err != nil {
return PipelineDef{}, fmt.Errorf("parsing JSON response from the Validator: %v", err)
}

return pipelineDef, nil
}
59 changes: 59 additions & 0 deletions screwdriver/screwdriver_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package screwdriver

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

Expand Down Expand Up @@ -176,3 +178,60 @@ func TestPipeline404(t *testing.T) {
t.Fatalf("err = %v, want %v", err, want)
}
}

func TestPipelineFromYaml(t *testing.T) {
json := `{
"jobs": {
"main": [
{
"image": "node:4",
"steps": {
"install": "npm install"
},
"environment": {
"ARRAY": "[\"a\",\"b\"]",
"BOOL": "true",
"OBJECT": "{\"a\":\"cat\",\"b\":\"dog\"}",
"NUMBER": "3",
"DECIMAL": "3.1415927",
"STRING": "test"
}
}
]
},
"workflow": []
}`

mainJob := JobDef{
Image: "node:4",
Steps: map[string]string{
"install": "npm install",
},
Environment: map[string]string{
"ARRAY": `["a","b"]`,
"BOOL": "true",
"OBJECT": `{"a":"cat","b":"dog"}`,
"NUMBER": "3",
"DECIMAL": "3.1415927",
"STRING": "test",
},
}

wantJobs := map[string][]JobDef{
"main": []JobDef{
mainJob,
},
}

yaml := bytes.NewBufferString("")
http := makeFakeHTTPClient(t, 200, json)
testAPI := api{"http://fakeurl", "faketoken", http}
p, err := testAPI.PipelineDefFromYaml(yaml)
if err != nil {
t.Errorf("Unexpected error from PipelineDefFromYaml: %v", err)
}

if !reflect.DeepEqual(p.Jobs, wantJobs) {
t.Errorf("PipelineDef.Jobs = %+v, \n want %+v", p.Jobs, wantJobs)
}
}

0 comments on commit 88b9b67

Please sign in to comment.