diff --git a/Dockerfile b/Dockerfile index 26810d73e94a109e6a72d79dc1062029c6677619..f2d4c9a0fce4b3d3d321b6e943ef8916e4727f4a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,11 @@ -FROM golang:latest AS builder +FROM golang:latest RUN mkdir /app COPY go.mod /app/ WORKDIR /app RUN go mod download COPY . /app -RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o rss_sync . -WORKDIR /app +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o rss_sync . + +COPY wait-for-it.sh /app/ +RUN chmod +x /app/wait-for-it.sh CMD ["/app/rss_sync"] \ No newline at end of file diff --git a/README.md b/README.md index cfa4fe3805ed66ceb77384161b98f9861118515a..f3e55b25fbabb9d9f525368f2a6c882333b57684 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ our attention (Security Releases, Product Updates etc) ## Avoiding Duplication We try to be as clever as is reasonably possible in terms of not duplicating RSS feed items into Gitlab. -A SQLite DB is used to store the GUID/FeedID combination which is checked when assessing articles for synchronisation. +A Redis DB is used to store the GUID/FeedID combination which is checked when assessing articles for synchronisation. In addition we also add the RSS feed's item GUID at the bottom of the issue description. Before synchronising an RSS item we run an issue search in the associated project, if we dont find the GUID in any issue we assume its not already been created. -This helps to guard against scenarios where you lose the SQLite DB and dont want RSS items reduplicating into Gitlab. +This helps to guard against scenarios where you lose the Redis DB and dont want RSS items reduplicating into Gitlab. If found in Gitlab it is marked as synchronised in the local database as well as printing an link to the existing issue(s) to stdout. ## Limiting what is initially synced. @@ -60,16 +60,20 @@ A Docker image is made available on [DockerHub](https://hub.docker.com/r/adamhf/ * GITLAB_API_TOKEN - Gitlab personal access token that will be used to create Issues NOTE: You must have access to create issues in the projects you specify in the config file. * CONFIG_DIR - The directory the application should look for config.yaml in. -* DATA_DIR - The directory the application should look for (or create) the state.db in. - -### Volume mounts -Make sure the location of your DATA_DIR environment variable is set to a persistant volume / mount as the database -that is contained within it stores the state of which RSS items have already been synced. +* REDIS_URL - The URL of the Redis host e.g. `redis:6379` +* REDIS_PASSWORD - Password for Redis, if an empty password is required set to `REDIS_PASSWORD=` ### Run it + +#### Via Docker ```bash -docker run -e GITLAB_API_TOKEN=<INSERT_TOKEN> -e DATA_DIR=/data -e CONFIG_DIR=/app -v <PATH_TO_DATA_DIR>:/data -v <PATH_TO_CONFIG_DIR>/config adamhf/rss-sync:latest +docker run -e GITLAB_API_TOKEN=<INSERT_TOKEN> -e DATA_DIR=/data -e CONFIG_DIR=/app -v REDIS_URL=<REDIS_URL> -v REDIS_PASSWORD=<REDIS_PASSWORD> -v <PATH_TO_CONFIG_DIR>/config adamhf/rss-sync:latest ``` +####Via docker-compose +```bash +docker-compose up``` + + ## Prometheus Metrics Two metrics (above and beyond what are exposed by the Go Prometheus library) are exposed on :8080/metrics diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000000000000000000000000000000000000..777691cc5f6ab2eef6b76824aa3475ecbfb74593 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,21 @@ +version: "3" +services: + app: + build: . + env_file: + - .env + depends_on: + - db + volumes: + - ./config.yaml:/config/config.yaml + command: + - /app/wait-for-it.sh + - ${REDIS_URL} + - --timeout=60 + - --strict + - -- + - /app/rss_sync + db: + image: 'bitnami/redis:latest' + environment: + - ALLOW_EMPTY_PASSWORD=yes diff --git a/wait-for-it.sh b/wait-for-it.sh new file mode 100644 index 0000000000000000000000000000000000000000..607a7d67695e5d2e0d70aa0537ad398d7415542e --- /dev/null +++ b/wait-for-it.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + WAITFORIT_BUSYTIMEFLAG="-t" + +else + WAITFORIT_ISBUSY=0 + WAITFORIT_BUSYTIMEFLAG="" +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi \ No newline at end of file