Add Nomad provider

I modeled it off the Kubernetes provider a bit. It supports setting task
config at group and task levels using services and meta attributes.
This commit is contained in:
Ian Fijolek
2022-12-25 22:29:08 -08:00
committed by CrazyMax
parent a7e8a9cd81
commit 6318e4f069
10 changed files with 505 additions and 7 deletions

View File

@@ -105,6 +105,8 @@ You can override this using the [`--config` flag or `CONFIG` env var with `serve
- production
file:
directory: ./imagesdir
nomad:
watchByDefault: true
```
## Environment variables

211
docs/providers/nomad.md Normal file
View File

@@ -0,0 +1,211 @@
# Nomad provider
## About
The Nomad provider allows you watch tasks running using the Docker provider for registry updates.
## Quick start
Here we'll go over basic deployment using a local Nomad cluster.
First, we'll deploy a Diun job:
```hcl
job "diun" {
type = "service"
group "diun" {
task "diun" {
driver = "docker"
config {
image = "crazymax/diun:latest"
args = ["serve"]
}
env = {
"NOMAD_ADDR" = "http://${attr.unique.network.ip-address}:4646/",
"DIUN_PROVIDERS_NOMAD" = true,
}
}
}
}
```
Task based configuration can be passed through Service tags or meta attributes. These can be defined at the task or even the group level where it will apply to all tasks within the group.
The example below will show all methods, but you only need to use one.
```hcl
job "whoami" {
type = "service"
group "whoami" {
network {
mode = "bridge"
port "web" {
to = 80
}
}
// This
meta {
diun.enable = true
}
// Or this
service {
tags = [
"diun.enable=true"
]
}
task "diun" {
driver = "docker"
config {
image = "containous/whoami:latest"
}
// Or this
meta {
diun.enable = true
}
// Or this
service {
tags = [
"diun.enable=true"
]
}
}
}
}
```
## Configuration
!!! hint
Environment variable `DIUN_PROVIDERS_NOMAD=true` can be used to enable this provider with default values.
Default values are assigned by the Nomad client. If not provided in your Diun configuration, the client will default to using the same config values as the `nomad` cli client.
!!! abstract "Environment variables"
* `NOMAD_ADDR`
* `NOMAD_REGION`
* `NOMAD_NAMESPACE`
* `NOMAD_HTTP_AUTH`
* `NOMAD_CACERT`
* `NOMAD_CAPATH`
* `NOMAD_CLIENT_CERT`
* `NOMAD_CLIENT_KEY`
* `NOMAD_TLS_SERVER_NAME`
* `NOMAD_SKIP_VERIFY`
* `NOMAD_TOKEN`
### `address`
The Nomad server address as URL.
!!! example "File"
```yaml
providers:
nomad:
address: "http://localhost:4646"
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_ENDPOINT`
Nomad server endpoint as URL, which is only used when the behavior based on environment variables described below
does not apply.
### `region`
Nomad region to query from
!!! example "File"
```yaml
providers:
nomad:
region: "region1"
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_REGION`
### `namespace`
Nomad namespace to query from
!!! example "File"
```yaml
providers:
nomad:
namespace: "namespace1"
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_NAMESPACE`
### `secretID`
SecretID to connect to Nomad API. This token must have permission to query and view Nomad jobs.
!!! example "File"
```yaml
providers:
nomad:
secretID: "secret"
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_SECRETID`
### `tlsInsecure`
Controls whether client does not verify the server's certificate chain and hostname (default `false`).
!!! example "File"
```yaml
providers:
nomad:
tlsInsecure: false
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_TLSINSECURE`
### `watchByDefault`
Enable watch by default. If false, tasks that don't have `diun.enable = true` in their meta or service tags will be ignored
(default `false`).
!!! example "File"
```yaml
providers:
nomad:
watchByDefault: false
```
!!! abstract "Environment variables"
* `DIUN_PROVIDERS_NOMAD_WATCHBYDEFAULT`
## Nomad annotations
You can configure more finely the way to analyze the image of your tasks through Nomad meta attributes or service tags:
| Name | Default | Description |
|---------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `diun.enable` | | Set to true to enable image analysis of this task |
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
| `diun.watch_repo` | `false` | Watch all tags of this task image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
| `diun.sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `semver`, `lexicographical` |
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
| `diun.hub_link` | _automatic_ | Set registry hub link for this image |
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |

10
go.mod
View File

@@ -19,6 +19,7 @@ require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/gregdel/pushover v1.1.0
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
github.com/hashicorp/nomad/api v0.0.0-20221121184155-2372c6d20c54
github.com/imdario/mergo v0.3.13
github.com/jedib0t/go-pretty/v6 v6.4.2
github.com/matcornic/hermes/v2 v2.1.0
@@ -36,7 +37,7 @@ require (
github.com/russross/blackfriday/v2 v2.1.0
github.com/sirupsen/logrus v1.9.0
github.com/streadway/amqp v1.0.0
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
github.com/tidwall/pretty v1.2.1
go.etcd.io/bbolt v1.3.6
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
@@ -86,9 +87,12 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/cronexpr v1.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/huandu/xstrings v1.2.0 // indirect
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -102,6 +106,8 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect

26
go.sum
View File

@@ -443,7 +443,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -482,8 +482,9 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregdel/pushover v1.1.0 h1:dwHyvrcpZCOS9V1fAnKPaGRRI5OC55cVaKhMybqNsKQ=
github.com/gregdel/pushover v1.1.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -495,17 +496,25 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c=
github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/nomad/api v0.0.0-20221121184155-2372c6d20c54 h1:eyCj5YuMoOAcHOP2i+NXylxvjQkzSHd5diuQqVT0vMw=
github.com/hashicorp/nomad/api v0.0.0-20221121184155-2372c6d20c54/go.mod h1:CkWF80BBiX2ha+nczdcXGjt6LeYgUVvaNEwRg2YVgE0=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
@@ -564,8 +573,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -612,8 +621,12 @@ github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mistifyio/go-zfs/v3 v3.0.0/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/buildkit v0.10.1-0.20220712094726-874eef9b70db h1:q7oAjNPIsQWTaT1GVvzAWeRJ70qPJHNtmQS046Thd3U=
github.com/moby/buildkit v0.10.1-0.20220712094726-874eef9b70db/go.mod h1:yle9eiU1fiJ/WhC4VTLOaQ6rxFou1mc4AhwScHwysi0=
@@ -755,8 +768,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
@@ -768,6 +781,7 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/shoenig/test v0.4.4 h1:juucmjQTPYUfO2+rID1wfQqsreSlk+2I8K/bQFsUZ9c=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
@@ -808,6 +822,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -817,8 +832,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=

View File

@@ -16,6 +16,7 @@ import (
dockerfilePrd "github.com/crazy-max/diun/v4/internal/provider/dockerfile"
filePrd "github.com/crazy-max/diun/v4/internal/provider/file"
kubernetesPrd "github.com/crazy-max/diun/v4/internal/provider/kubernetes"
nomadPrd "github.com/crazy-max/diun/v4/internal/provider/nomad"
swarmPrd "github.com/crazy-max/diun/v4/internal/provider/swarm"
"github.com/crazy-max/gohealthchecks"
"github.com/hako/durafmt"
@@ -175,6 +176,11 @@ func (di *Diun) Run() {
di.createJob(job)
}
// Nomad provider
for _, job := range nomadPrd.New(di.cfg.Providers.Nomad).ListJob() {
di.createJob(job)
}
di.wg.Wait()
log.Info().
Int("added", entries.CountNew).

View File

@@ -0,0 +1,28 @@
package model
import (
"github.com/crazy-max/diun/v4/pkg/utl"
)
// PrdNomad holds nomad provider configuration
type PrdNomad struct {
Address string `yaml:"address" json:"address,omitempty" validate:"omitempty"`
Region string `yaml:"region,omitempty" json:"region,omitempty" validate:"omitempty"`
SecretID string `yaml:"secretID,omitempty" json:"secretID,omitempty" validate:"omitempty"`
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty" validate:"omitempty"`
TLSInsecure *bool `yaml:"tlsInsecure" json:"tlsInsecure,omitempty" validate:"required"`
WatchByDefault *bool `yaml:"watchByDefault" json:"watchByDefault,omitempty" validate:"required"`
}
// GetDefaults gets the default values
func (s *PrdNomad) GetDefaults() *PrdNomad {
n := &PrdNomad{}
n.SetDefaults()
return n
}
// SetDefaults sets the default values
func (s *PrdNomad) SetDefaults() {
s.TLSInsecure = utl.NewFalse()
s.WatchByDefault = utl.NewFalse()
}

View File

@@ -7,6 +7,7 @@ type Providers struct {
Kubernetes *PrdKubernetes `yaml:"kubernetes,omitempty" json:"kubernetes,omitempty" label:"allowEmpty" file:"allowEmpty"`
File *PrdFile `yaml:"file,omitempty" json:"file,omitempty"`
Dockerfile *PrdDockerfile `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"`
Nomad *PrdNomad `yaml:"nomad,omitempty" json:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// GetDefaults gets the default values

View File

@@ -0,0 +1,49 @@
package nomad
import (
"github.com/crazy-max/diun/v4/internal/model"
"github.com/crazy-max/diun/v4/internal/provider"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
// Client represents an active nomad provider object
type Client struct {
*provider.Client
config *model.PrdNomad
logger zerolog.Logger
}
// New creates new kubernetes provider instance
func New(config *model.PrdNomad) *provider.Client {
return &provider.Client{
Handler: &Client{
config: config,
logger: log.With().Str("provider", "nomad").Logger(),
},
}
}
// ListJob returns job list to process
func (c *Client) ListJob() []model.Job {
if c.config == nil {
return []model.Job{}
}
images := c.listTaskImages()
if len(images) == 0 {
log.Warn().Msg("No image found")
return []model.Job{}
}
c.logger.Info().Msgf("Found %d image(s) to analyze", len(images))
var list []model.Job
for _, image := range images {
list = append(list, model.Job{
Provider: "nomad",
Image: image,
})
}
return list
}

View File

@@ -0,0 +1,126 @@
package nomad
import (
"reflect"
"strings"
"github.com/crazy-max/diun/v4/internal/model"
"github.com/crazy-max/diun/v4/internal/provider"
nomad "github.com/hashicorp/nomad/api"
)
func parseServiceTags(tags []string) map[string]string {
labels := map[string]string{}
for _, tag := range tags {
tagParts := strings.SplitN(tag, "=", 2)
if len(tagParts) < 2 {
continue
}
labels[tagParts[0]] = tagParts[1]
}
return labels
}
func updateMap(m1, m2 map[string]string) map[string]string {
for key, value := range m2 {
m1[key] = value
}
return m1
}
func (c *Client) listTaskImages() []model.Image {
config := &nomad.Config{
Address: c.config.Address,
Region: c.config.Region,
SecretID: c.config.SecretID,
Namespace: c.config.Namespace,
}
if *c.config.TLSInsecure {
config.TLSConfig.Insecure = true
}
client, err := nomad.NewClient(config)
if err != nil {
c.logger.Error().Err(err).Msg("Cannot create Nomad client")
return []model.Image{}
}
jobs, _, err := client.Jobs().List(nil)
if err != nil {
c.logger.Error().Err(err).Msg("Cannot list Nomad jobs")
}
var list []model.Image
for _, job := range jobs {
jobInfo, _, err := client.Jobs().Info(job.ID, nil)
if err != nil {
c.logger.Error().Err(err).Msg("Cannot get info for job")
}
for _, taskGroup := range jobInfo.TaskGroups {
// Get task group service labels
groupLabels := map[string]string{}
for _, service := range taskGroup.Services {
groupLabels = updateMap(groupLabels, parseServiceTags(service.Tags))
}
for _, task := range taskGroup.Tasks {
if task.Driver != "docker" {
continue
}
if taskImage, ok := task.Config["image"]; ok {
imageName := taskImage.(string)
if imageName == "${meta.connect.sidecar_image}" {
c.logger.Debug().
Str("job_id", job.ID).
Str("task_group", *taskGroup.Name).
Str("task_name", task.Name).
Msg("Skipping connect sidecar")
continue
}
// Get task service labels
labels := map[string]string{}
labels = updateMap(labels, groupLabels)
for _, service := range task.Services {
labels = updateMap(labels, parseServiceTags(service.Tags))
}
// Finally, merge task meta values
labels = updateMap(labels, task.Meta)
image, err := provider.ValidateImage(imageName, labels, *c.config.WatchByDefault)
if err != nil {
c.logger.Error().
Err(err).
Str("job_id", job.ID).
Str("task_group", *taskGroup.Name).
Str("task_name", task.Name).
Str("image_name", imageName).
Msg("Error validating image")
continue
} else if reflect.DeepEqual(image, model.Image{}) {
c.logger.Debug().
Str("job_id", job.ID).
Str("task_group", *taskGroup.Name).
Str("task_name", task.Name).
Str("image_name", imageName).
Msg("Watch disabled")
continue
}
list = append(list, image)
}
}
}
}
return list
}

View File

@@ -0,0 +1,53 @@
package nomad
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseServiceTags(t *testing.T) {
testCases := []struct {
input []string
expected map[string]string
}{
{
input: []string{
"noequal",
},
expected: map[string]string{},
},
{
input: []string{
"emptyequal=",
},
expected: map[string]string{
"emptyequal": "",
},
},
{
input: []string{
"key=value",
},
expected: map[string]string{
"key": "value",
},
},
{
input: []string{
"withequal=a=b",
},
expected: map[string]string{
"withequal": "a=b",
},
},
}
for _, tt := range testCases {
tt := tt
t.Run(tt.input[0], func(t *testing.T) {
result := parseServiceTags(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}