Skip to content

Commit

Permalink
/repos
Browse files Browse the repository at this point in the history
  • Loading branch information
tyng94 committed Mar 23, 2024
1 parent 8672308 commit 8efb31d
Show file tree
Hide file tree
Showing 50 changed files with 763 additions and 3,628 deletions.
28 changes: 19 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
docker-registry-watcher
registrywatcher
*.toml
!test.toml
!sample.toml
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# dependencies
node_modules
# Test binary, built with `go test -c`
*.test

# logs
npm-debug.log
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
29 changes: 0 additions & 29 deletions Dockerfile

This file was deleted.

4 changes: 0 additions & 4 deletions Dockerfile-golang

This file was deleted.

44 changes: 37 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
all: fmt test
all: fmt lint test

test:
@echo "-- running tests --"
go test ./...
@echo "-- tests done --\n\n"
@echo "===================================="
@echo "########## Running tests ##########"
@echo "===================================="
@ENV=test go test ./...
@echo "================= OK ================="
@echo ""

fmt:
@echo "-- running formatter --"
go fmt ./...
@echo "-- formatter done --\n\n"
@echo "===================================="
@echo "######### Formatting code ##########"
@echo "===================================="
@goimports-reviser -rm-unused -imports-order "std,company,project,general" ./...
@gofmt -l -w .
@echo "================= OK ================="
@echo ""

fmt-check:
@echo "===================================="
@echo "####### Checking Formatting ########"
@echo "===================================="
@goimports-reviser -list-diff -imports-order "std,company,project,general" ./...
@files=$(gofmt -l .) && [ -z "$files" ]
@echo "================= OK ================="
@echo ""

lint:
@echo "===================================="
@echo "########## Linting code ###########"
@echo "===================================="
@golangci-lint run
@echo "================= OK ================="
@echo ""

test-env:
./env/test/script.sh

dev:
docker compose -f env/dev/docker-compose.yml up --build
153 changes: 9 additions & 144 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,148 +1,13 @@
# Registrywatcher
# registrywatcher

![Frontend](public/gifs/search.gif)
This is my idealised template for a golang service.

Watches docker registries for new tags on selected repositories.
## Features

The service maintains an internal state stored in Postgres for each watched repository for `pinned_tag` and `auto_deploy`
- [x] Developer Tooling
- [x] Makefile
- [x] Docker-compose
- [x] Linting
- [x] Testing

`pinned_tag"` is set to an empty string by default. `pinned_tag=""` has a special meaning within this service. It enables the auto-deployment feature for versioned tags, (i.e. a new tag `v1.0.0` will be autodeployed if the current deployed tag is `v0.9.0`)

`pinned_tag` can also be set to a custom tag through the `/tags/$REPO_NAME` endpoint. Auto deployment for custom tags happen if the docker content digest of of the tag changes (i.e. the tagged docker image was overwritten).

`auto_deploy` determines whether auto deployment is enabled for both custom tags and versiomed tags.

Currently only Nomad is supported as the deployment client.

## Configuration

Before running the service locally or in production, the config file `config/staging.toml` must be present. A template is provided in config/sample.toml with sensible defaults. Most should be left alone unless you're developing `registrywatcher` itself. However, there are a few you may want to change in a production environment.
A sample config file template has been provided in `config/sample.toml`

## Endpoints

```yml
- url: /ping/
method: GET

Response:
- message: string

description: Returns "pong", for health check.
```
```yml
- url: /tags/$REPO_NAME/reset
method: POST

JSON Body Request:
- N/A

Response:
- message: string

description: To reset the pinned_tag to latest, which is the default value. See top of the README for more info.
```
```yml
- url: /tags/$REPO_NAME
method: POST

JSON Body Request: (at least 1 argument provided)
- pinned_tag: string (no default)
- auto_deploy: bool (no default)

Response:
- message: string

description: To update the pinned_tag for the given repo_name and deploy it regardless of current deployed tag.
```
```yml
- url: /tags/$REPO_NAME
method: GET

200 Response:
- repo_tag: string

400 Response:
- message: string

description: To get the pinned_tag value for the given $REPO_NAME.
```
```yml
- url: /repos
method: GET

200 Response:
- $REPO1_NAME(string):
- "auto_deploy": bool
- "pinned_tag": string
- "pinned_tag_value": string
- "tags": [string, ...]
...

description: To get the pinned_tag value for all watched repositories.
```
## Local development
`docker-compose up -d`

## Deployment

Config files are located in `$PROJ_ROOT/config`. It's best for the config file to be interpolated with the necessary values during deployment, i.e. repositories to be watched, nomad token. A sample config file is provided anyway for running locally (with sensitive information left to be filled in by the user)

The following environment variables must be present for deployment:
- `DATABASE_URL` to connect to the postgres db (if not present `database_url` in config file will be used)
- `NOMAD_TOKEN` to access the nomad API

The following environment variables are optional:
- `VAULT_TOKEN` to run the nomad job associated with a watched repo, if the nomad job has Vault secrets.

The configuration file must be interpolated by Nomad to fill the following information:
- `registry_auth ` for each of the supported registries
- `watched_repositories` lists all the repositories to watch for
- key value pairs in `repo_map`, which maps the docker registry and nomad job name of each watched repositories

## Test setup

Ports 5000 and 5432 need to be free as they are currently hardcoded for the registry and postgres container respectively.
https://stackoverflow.com/questions/48593016/postgresql-docker-role-does-not-exist

You need to make one modification to your docker daemon config (usually at ~/.docker/daemon.json)

Add the flag `--insecure-registry localhost:5000` to your docker daemon, documented [here](https://docs.docker.com/registry/insecure/) for testing against an insecure registry.

You may need to update the certificates from time to time. To do so, run

```bash
make snakeoil
```

## Tests

Run locally:

```bash
// run both integration and unit tests
gotestsum -- -tags="integration unit" ./...
// clean testcache
go clean -testcache
```

The tests don't test the nomad deployment capability of the service.

## TODO

An interface to swap in the deployment agent (only Nomad supported for now).

Tests take too long to run, this is mainly due each integration test case spinning up and down its own docker containers.

Nomad mock server cannot run jobs, which blocks writing of integration tests involving Nomad API calls

Buttons should implement debouncing

Bug where only new tags/SHA changes AFTER you toggle back auto deployment will trigger an update, meaning any updates during the window where auto deployment was turned off are moot.
## Usage
13 changes: 13 additions & 0 deletions app/health/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package health

import (
"net/http"

"github.com/gin-gonic/gin"
)

func PingHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
19 changes: 19 additions & 0 deletions app/models/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package models

type Repository struct {
RepositoryName string `gorm:"not null;unique;primaryKey" binding:"required"`
PinnedTag string `gorm:"not null;unique" binding:"required,email"`
AutoDeploy bool `gorm:"not null" binding:"required"`
Tags []Tag `gorm:"foreignKey:RepositoryID"`
}

func (r Repository) TableName() string {
return "deployed_repository_version"
}

type Tag struct {
ID uint `gorm:"primaryKey"`
TagName string `gorm:"not null" binding:"required"`
Digest string `gorm:"not null" binding:"required"`
RepositoryID string
}
16 changes: 16 additions & 0 deletions app/repository/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package repository

type GetReposiroriesRequest struct {
RecipientID uint `form:"recipient_id" binding:"required"`
}

type GetRepositoriesResponse struct {
RepositoryMap map[string]RepositoryData
}

type RepositoryData struct {
AutoDeploy bool `json:"auto_deploy"`
PinnedTag string `json:"pinned_tag"`
PinnedTagValue string `json:"pinned_tag_value"`
Tags []string `json:"tags"`
}
43 changes: 43 additions & 0 deletions app/repository/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package repository

import (
"net/http"
"registrywatcher/app/models"
"registrywatcher/utilities/config"

"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

type RepositoryService struct {
DB *gorm.DB
Config *config.Config
// Sender external.Sender
}

// GetRepositories is a handler that returns a list of repositories
func (m RepositoryService) GetRepositories(c *gin.Context) {

var repositories []models.Repository
if err := m.DB.Preload("Tags").Find(&repositories).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

response := map[string]RepositoryData{}

for _, repo := range repositories {
tags := []string{}
for _, tag := range repo.Tags {
tags = append(tags, tag.TagName)
}

response[repo.RepositoryName] = RepositoryData{
AutoDeploy: repo.AutoDeploy,
PinnedTag: repo.PinnedTag,
PinnedTagValue: repo.PinnedTag,
Tags: tags,
}
}
c.JSON(http.StatusOK, response)
}
Loading

0 comments on commit 8efb31d

Please sign in to comment.