Skip to content

Commit

Permalink
Add Hodometer stub receiver (#211)
Browse files Browse the repository at this point in the history
* Add structs representing events in MixPanel format

* Add test cases for parsing events in MixPanel format from JSON

* Add custom unmarshaller for event properties

The properties are a combination of expected fields and arbitrary additional ones.
We could simply use a map, but that does not adequately represent the expected fields,
making calling code more tedious with existence checks.

* Add recorder/store for incoming events

These events are recorded in order of incidence.

* Make recorder thread-safe with a write lock

* Add (WIP) HTTP server compatible with MixPanel's Track API

* Return 400 status code for unparsable events

* Join simple lines in handler logic

* Add no-op recorder for testing, decorators, etc.

* Add recorder decorator counting number of events seen

* Use more idiomatic REST error handling

* Add basic cmd package for stub receiver

Here, 'basic' means with hard-coded values.

* Refactor Make build targets to cmd-specific & all Hodometer apps

* Add constructor for no-op recorder

* Allow both GET & POST requests in stub receiver

* Refactor event decoding to method in stub receiver

* Refactor Status response handling to method in stub receiver

* Simplify status-handling method in stub receiver

Idiomatically, error handling and the 'uncommon' case should be nested,
whereas the happy path should remain as unnested as possible.

* Remove superfluous setting of OK response code in stub receiver

* Add CLI args for stub receiver

* Add support for different recording levels

* Add source & func fields to logger in stub receiver

* Add compile-time interface implementation checks

* Add event details method to Recorder interface

Add method to interface.
Add method to implementations.
Change write-only to read-write locks for improved concurrency (assuming frequent reads).

* Implement handler for event details in stub receiver

* Add custom JSON marshalling for event properties

Whilst we internally separate well-known fields from arbitrary ones,
the law of least surprise dictates that events are returned in the
flattened structure they were provided in.

* Return type error on failing to parse insert ID from JSON

* Remove superfluous unmarshalling case for JSON tokens

* Add test case for unmarshalling incomplete data

* Refactor well-known property keys to package constants in stub receiver

* Fix incorrect expected error in unmarshalling test case

Also reorder test cases so those expecting an error are grouped together.

* Fail unmarshalling if any required property are not present

* Add expected errors in unmarshalling test cases

* Add insert ID to Hodometer events

The insert ID is the creation timestamp of the event.
This event can then be retried, even if the timestamp of the event were to change.
This allows for retries of the HTTP request; subsequent metrics publishing will
produce a new timestamp and thus a new logical event.

* Add test for custom Event marshalling into JSON

* Add extra tests cases for event marshalling into JSON

* Use recursive discovery of test files

Previously, only tests at the top level were discovered.  This was a bug.

* Support custom metrics publishing endpoint

This allows for testing with the stub receiver.
It is not intended for users to override, as the API key is deliberately hard-coded.

* Refactor default log level to package-level constant

This aids both consistency and clarity.

* Refactor arg parsing logic into per-arg functions

The existing function was growing long enough to become difficult to read.

* Refactor arg-parsing error logging to main function

It felt weird to have the arg-handling logic accept a logger, but then return configuration
for logging.
It was not clear whether this should be allowed to override the log level for outputting errors or not.
Now, the main function is fully responsible for how it wants to configure the logger.

* Support publishing metrics to multiple endpoints

* Publish to all API endpoints concurrently

As there are retries enabled, it could take minutes for a single endpoint to be published to,
or to fail.
There is no point in holding up the other publishing calls for such cases.

* Rename Dockerfile to disambiguate from stub receiver & update Makefile

* Build only hodometer (not stub receiver) within its Dockerfile

* Set non-root user in hodometer Dockerfile

Also add build args to allow setting the user and group IDs.

* Add Dockerfile for hodometer stub receiver

* Move hodometer files to sibling dir of stub receiver

This separation allows independent testing of both submodules.

* Update hodometer package references in cmd to reflect new package path

* Move hodometer cmd package to sibling dir of receiver cmd package

* Update path for hodometer build in Make target

* Separate testing Make targets for hodometer & stub receiver

* Add Docker Make target for stub receiver

* Use regexes to avoid flakiness in checking error messages

The error message for unmarshalling is constructed using a map,
the keys of which are not guaranteed to be in any particular order,
thus any set order of missing fields in a test could be wrong for the actual result.

* Add test for receiver /track handler

* Add handling for empty request bodies in /track endpoint

This allows differentiation of no event vs. an incomplete or ill-formatted event.

* Add test cases for ill-formatted & incomplete events for receiver handler

* Use nil event when error expected in test

* Add more event-parsing test cases for missing data

* Rename test case for consistency

* Add custom Event unmarshalling method for stub receiver

It is cleaner to fail on missing fields whilst unmarshalling the payload,
rather than performing post-hoc checks.
This ensures the checks will be performed, and is within a well-known interface
for parsing/unmarshalling logic.

* Add verbose flag for testing Make targets

* Add happy-path test cases for /track handler in stub receiver

This includes both the verbose and non-verbose versions of the happy path,
as well as both the request body and query parameter approaches to passing
event data.
In addition, rename test fields for clarity and add additional checks on 'status' responses.

* Join short per-param lines in receiver test cases for concision

* Use POST HTTP method when setting request body in receiver tests

* Rename test param for semantic clarity

The field is an error message, not actually an error.

* Update package path for build/version info in Makefile
  • Loading branch information
agrski authored May 20, 2022
1 parent a6aa71c commit 337bf6a
Show file tree
Hide file tree
Showing 22 changed files with 1,365 additions and 141 deletions.
7 changes: 6 additions & 1 deletion hodometer/Dockerfile → hodometer/Dockerfile.hodometer
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ WORKDIR /build
ADD . .

RUN rm -r apis && mv apis-TEMP apis
RUN make build
RUN make build-hodometer

################################################################################

FROM golang:alpine
COPY --from=builder /build/bin/hodometer /bin/hodometer

ARG UID=1000
ARG GID=1000
USER ${UID}:${GID}

CMD ["/bin/hodometer"]
19 changes: 19 additions & 0 deletions hodometer/Dockerfile.receiver
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM golang:alpine AS builder

RUN apk add --upgrade make cmake

WORKDIR /build
COPY . .

RUN make build-receiver

################################################################################

FROM golang:alpine
COPY --from=builder /build/bin/receiver /bin/receiver

ARG UID=1000
ARG GID=1000
USER ${UID}:${GID}

CMD ["/bin/receiver"]
39 changes: 30 additions & 9 deletions hodometer/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ IMAGE_NAME ?= hodometer
IMAGE_TAG ?= latest

HODOMETER_IMG = ${DOCKER_REPO}/${IMAGE_NAME}:${IMAGE_TAG}
RECEIVER_IMG = ${DOCKER_REPO}/${IMAGE_NAME}-receiver:${IMAGE_TAG}

GO_ENV = GO111MODULE=on CGO_ENABLED=0

VERBOSE ?=

# Build information
VERSION_PACKAGE = $(shell sed -n 's/^module\s\+//p' go.mod)/pkg
VERSION_PACKAGE = $(shell sed -n 's/^module\s\+//p' go.mod)/pkg/hodometer
BUILD_VERSION = $(shell cat ./version.txt)
BUILD_TIME = $(shell date -u -Iseconds)
GIT_BRANCH = $(shell git branch --show-current)
Expand All @@ -22,8 +25,8 @@ lint:
gofmt -w pkg
golangci-lint run --fix

.PHONY: build
build: test
.PHONY: build-hodometer
build-hodometer: test-hodometer
$(GO_ENV) \
go build \
-o bin/hodometer \
Expand All @@ -34,11 +37,25 @@ build: test
-X '$(VERSION_PACKAGE).GitCommit=$(GIT_COMMIT)' \
-X '$(VERSION_PACKAGE).ReleaseType=$(RELEASE_TYPE)' \
" \
./cmd
./cmd/hodometer/

.PHONY: build-receiver
build-receiver: test-receiver
$(GO_ENV) go build -o bin/receiver ./cmd/receiver

.PHONY: build
build: build-hodometer build-receiver

.PHONY: test-hodometer
test-hodometer:
$(GO_ENV) go test $(VERBOSE) ./cmd/hodometer/... ./pkg/hodometer/...

.PHONY: test-receiver
test-receiver:
$(GO_ENV) go test $(VERBOSE) ./cmd/receiver/... ./pkg/receiver/...

.PHONY: test
test:
$(GO_ENV) go test ./cmd ./pkg
test: test-hodometer test-receiver

.PHONY: clean
clean:
Expand All @@ -49,6 +66,10 @@ copy-apis:
rm -rf apis-TEMP
cp -r ../apis/mlops/scheduler apis-TEMP

.PHONY: build-docker
build-docker: copy-apis
docker build -t ${HODOMETER_IMG} -f Dockerfile .
.PHONY: build-hodometer-docker
build-hodometer-docker: copy-apis
docker build -t ${HODOMETER_IMG} -f Dockerfile.hodometer .

.PHONY: build-receiver-docker
build-receiver-docker:
docker build -t ${RECEIVER_IMG} -f Dockerfile.receiver .
107 changes: 0 additions & 107 deletions hodometer/cmd/args.go

This file was deleted.

Loading

0 comments on commit 337bf6a

Please sign in to comment.