├── .github ├── FUNDING.yml └── workflows │ ├── ci.yaml │ ├── release-docker.yaml │ └── release.yaml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── bootstrap.go ├── bootstrap_fs.go ├── bootstrap_git.go └── cmd.go ├── go.mod ├── go.sum ├── main.go ├── pkg ├── nomad │ └── nomad.go ├── reconcile │ └── reconcile.go └── repository │ └── repository.go ├── scripts └── start-nomad.sh └── test └── data └── jobs ├── hello_world.nomad └── traefik.nomad /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jonasvinther # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ 'main' ] 6 | pull_request: 7 | branches: [ '**' ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 1.x 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: ^1.17 20 | 21 | - name: Check out code into the Go module directory 22 | uses: actions/checkout@v2 23 | 24 | - name: Get dependencies 25 | run: | 26 | go get -v -t -d ./... 27 | if [ -f Gopkg.toml ]; then 28 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 29 | dep ensure 30 | fi 31 | 32 | - name: Build 33 | run: go build -o nomoperator -v . 34 | 35 | - name: Test 36 | run: go test -v ./... 37 | -------------------------------------------------------------------------------- /.github/workflows/release-docker.yaml: -------------------------------------------------------------------------------- 1 | name: Release Docker 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build_release_image: 14 | name: Build Docker image 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v2 19 | 20 | - name: Get the tag name 21 | run: echo "TAG=${GITHUB_REF##*/v}" >> $GITHUB_ENV 22 | 23 | - name: Login to the Container registry 24 | uses: docker/login-action@v1 25 | with: 26 | registry: ${{ env.REGISTRY }} 27 | username: ${{ github.repository_owner }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Build and push Docker image 31 | id: docker_build 32 | uses: docker/build-push-action@v2 33 | with: 34 | context: . 35 | push: true 36 | tags: | 37 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest 38 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release Go 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.*.* 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: ^1.17 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | nomoperator 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - 3 | # Custom environment variables to be set during the builds. 4 | # Default is empty. 5 | env: 6 | - CGO_ENABLED=0 7 | 8 | # Binary name. 9 | # Can be a path (e.g. `bin/app`) to wrap the binary in a directory. 10 | # Default is the name of the project directory. 11 | binary: nomoperator 12 | 13 | # GOOS list to build for. 14 | # For more info refer to: https://golang.org/doc/install/source#environment 15 | # Defaults are darwin and linux. 16 | goos: 17 | - darwin 18 | - linux 19 | - windows -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | RUN apk update && apk add --no-cache git 4 | 5 | ENV GO111MODULE=on 6 | 7 | WORKDIR /app 8 | 9 | ADD . . 10 | 11 | RUN go mod download 12 | RUN go get -d -v 13 | RUN CGO_ENABLED=0 GOOS=linux go build -o /go/bin/nomoporator 14 | 15 | RUN adduser -S scratchuser 16 | RUN chown scratchuser /go/bin/nomoporator 17 | 18 | FROM scratch 19 | COPY --from=builder /go/bin/nomoporator /nomoporator 20 | COPY --from=builder /etc/passwd /etc/passwd 21 | USER scratchuser 22 | ENTRYPOINT ["/nomoporator"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jonas Vinther 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test build compile start-nomad 2 | 3 | test: 4 | go test -v ./... 5 | 6 | build: 7 | go build -o nomoperator 8 | 9 | compile: 10 | echo "Compiling for every OS and Platform" 11 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/nomoperator-linux-amd64 main.go 12 | CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o bin/nomoperator-linux-arm main.go 13 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/nomoperator-linux-arm64 main.go 14 | CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -o bin/nomoperator-freebsd-386 main.go 15 | CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o bin/nomoperator.exe main.go 16 | 17 | start-nomad: 18 | ./scripts/start-nomad.sh 19 | 20 | all: test build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nomoporator 2 | Nomoporator is a GitOps operator for Hashicorp Nomad. 3 | 4 | ## How to use 5 | 6 | ### Setting up Nomoporator 7 | 8 | #### Environment variables 9 | It's also possible configure Nomoporator via environment variables by setting them like this: 10 | ``` 11 | NOMAD_ADDR - Required to overide the default of http://127.0.0.1:4646. 12 | NOMAD_TOKEN - Required with ACLs enabled. 13 | NOMAD_CACERT - Required with TLS enabled. 14 | NOMAD_CLIENT_CERT - Required with TLS enabled. 15 | NOMAD_CLIENT_KEY - Required with TLS enabled. 16 | ``` 17 | 18 | ### Bootstrapping using a local path 19 | 20 | > Get help with `./nomoporator bootstrap fs -h` 21 | 22 | ``` 23 | Bootstrap Nomad using a local path 24 | 25 | Usage: 26 | nomoperator bootstrap fs [path] [flags] 27 | 28 | Flags: 29 | --base-dir string Path to the base directory (default "./") 30 | --delete Enable delete missing jobs 31 | -h, --help help for fs 32 | --path string glob pattern relative to the base-dir (default "**/*.nomad") 33 | --var-path string var glob pattern relative to the base-dir (default "**/*.vars.yml") 34 | --watch Enable watch mode 35 | 36 | Global Flags: 37 | -a, --address string Address of the Nomad server 38 | ``` 39 | 40 | Use it like this: 41 | ``` 42 | ./nomoperator bootstrap fs --base-dir /path/to/base/dir --path jobs/*.nomad 43 | ``` 44 | 45 | ### Bootstrapping using a git repository 46 | 47 | > Get help with `./nomoporator bootstrap git -h` 48 | 49 | ``` 50 | Bootstrap Nomad using a git repository 51 | 52 | Usage: 53 | nomoperator bootstrap git [git repo] [flags] 54 | 55 | Flags: 56 | --branch string git branch (default "main") 57 | --delete Enable delete missing jobs (default true) 58 | -h, --help help for git 59 | --password string SSH private key password 60 | --path string glob pattern relative to the repository root (default "**/*.nomad") 61 | --ssh-insecure-ignore-host-key Ignore insecure SSH host key 62 | --ssh-key string SSH private key 63 | --url string git repository URL 64 | --username string SSH username (default "git") 65 | --var-path string var glob pattern relative to the repository root (default "**/*.vars.yml") 66 | --watch Enable watch mode (default true) 67 | 68 | Global Flags: 69 | -a, --address string Address of the Nomad server 70 | ``` 71 | 72 | Use it like this: 73 | ``` 74 | ./nomoperator bootstrap git --url https://github.com/jonasvinther/nomad-state.git --path jobs/*.nomad --branch main 75 | ``` 76 | 77 | ## Run as Nomad job 78 | ```yaml 79 | job "nomoperator" { 80 | datacenters = ["dc1"] 81 | group "nomoperator" { 82 | count = 1 83 | task "nomoperator" { 84 | driver = "exec" 85 | config { 86 | command = "nomoperator" 87 | args = ["bootstrap", "git", "--url", "https://github.com/jonasvinther/nomad-state.git", "--branch", "main", "--path", "jobs/*.nomad"] 88 | } 89 | artifact { 90 | source = "https://github.com/jonasvinther/nomad-gitops-operator/releases/download/v0.0.2/nomad-gitops-operator_0.0.2_linux_amd64.tar.gz" 91 | destination = "local" 92 | mode = "any" 93 | } 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | ## SSH 100 | 101 | You can use SSH keys to connect to a private git repository. 102 | 103 | * Generate a public and private key 104 | 105 | ```bash 106 | ssh-keygen -t ed25519 -C "nomoperator" -f "nomoperatordeploykey" -N "" 107 | ``` 108 | 109 | If you would like to set password remove `-N ""` and enter the password. Make sure to set `--username sshusername ` and `--pasword sshpassword` when running nomoperator. 110 | 111 | * Configure the server git repository with public key 112 | 113 | * Generate `known_hosts` for the git server in `/path_to/known_hosts` which is accessible via nomoperator. 114 | 115 | ```bash 116 | ssh-keyscan -t ed25519 github.com 117 | ``` 118 | 119 | If your git server uses non started port use the `-p` flag. 120 | 121 | ```bash 122 | ssh-keyscan -t ed25519 -p 2222 mygitserver.com 123 | ``` 124 | 125 | If you would like to avoid using hosts files you can set `--ssh-insecure-ignore-host-key=true`. This is highly discouraged due to security risks. 126 | 127 | * Run as nomad job 128 | 129 | ```yaml 130 | job "nomoperator" { 131 | datacenters = ["dc1"] 132 | group "nomoperator" { 133 | count = 1 134 | task "nomoperator" { 135 | driver = "exec" 136 | env { 137 | SSH_KNOWN_HOSTS = "/path_to/known_hosts" 138 | SSH_KEY = <