diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c4cf7c..f044a5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,3 +22,4 @@ jobs: uses: actions/checkout@v2 - name: CI Tasks run: make ci + diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..59340cd --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,49 @@ +name: Create and publish the Docker image + +on: + push: + tags: ['v*'] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the Container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} + + - name: Build and push Docker image + uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + with: + context: . + platforms: linux/amd64,linux/arm/v7 + file: ./build/package/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/Makefile b/Makefile index 59b827f..4daaa6d 100644 --- a/Makefile +++ b/Makefile @@ -18,3 +18,7 @@ lint: @docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.43.0 golangci-lint run -v .PHONY: lint +docker-build: + @echo "Building docker image" + @docker build -t dyndns-netcup-go -f ./build/package/Dockerfile . +.PHONY: docker-build diff --git a/README.md b/README.md index fa2e951..65ffd15 100644 --- a/README.md +++ b/README.md @@ -4,50 +4,52 @@ [![Release](https://img.shields.io/github/release/Hentra/dyndns-netcup-go?include_prereleases)](https://github.com/Hentra/dyndns-netcup-go/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/Hentra/dyndns-netcup-go)](https://goreportcard.com/report/github.com/Hentra/dyndns-netcup-go) -Dyndns client for the netcup dns API written in go. Not +Dyndns client for the netcup DNS API written in go. Not related to netcup GmbH. It is **heavily** inspired by [this](https://github.com/stecklars/dynamic-dns-netcup-api) project which might be also a good solution for your -dynamic dns needs. +dynamic DNS needs. ## Table of Contents * [Features](#features) - * [Implemented](#implemented) - * [Missing](#missing) * [Installation](#installation) - * [Manual](#manual) - * [From source](#from-source) + * [Docker](#docker) + * [Manual](#manual) + * [From source](#from-source) * [Usage](#usage) - * [Prequisites](#prequisites) - * [Run dyndns-netcup-go](#run-dyndns-netcup-go) - * [Commandline flags](#commandline-flags) - * [Cache](#cache) + * [Prequisites](#prequisites) + * [Run dyndns-netcup-go](#run-dyndns-netcup-go) + * [Commandline flags](#commandline-flags) + * [Cache](#cache) * [Contributing](#contributing) ## Features -### Implemented * Multi domain support * Subdomain support * TTL update support -* Creation of a DNS record if it doesn't already exist. +* Creation of a DNS record if it doesn't already exist * Multi host support (nice when you need to update both `@` and `*`) * IPv6 support -* Verbose option (when you specify `-v` you get plenty information) -* Cache IP of the executing machine and only update when it changes -### Missing +If you need additional features please open up an +[Issue](https://github.com/Hentra/dyndns-netcup-go/issues). -* MX entry support +## Installation -There are currently no plans to implement this features. If you need those (or additional features) please -open up an [Issue](https://github.com/Hentra/dyndns-netcup-go/issues). +### Docker -## Installation + docker run -d \ + -v $(pwd)/config.yml:/config.yml \ + -e INTERVAL=300 \ + ghcr.io/hentra/dyndns-netcup-go + +The environment variable `INTERVAL` defines the interval of DNS updates in +seconds. ### Manual 1. Download the lastest [binary](https://github.com/Hentra/dyndns-netcup-go/releases) for your OS diff --git a/build/package/Dockerfile b/build/package/Dockerfile new file mode 100644 index 0000000..49fc53b --- /dev/null +++ b/build/package/Dockerfile @@ -0,0 +1,14 @@ +# build stage +FROM golang:1.18.1-alpine3.15 as build-stage +WORKDIR /app +COPY . . +RUN CGO_ENABLED=0 go build -o /dyndns-docker ./cmd/dyndns-netcup-docker/main.go + +# production stage +LABEL org.opencontainers.image.source https://github.com/Hentra/dyndns-netcup-go +FROM alpine:latest +RUN mkdir /cache/ +COPY --from=build-stage /dyndns-docker /dyndns-docker + +ENTRYPOINT ["/dyndns-docker"] + diff --git a/cmd/dyndns-netcup-docker/main.go b/cmd/dyndns-netcup-docker/main.go new file mode 100644 index 0000000..c78c890 --- /dev/null +++ b/cmd/dyndns-netcup-docker/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "log" + "os" + "strconv" + "time" + + "github.com/Hentra/dyndns-netcup-go/internal" +) + +const ( + configFileLocation = "/config.yml" + ipCacheLocation = "/ipcache" + defaultInterval = time.Minute + intervalEnv = "INTERVAL" +) + +func main() { + interval, err := parseEnv() + if err != nil { + log.Fatal("Could not parse interval: ", err) + } + + logger := internal.NewLogger(true) + + config, err := internal.LoadConfig(configFileLocation) + if err != nil { + logger.Error("Error loading config file. Make sure a config file is mounted to ", configFileLocation, ":", err) + } + + config.IPCache = ipCacheLocation + + cache, err := internal.NewCache(config.IPCache, time.Second*time.Duration(config.IPCacheTimeout)) + if err != nil { + logger.Error(err) + } + + configurator := internal.NewDNSConfigurator(config, cache, logger) + for { + logger.Info("configure DNS records") + configurator.Configure() + time.Sleep(interval) + } +} + +func parseEnv() (time.Duration, error) { + intervalTime := defaultInterval + if interval, exists := os.LookupEnv(intervalEnv); exists { + intervalSeconds, err := strconv.Atoi(interval) + if err != nil { + return 0, err + } + intervalTime = time.Duration(intervalSeconds) * time.Second + } + + return intervalTime, nil +}