jchesterpivotal / concourse-build-resource Goto Github PK
View Code? Open in Web Editor NEWA Concourse resource for observing Concourse builds
License: Apache License 2.0
A Concourse resource for observing Concourse builds
License: Apache License 2.0
Right now put
/ out
is a no-op.
What it should do is trigger a build.
This would first require authentication logic, which is a whole thing.
Not that I particularly like being on the bleeding edge of things, but GOPATH is basically easy-CI kryptonite. So I am going to go through this in order to make #7 easier.
Currently, the resource injects 4 metadata (its release version, its git ref, the timestamp and the remote Concourse version).
It injects these into all of the JSON files it produces. This makes it easy to get basic provenance of a record inserted into a database.
What I would like to have is a trustworthy unique identity for any particular get. None of the existing metadata, singly or in combination, is able to do this. In particular there's a temptation to assume that together they will be unique, but that's not true: I could be running jobs in parallel. Or I might be running the resource in multiple places and pooling the data.
To guarantee uniqueness, add a UUID to the injected metadata. This simplifies insert logic and lets me tie together all the information ever retrieved on a particular occasion.
As released in v0.11.0.
When I compare the browser readout of events with the events.json
file written by the resource, I see that the latter is missing a lot of information I would very much like to read.
In particular, the event type (eg, log
, finish-put
) is missing.
The root of the problem seems to be that what I get from Concourse's stream handling code is the atc.Event
type. What I actually want is the atc.Envelope
type, which has the data put into atc.Event
as well as the event type/type version information that's currently missing.
Turns out there's an endpoint I overlooked.
Will probably be a little tedious to go through and backfill the various files and whatnot, but the additional of access to the triggering info is worth the effort IMV.
job.json
job-12345.json
team_pipeline-name_job-name.json
tasks/show-job
One of my purposes is to capture data about builds and resource versions for further analysis. In practice this means I will be feeding a database. I imagine other folks will want to do this too.
As well as JSON, spitting out one or more files with SQL inserts would be useful. The files could be picked up by subsequent tasks or resources for further action.
postgresql-database
or perhaps gcp-bigquery
resource, or is that too much effort vs value?show-sql-inserts
utility taskJob logs/events are often private, even if the overall pipeline is public. In the original events.log
implementation, receiving a "not authorized" message from the ATC would lead to skipping writing the events.log
file.
Because the logic for events.json
follows a distinct path, it is not correctly skipping. Instead, it collects events and stores them in a null
, which is faithfully written out to disk. This is because whereas the events.log
file is handled in a single specialised method, events.json
is handled by two routines following the convention of other endpoints: a fetch method and a write method.
The presence of an incomplete events.json
file confuses downstream tools which expect the existence/non-existence of the file to signal whether the events could be retrieved.
Stop doing that. The logic which fetches events for encoding as a JSON array embedded in events.json
should somehow signal that the file is not to be written.
For example, it seems quite easy to hang on fetching event streams.
This resource scrapes remote Concourses. It relies on go-concourse
, for which the upstream code generally tracks the latest Concourse release. While the API should be stable, we live in a world of chaos; incompatibilities may well arise.
For debugging and record-keeping purposes, users may wish to snapshot the version of Concourse their data was scraped from.
This information is visible in the x-concourse-version
header in API responses.
concourse_build_resource
object.concourse_version
K-V file.I believe this happens because, when event streams are unauthorised (ie, the logs are not public), it returns a nil. Then I defer
a .Close()
call, which is invoked during error-handling. Kablammo.
Right now I basically ignore the business of keeping in sync with the upstream code from Concourse (go-concourse
, atc
etc). This is in part because Concourse itself is sprawled over multiple repositories that are not really synced with each other within the repositories. The syncing is achieved as a by-product of manufacturing BOSH releases, but not applied back to the repositories in a way that go modules can easily consume.
At the moment I have a little bit of a Frankenstein's go.mod
file to try and approximately pin versions. This works, but has problems:
fly
policy and wash my hands of it?The Concourse team are aware of the problems of multi-repo sprawl and are working towards consolidating into a monorepo.
When that work lands and stabilises, I anticipate that all my direct dependencies on Concourse code will be safely consumable as a single version line in go.mod
. I will be able to introduce pipeline jobs to monitor and update this resource to keep in sync with the upstream.
None currently; the issue is blocked on the upstream work.
Suppose I have a massive directory full of JSON files. Which version of concourse-build-resource is responsible? Perhaps there is a bug and I need to redo some scraping. Or perhaps a later version has information an earlier version didn't capture, and I want to know that.
If I set initial_build_id
to 100, but the earliest global build number visible for a team/pipeline/job combination is 101, the resource will "hang" after fetching version 100. This is because I use since
as my search parameter, which returns an empty array for build IDs that are below its range.
Use limit=1
and until=1
to find the earliest version visible for a given team/pipeline/job. If it is above initial_build_id
, use that number instead.
The versioned resource types endpoint for a pipeline returns very useful information about the configuration of resources at the time they were used.
The distinction may vanish in future, but for now it's very helpful.
As a lazy person, I don't want to write code to read JSON so I can do smart things with it. I want someone else to explode my incoming data into a variety of shapes that I can use easily.
put
ting to file stores using glob patterns)
cat
et al in simple task scripts)build-pass-fail
to use the status
file instead of parsing JSON.In the current design, a user fully specifies the team, pipeline and job they wish to follow:
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
team: main
pipeline: example-pipeline
job: some-job-you-are-interested-in
This is good if I want to just follow a single job. But it is a hassle when scaling up for two reasons.
Repetition. If I am following twenty jobs, I need to create a resource for every job. But these resources will have repeated configuration for URL, team and pipeline. This is unwieldy in terms of YAML and also, means I am spamming Concourse with 20 check containers instead of a single check container.
Dynamism. If I want to keep my scraping up to date, I will need to manually reconcile my scraping pipeline with target jobs. If a job is added on the target system, it does not get watched automatically. If the job is removed, my pipeline breaks.
Under the hood this resource uses go-concourse
to interact with Concourse. Based on an eyeball inspection, it will be possible to loosen the restriction, so that one can track jobs to an arbitrary level of the hierarchy.
Afterwards, these would all be legal configurations:
# Track builds of a single job in single pipeline in single team
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
team: main
pipeline: example-pipeline
job: some-job-you-are-interested-in
# Track builds of all jobs in a single pipeline in a single team
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
team: main
pipeline: example-pipeline
# Track builds of all jobs in all pipelines in a single team
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
team: main
# Track builds of all jobs in all pipelines in all teams (ie, track everything)
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
When a user provides a URL of the form https://example.com/
instead of https://example.com
, it leads to a whole bunch of //
in the API paths assembled by go-concourse
.
Right now the versioning is based purely on the build ID. But the build ID is not a single, immutable snapshot of the external state, because a build can meaningfully evolve under the same ID number.
Instead, the version should be ID + a build status.
For example, 123+pending
is different from 123+started
, which is different again from 123+failed
.
Users of the resource will probably only be interested in one or two statuses. So it should be possible to filter -- "only show me new passes", etc, with "show me everything for every build" as the default behaviour. I'd like feedback on whether that's better as a get
param or as a source
param.
They come back from a remote Concourse in the form of 3, 2, 1 etc; but check
needs to turn this into 1, 2, 3.
As a consequence of copying and pasting great slabs due to impatience.
Currently, the resource renders events out to events.log
. This is useful for folks wanting raw loglines.
It would be nice to have access to the original JSON form (say events.json
), however. This would allow analysis of timestamps and grouping into the various steps.
To save on database usage, Concourse operators can configure their ATC to only retain a certain number of build logs. This appears to upset the logic I use when catching up from initial_build_id
.
Often the README is showing features that don't yet exist in the latest release. For users who pull the latest
docker image, this can lead to confusing behaviour.
The response from the endpoint is an array, not an object, so my hacky trick of using string substitution on parentheses instead injects into the first resource in the array:
[
{
"concourse_build_resource": {
"release": "0.10.0",
"git_ref": "d3767db",
"get_timestamp": 1537644501,
"concourse_version": "[redacted]",
"get_uuid": "[redacted]"
},
"name": "bosh-deployment",
"type": "docker-image",
"source": {
"repository": "[redacted]"
},
"privileged": false,
"check_every": "",
"tags": null,
"params": null,
"version": {
"digest": "sha256:[redacted]"
}
},
{
"name": "cron-resource",
"type": "docker-image",
"source": {
"repository": "[redacted]"
},
"privileged": false,
"check_every": "",
"tags": null,
"params": null,
"version": {
"digest": "sha256:[redacted]"
}
}
]
I can see two possibilities:
I'm inclined to prefer option 2, as it doesn't require someone iterating over the array to add special-case logic to skip the metadata object.
Currently, the resource only works for public jobs in public pipelines.
This is rather limiting.
Something like:
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
authentication:
# ???
Assuming I focus on github only and split out other auth options into standalone issues.
Currently the resource outputs team, pipeline, job etc. It also outputs url
, which is the full build URL.
But actually, there are a bunch of different URLs I might want:
concourse_url
: the base URL of the Concourse. This would be the same as the concourse_url
passed into source.team_url
: the URL for the teampipeline_url
: the URL for the pipelinejob_url
: the URL for the job (defaulting to latest build)build_url
: the URL for the build -- what url
is currently.At the moment, the resource begins with the most recent build from the remote Concourse. But I might actually want to start with a historical version and then work forwards.
Suppose I have a Concourse with 100 builds. If I have this configuration:
- name: build
type: concourse-build
source:
concourse_url: https://concourse.example.com
Then the resource will start at build 100: [{"build_id","100"}]
.
But what I want to be able to do is start with, say, build 95:
source:
concourse_url: https://concourse.example.com
initial_build_id: 95
Then the resource would return [{"build_id","95"}]
for the first invocation (ie, it would not dial out at all, but simply return the configured value). When invoked the second time it would detect the difference and return the rest: [{"build_id","96"}, {"build_id","97"}, {"build_id","98"}, {"build_id","99"}, {"build_id","100"}]
.
build_id
(the actual version) and the build name / build number shown in the web UI for each individual job?Since
for API pagingSuppose I get this resource.json
:
{
"inputs": [
{
"name": "stable-pool",
"resource": "stable-pool",
"type": "pool",
"version": {
"ref": "7a11a45a5f3f62e42d5d5e50df993699fe9ddd5f"
},
"metadata": [
{
"name": "lock_name",
"value": "bellatrix"
},
{
"name": "pool_name",
"value": "cf-deployment/stable"
}
],
"pipeline_id": 9,
"first_occurrence": true
}
],
"outputs": [
{
"id": 0,
"type": "",
"metadata": null,
"resource": "stable-pool",
"version": {
"ref": "950a4bdde704c758b9184b2092895227a7ae10a6"
},
"enabled": false
}
]
}
Here I see that stable-pool
appears twice because it appears in both get
and put
steps of the underlying build. But information is missing from the output
that was present in the input
. In this case I can deduce this information from reading the input
description, but it is common to have a put
with no corresponding get
in the same job. That means I will be stuck with less information than I want.
It appears that this is due to the current implementation in Concourse. The input
array is populated with PublicBuildInput
type, but there is no corresponding PublicBuildOutput
type. Instead the outputs are populated with VersionedResource
objects. In turn the information for both is fetched from the database in build.Resources()
, which appears to be intended to populate the PublicBuildInput
type, as it selects out of the build_inputs
table but not the build_outputs
table. Separately there is a build.GetVersionedResources()
method which selects from both build_inputs
and build_outputs
, but which returns the VersionedResource
type. It is not used by the endpoint that resources.json
is based on.
Concourse also exposes a toplevel /api/v1/resources
endpoint. This endpoint is agnostic to input or output status and shows all (publicly-visible) resources across the entire Concourse instance. The data in this endpoint can fill some of the missing information for the output
entries (eg, name, type) but not all (version metadata).
It's not ideal, but it's better than the current state of affairs.
It is confusing and annoying.
Switch the current team-pipeline-job files to team_pipeline_job. This is because pipelines and jobs are typically named using hyphens, which means the filenames are ambiguous to a casual glance.
So, for example, plan-main-a-pipeline-name-a-job-name-123.json
would become plan_main_a-pipeline-name_a-job-name_123.json
. A little jarring, but at least consistent.
So after the first page is processed, things stop proceeding, because I switched to using Since
paging.
This is not quite a regression from #13, but it defeats the purpose of having initial_build_id
.
The problem seems to be that sometimes builds come back in different orders: sometimes ascending, sometimes descending. So my simple tactic of reversing the order isn't always going to work.
Suppose I have two independent groups with independent Concourses: Upstream and Downstream.
Upstream produces software in their Concourse pipelines which is then uploaded to a repository. In the Downstream pipelines, these uploads are detected and the Downstream team performs more processing. An example is having a central team performing security scanning, legal reviews, integration testing for major products etc.
When Downstream is watching only one Upstream, it is possible for these teams to coordinate. Downstream can add the Upstream pipeline to their monitors. But when Downstream is watching many outputs from many groups, this becomes untenable.
A feedback loop from Downstream's pipeline to Upstream's pipeline. Upstream want to know if particular resource versions they emitted from a pipeline caused a Downstream failure. To do so, Upstream needs a Job that will fail when Downstream fails.
This resource provides one part of the solution (fetching build results). But it's not enough. Also required is a way to convert that information into a pass/fail signal.
So: add a task that can be dropped into existing pipelines.
As a Pivot
I want to have tested code
So that I may be received in polite company
Given a codebase which is untested
When I backfill the tests
Then I should feel less guilty
Currently, all the fetches and writes performed during in
are done sequentially.
Golang is meant to be good at this concurrency stuff. Maybe parallelise the fetch/write for each endpoint.
Right now, the README is edited by hand and the utility task YAMLs are all swinging wild and free without any versioning at all.
A task that edits these files to substitute in the release version, with a commit made back to the repo, should probably go in shipit
.
Currently it's hardcoded to 50. This is the value used in go-concourse
.
This limits the use of fly check-resource --from
to collect historical build information.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.