Skip to content

Commit

Permalink
Inject metadata into events.json
Browse files Browse the repository at this point in the history
- Extract JSON fetching into a separate method, so it can
  follow the same logic as other endpoints.

[#20]

Signed-off-by: Jacques Chester <[email protected]>
  • Loading branch information
jchesterpivotal authored and jchester committed Oct 6, 2018
1 parent 3534d29 commit d013b6b
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ Will produce a number of files in the resource directory.
* `job.json`: the Job structure
* `versioned_resource_types.json`: information about non-core resource types used in the build. This covers things
added to a pipeline using `resource_types:`, but not core resources like `git-resource`.
* `events.json`: an array of JSON objects from the Job, based on the eventstream sent to `fly` or the web UI.
* `events.json`: contains an array of JSON objects based on the eventstream sent to `fly` or the web UI.
* `events.log`: the rendered logs from the Job, as they would appear in `fly` or the web UI.

Use `events.log` if you just want to slurp text output.
Use `events.log` if you just want to slurp text output. The `events.json` file is not a literal transcription of
the eventstream. Instead an object is constructed containing an array of event objects, as well as injected
metadata.

### The original resources with information encoded in the filename

Expand Down
101 changes: 53 additions & 48 deletions pkg/in/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ type versionedResourceTypesWrapper struct {
VersionedResourceTypes atc.VersionedResourceTypes `json:"versioned_resource_types"`
}

type eventsWrapper struct {
Events []atc.Event `json:"events"`
}

type inner struct {
inRequest *config.InRequest
concourseClient gc.Client
Expand All @@ -40,6 +44,7 @@ type inner struct {
plan atc.PublicBuildPlan
job atc.Job
versionedResourceTypes versionedResourceTypesWrapper
events eventsWrapper
buildId int
}

Expand Down Expand Up @@ -113,8 +118,25 @@ func (i inner) In() (*config.InResponse, error) {
return nil, err
}

// events
err = i.writeEventsLogAndJson()
// Events need to be fetched twice: once for the .json, once for the .log.
// The json follows the same basic logic as other endpoints. The log file however
// requires me to use a Concourse package to render it. This has clever EOFing
// stuff that I don't feel comfortable trying to reverse engineer, hence the
// double-handling of events.

// events part 1: JSON
err = i.getEventsForJson()
if err != nil {
return nil, err
}

err = i.writeJsonFile("events", i.events)
if err != nil {
return nil, err
}

// events part 2: rendering to pretty text
err = i.getAndWriteRenderedEventLog()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -253,12 +275,36 @@ func (i *inner) getVersionedResourceTypes() error {
return nil
}

// This method is gross because it needs to fetch events twice: once for the .log, once for the .json.
// This has to do with the clever way events are handled by Concourse and also to do with my unwillingness
// to completely and properly tease apart a smarter way to do this.
func (i *inner) writeEventsLogAndJson() error {
//////////////////////// Rendered log ////////////////////////
func (i *inner) getEventsForJson() error {
eventsForJsonFile, err := i.concourseClient.BuildEvents(i.inRequest.Version.BuildId)
if err != nil && err.Error() == "not authorized" {
log.Printf("was unauthorized to fetch events for build '%s', no event JSON will be written.", i.inRequest.Version.BuildId)
return nil
}
if err != nil {
return fmt.Errorf("error while fetching events for build '%s': '%s", i.inRequest.Version.BuildId, err.Error())
}
defer eventsForJsonFile.Close()

eventsArr := make([]atc.Event, 0)
for {
ev, err := eventsForJsonFile.NextEvent()
if err != nil {
if err == io.EOF {
break
}
return err
}

eventsArr = append(eventsArr, ev)
}

i.events = eventsWrapper{Events: eventsArr}

return nil
}

func (i *inner) getAndWriteRenderedEventLog() error {
eventsForLogFile, err := i.concourseClient.BuildEvents(i.inRequest.Version.BuildId)
// first, check if we are even authorised
if err != nil && err.Error() == "not authorized" {
Expand Down Expand Up @@ -291,47 +337,6 @@ func (i *inner) writeEventsLogAndJson() error {
return err
}

//////////////////////// JSON ////////////////////////

eventsForJsonFile, err := i.concourseClient.BuildEvents(i.inRequest.Version.BuildId)
defer eventsForJsonFile.Close()

jsonBuilder := &strings.Builder{}
jsonEnc := json.NewEncoder(jsonBuilder)
for {
ev, err := eventsForJsonFile.NextEvent()
if err != nil {
if err == io.EOF {
break
}
return err
}

err = jsonEnc.Encode(ev)
if err != nil {
return err
}
jsonBuilder.WriteString(",")
}
jsonStr := fmt.Sprintf("[%s]", strings.TrimSuffix(jsonBuilder.String(), ","))
unadornedJsonPath := filepath.Join(i.inRequest.WorkingDirectory, "events.json")
err = ioutil.WriteFile(unadornedJsonPath, []byte(jsonStr), os.ModePerm)
if err != nil {
return err
}

detailedJsonPath := filepath.Join(i.inRequest.WorkingDirectory, fmt.Sprintf("%s.json", i.addDetailedPostfixTo("events")))
_, err = fileutils.CopyFile(unadornedJsonPath, detailedJsonPath)
if err != nil {
return err
}

numberedJsonPath := filepath.Join(i.inRequest.WorkingDirectory, fmt.Sprintf("%s.json", i.addBuildNumberPostfixTo("events")))
_, err = fileutils.CopyFile(unadornedJsonPath, numberedJsonPath)
if err != nil {
return err
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/in/in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func TestInPkg(t *testing.T) {
gt.Expect(AFileExistsContaining("build/plan.json", `"concourse_build_resource":{"release":"v0.99.11","git_ref":"abcdef1234567890","get_timestamp":1234567890,"concourse_version":"3.99.11","get_uuid":"96d7128f-bacf-4f60-9ffd-1a9ca4c9e1d7"},`, gt)).To(gomega.BeTrue())
gt.Expect(AFileExistsContaining("build/resources.json", `"concourse_build_resource":{"release":"v0.99.11","git_ref":"abcdef1234567890","get_timestamp":1234567890,"concourse_version":"3.99.11","get_uuid":"96d7128f-bacf-4f60-9ffd-1a9ca4c9e1d7"},`, gt)).To(gomega.BeTrue())
gt.Expect(AFileExistsContaining("build/job.json", `"concourse_build_resource":{"release":"v0.99.11","git_ref":"abcdef1234567890","get_timestamp":1234567890,"concourse_version":"3.99.11","get_uuid":"96d7128f-bacf-4f60-9ffd-1a9ca4c9e1d7"},`, gt)).To(gomega.BeTrue())
gt.Expect(AFileExistsContaining("build/events.json", `"concourse_build_resource":{"release":"v0.99.11","git_ref":"abcdef1234567890","get_timestamp":1234567890,"concourse_version":"3.99.11","get_uuid":"96d7128f-bacf-4f60-9ffd-1a9ca4c9e1d7"},`, gt)).To(gomega.BeTrue())
})

it("writes out a concourse_build_resource_release file", func() {
Expand Down

0 comments on commit d013b6b

Please sign in to comment.