mirror of
https://github.com/crazy-max/diun.git
synced 2025-03-15 03:44:47 +00:00
Automatically determine registry options based on image name (#103)
Co-authored-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
ca61056b3d
commit
c0e3f7e85f
25 changed files with 287 additions and 118 deletions
|
@ -75,14 +75,15 @@ You can define a configuration file through the option `--config` with the follo
|
|||
timeout: 10s
|
||||
|
||||
regopts:
|
||||
someregistryoptions:
|
||||
- name: "myregistry"
|
||||
username: foo
|
||||
password: bar
|
||||
timeout: 20s
|
||||
onemore:
|
||||
insecureTLS: true
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo2
|
||||
password: bar2
|
||||
insecureTLS: true
|
||||
|
||||
providers:
|
||||
docker:
|
||||
|
@ -131,11 +132,16 @@ All configuration from file can be transposed into environment variables. As an
|
|||
timeout: 10s
|
||||
|
||||
regopts:
|
||||
someregistryoptions:
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
- name: "registry.gitlab.com"
|
||||
selector: image
|
||||
username: fii
|
||||
password: bor
|
||||
timeout: 20s
|
||||
|
||||
|
||||
providers:
|
||||
kubernetes:
|
||||
tlsInsecure: false
|
||||
|
@ -168,9 +174,15 @@ Can be transposed to:
|
|||
DIUN_NOTIF_WEBHOOK_HEADERS_AUTHORIZATION=Token123456
|
||||
DIUN_NOTIF_WEBHOOK_TIMEOUT=10s
|
||||
|
||||
DIUN_REGOPTS_SOMEREGISTRYOPTIONS_USERNAME=foo
|
||||
DIUN_REGOPTS_SOMEREGISTRYOPTIONS_PASSWORD=bar
|
||||
DIUN_REGOPTS_SOMEREGISTRYOPTIONS_TIMEOUT=20s
|
||||
DIUN_REGOPTS_0_NAME=docker.io
|
||||
DIUN_REGOPTS_0_SELECTOR=image
|
||||
DIUN_REGOPTS_0_USERNAME=foo
|
||||
DIUN_REGOPTS_0_PASSWORD=bar
|
||||
DIUN_REGOPTS_1_NAME=registry.gitlab.com
|
||||
DIUN_REGOPTS_1_SELECTOR=image
|
||||
DIUN_REGOPTS_1_USERNAME=fii
|
||||
DIUN_REGOPTS_1_PASSWORD=bor
|
||||
DIUN_REGOPTS_1_TIMEOUT=20s
|
||||
|
||||
PROVIDERS_KUBERNETES_TLSINSECURE=false
|
||||
PROVIDERS_KUBERNETES_NAMESPACES=default,production
|
||||
|
|
|
@ -1,87 +1,155 @@
|
|||
# Registries options configuration
|
||||
# Registry options configuration
|
||||
|
||||
## `username`
|
||||
## Overview
|
||||
|
||||
Registry options is used to authenticate against a registry during the analysis of an image:
|
||||
|
||||
```yaml
|
||||
regopts:
|
||||
- name: "myregistry"
|
||||
username: fii
|
||||
password: bor
|
||||
timeout: 5s
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
- name: "docker.io/crazymax"
|
||||
selector: image
|
||||
usernameFile: /run/secrets/username
|
||||
passwordFile: /run/secrets/password
|
||||
```
|
||||
|
||||
`myregistry` will be used as a `name` selector (default) if referenced by its [name](#name).
|
||||
|
||||
`docker.io` will be used as an `image` selector. If an image is on DockerHub (`docker.io` domain), this registry options will
|
||||
be selected if not referenced as a `regopt` name.
|
||||
|
||||
`docker.io/crazymax` will be used as an `image` selector. If an image is on DockerHub and in `crazymax` namespace, this registry options will
|
||||
be selected if not referenced as a `regopt` name.
|
||||
|
||||
## Configuration
|
||||
|
||||
### `name`
|
||||
|
||||
Unique name for registry options. This name can be used through `diun.regopt`
|
||||
[Docker](../providers/docker.md#docker-labels) / [Swarm](../providers/swarm.md#docker-labels) label
|
||||
or [Kubernetes annotation](../providers/kubernetes.md#kubernetes-annotations) and also as `regopt` for the [file provider](../providers/file.md).
|
||||
|
||||
!!! warning
|
||||
* **Required**
|
||||
* Must be **unique**
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
- name: "myregistry"
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<KEY>_NAME`
|
||||
|
||||
### `selector`
|
||||
|
||||
What kind of selector to use to retrieve registry options. (default `name`)
|
||||
|
||||
!!! warning
|
||||
* Accepted values are `name` or `image`
|
||||
|
||||
* `name` selector is the default value and will retrieve this registry options only if it's referenced by its [name](#name).
|
||||
* `image` selector will retrieve this registry options if the given image matches the registry domain or repository path.
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
- name: "myregistry"
|
||||
selector: name
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<KEY>_SELECTOR`
|
||||
|
||||
### `username`
|
||||
|
||||
Registry username.
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
- name: "myregistry"
|
||||
username: foo
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_USERNAME`
|
||||
* `DIUN_REGOPTS_<KEY>_USERNAME`
|
||||
|
||||
## `usernameFile`
|
||||
### `usernameFile`
|
||||
|
||||
Use content of secret file as registry username if `username` not defined.
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
- name: "myregistry"
|
||||
usernameFile: /run/secrets/username
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_USERNAMEFILE`
|
||||
* `DIUN_REGOPTS_<KEY>_USERNAMEFILE`
|
||||
|
||||
## `password`
|
||||
### `password`
|
||||
|
||||
Registry password.
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
- name: "myregistry"
|
||||
username: foo
|
||||
password: bar
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_PASSWORD`
|
||||
* `DIUN_REGOPTS_<KEY>_PASSWORD`
|
||||
|
||||
## `passwordFile`
|
||||
### `passwordFile`
|
||||
|
||||
Use content of secret file as registry password if `password` not defined.
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
usernameFile: /run/secrets/username
|
||||
usernameFile: /run/secrets/password
|
||||
- name: "myregistry"
|
||||
passwordFile: /run/secrets/password
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_PASSWORDFILE`
|
||||
* `DIUN_REGOPTS_<KEY>_PASSWORDFILE`
|
||||
|
||||
## `timeout`
|
||||
### `timeout`
|
||||
|
||||
Timeout is the maximum amount of time for the TCP connection to establish. (default `10s`)
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
- name: "myregistry"
|
||||
timeout: 10s
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_TIMEOUT`
|
||||
* `DIUN_REGOPTS_<KEY>_TIMEOUT`
|
||||
|
||||
## `insecureTLS`
|
||||
### `insecureTLS`
|
||||
|
||||
Allow contacting docker registry over HTTP, or HTTPS with failed TLS verification. (default `false`)
|
||||
|
||||
!!! example "Config file"
|
||||
```yaml
|
||||
regopts:
|
||||
<name>:
|
||||
- name: "myregistry"
|
||||
insecureTLS: false
|
||||
```
|
||||
|
||||
!!! abstract "Environment variables"
|
||||
* `DIUN_REGOPTS_<NAME>_INSECURETLS`
|
||||
* `DIUN_REGOPTS_<KEY>_INSECURETLS`
|
||||
|
|
|
@ -170,7 +170,7 @@ You can configure more finely the way to analyze the image of your container thr
|
|||
| Name | Default | Description |
|
||||
|-------------------------------|---------------|---------------|
|
||||
| `diun.enable` | | Set to true to enable image analysis of this container |
|
||||
| `diun.regopts_id` | | Registry options ID from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.regopt` | | Registry options name from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.watch_repo` | `false` | Watch all tags of this container image |
|
||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||
| `diun.include_tags` | | Semi-colon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||
|
|
|
@ -17,14 +17,18 @@ watch:
|
|||
schedule: "* * * * *"
|
||||
|
||||
regopts:
|
||||
someregistryoptions:
|
||||
- name: "myregistry"
|
||||
username: fii
|
||||
password: bor
|
||||
timeout: 5s
|
||||
- name: "docker.io/crazymax"
|
||||
selector: image
|
||||
username: fii
|
||||
password: bor
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
timeout: 20
|
||||
onemore:
|
||||
username: foo2
|
||||
password: bar2
|
||||
insecureTLS: true
|
||||
|
||||
providers:
|
||||
file:
|
||||
|
@ -34,18 +38,20 @@ providers:
|
|||
```yaml
|
||||
### /path/to/config.yml
|
||||
|
||||
# Watch latest tag of crazymax/nextcloud image on docker.io (DockerHub) with registry ID 'someregistryoptions'.
|
||||
# Watch latest tag of crazymax/nextcloud image on docker.io (DockerHub)
|
||||
# with registry options named 'docker.io/crazymax' (image selector).
|
||||
- name: docker.io/crazymax/nextcloud:latest
|
||||
regopts_id: someregistryoptions
|
||||
|
||||
# Watch 4.0.0 tag of jfrog/artifactory-oss image on frog-docker-reg2.bintray.io (Bintray) with registry ID 'onemore'.
|
||||
# Watch 4.0.0 tag of jfrog/artifactory-oss image on frog-docker-reg2.bintray.io (Bintray)
|
||||
# with registry options named 'myregistry' (name selector).
|
||||
- name: jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0
|
||||
regopts_id: onemore
|
||||
regopt: myregistry
|
||||
|
||||
# Watch coreos/hyperkube image on quay.io (Quay) and assume latest tag.
|
||||
- name: quay.io/coreos/hyperkube
|
||||
|
||||
# Watch crazymax/swarm-cronjob image and assume docker.io registry and latest tag.
|
||||
# Watch crazymax/swarm-cronjob image and assume docker.io registry and latest tag
|
||||
# with registry options named 'docker.io/crazymax' (image selector).
|
||||
# Only include tags matching regexp ^1\.2\..*
|
||||
- name: crazymax/swarm-cronjob
|
||||
watch_repo: true
|
||||
|
@ -53,6 +59,7 @@ providers:
|
|||
- ^1\.2\..*
|
||||
|
||||
# Watch portainer/portainer image on docker.io (DockerHub) and assume latest tag
|
||||
# with registry options named 'docker.io' (image selector).
|
||||
# Only watch latest 10 tags and include tags matching regexp ^(0|[1-9]\d*)\..*
|
||||
- name: docker.io/portainer/portainer
|
||||
watch_repo: true
|
||||
|
@ -60,7 +67,8 @@ providers:
|
|||
include_tags:
|
||||
- ^(0|[1-9]\d*)\..*
|
||||
|
||||
# Watch alpine image (library) and assume docker.io registry and latest tag.
|
||||
# Watch alpine image (library) and assume docker.io registry and latest tag
|
||||
# with registry options named 'docker.io' (image selector).
|
||||
# Force linux/arm64/v8 platform for this image
|
||||
- name: alpine
|
||||
watch_repo: true
|
||||
|
@ -83,7 +91,8 @@ watch:
|
|||
schedule: "* * * * *"
|
||||
|
||||
regopts:
|
||||
jfrog:
|
||||
- name: "docker.bintray.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
|
||||
|
@ -97,7 +106,6 @@ providers:
|
|||
- name: crazymax/cloudflared
|
||||
watch_repo: true
|
||||
- name: docker.bintray.io/jfrog/xray-mongo:3.2.6
|
||||
regopts_id: jfrog
|
||||
```
|
||||
|
||||
Here we want to analyze all tags of `crazymax/cloudflared` and `docker.bintray.io/jfrog/xray-mongo:3.2.6` tag. Now let's start Diun:
|
||||
|
@ -164,7 +172,7 @@ The configuration file(s) defines a slice of images to analyze with the followin
|
|||
| Name | Default | Description |
|
||||
|-------------------------------|----------------------------------|---------------|
|
||||
| `name` | `latest` | Docker image name to watch using `registry/path:tag` format. If registry omitted, `docker.io` will be used and if tag omitted, `latest` will be used |
|
||||
| `regopts_id` | | Registry options ID from [`regopts`](../config/regopts.md) to use |
|
||||
| `regopt` | | Registry options name from [`regopts`](../config/regopts.md) to use |
|
||||
| `watch_repo` | `false` | Watch all tags of this image |
|
||||
| `max_tags` | `0` | Maximum number of tags to watch if `watch_repo` enabled. `0` means all of them |
|
||||
| `include_tags` | | List of regular expressions to include tags. Can be useful if you enable `watch_repo` |
|
||||
|
|
|
@ -273,7 +273,7 @@ You can configure more finely the way to analyze the image of your pods through
|
|||
| Name | Default | Description |
|
||||
|-------------------------------|---------------|---------------|
|
||||
| `diun.enable` | | Set to true to enable image analysis of this pod |
|
||||
| `diun.regopts_id` | | Registry options ID from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.regopt` | | Registry options name from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.watch_repo` | `false` | Watch all tags of this pod image |
|
||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||
| `diun.include_tags` | | Semi-colon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||
|
|
|
@ -174,7 +174,7 @@ You can configure more finely the way to analyze the image of your service throu
|
|||
| Name | Default | Description |
|
||||
|-------------------------------|---------------|---------------|
|
||||
| `diun.enable` | | Set to true to enable image analysis of this service |
|
||||
| `diun.regopts_id` | | Registry options ID from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.regopt` | | Registry options name to use from [`regopts`](../config/regopts.md) to use |
|
||||
| `diun.watch_repo` | `false` | Watch all tags of this service image |
|
||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||
| `diun.include_tags` | | Semi-colon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||
|
|
|
@ -36,19 +36,23 @@ func (di *Diun) createJob(job model.Job) {
|
|||
return
|
||||
}
|
||||
|
||||
// Registry options
|
||||
regOpts, err := di.cfg.GetRegOpts(job.Image.RegOptsID)
|
||||
// Get registry options
|
||||
reg, err := di.cfg.RegOpts.Select(job.Image.RegOpt, job.RegImage)
|
||||
if err != nil {
|
||||
sublog.Warn().Err(err).Msg("Registry options")
|
||||
} else if reg != nil {
|
||||
sublog.Debug().Str("regopt", reg.Name).Msg("Registry options will be used")
|
||||
} else {
|
||||
reg = (&model.RegOpt{}).GetDefaults()
|
||||
}
|
||||
|
||||
regUser, err := utl.GetSecret(regOpts.Username, regOpts.UsernameFile)
|
||||
regUser, err := utl.GetSecret(reg.Username, reg.UsernameFile)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("Cannot retrieve username secret for regopts %s", job.Image.RegOptsID)
|
||||
log.Warn().Err(err).Msgf("Cannot retrieve username secret for regopts %s", reg.Name)
|
||||
}
|
||||
regPassword, err := utl.GetSecret(regOpts.Password, regOpts.PasswordFile)
|
||||
regPassword, err := utl.GetSecret(reg.Password, reg.PasswordFile)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("Cannot retrieve password secret for regopts %s", job.Image.RegOptsID)
|
||||
log.Warn().Err(err).Msgf("Cannot retrieve password secret for regopts %s", reg.Name)
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
|
@ -78,8 +82,8 @@ func (di *Diun) createJob(job model.Job) {
|
|||
job.Registry, err = registry.New(registry.Options{
|
||||
Username: regUser,
|
||||
Password: regPassword,
|
||||
Timeout: *regOpts.Timeout,
|
||||
InsecureTLS: *regOpts.InsecureTLS,
|
||||
Timeout: *reg.Timeout,
|
||||
InsecureTLS: *reg.InsecureTLS,
|
||||
UserAgent: di.meta.UserAgent,
|
||||
ImageOs: job.Image.Platform.Os,
|
||||
ImageArch: job.Image.Platform.Arch,
|
||||
|
|
|
@ -14,11 +14,11 @@ import (
|
|||
|
||||
// Config holds configuration details
|
||||
type Config struct {
|
||||
Db *model.Db `yaml:"db,omitempty" json:"db,omitempty"`
|
||||
Watch *model.Watch `yaml:"watch,omitempty" json:"watch,omitempty"`
|
||||
Notif *model.Notif `yaml:"notif,omitempty" json:"notif,omitempty"`
|
||||
RegOpts map[string]*model.RegOpts `yaml:"regopts,omitempty" json:"regopts,omitempty" validate:"unique"`
|
||||
Providers *model.Providers `yaml:"providers,omitempty" json:"providers,omitempty" validate:"required"`
|
||||
Db *model.Db `yaml:"db,omitempty" json:"db,omitempty"`
|
||||
Watch *model.Watch `yaml:"watch,omitempty" json:"watch,omitempty"`
|
||||
Notif *model.Notif `yaml:"notif,omitempty" json:"notif,omitempty"`
|
||||
RegOpts model.RegOpts `yaml:"regopts,omitempty" json:"regopts,omitempty" validate:"unique=Name,dive"`
|
||||
Providers *model.Providers `yaml:"providers,omitempty" json:"providers,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
// Load returns Configuration struct
|
||||
|
@ -76,16 +76,6 @@ func (cfg *Config) loadEnv(out interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) GetRegOpts(id string) (*model.RegOpts, error) {
|
||||
if len(id) == 0 {
|
||||
return (&model.RegOpts{}).GetDefaults(), nil
|
||||
}
|
||||
if regopts, ok := cfg.RegOpts[id]; ok {
|
||||
return regopts, nil
|
||||
}
|
||||
return (&model.RegOpts{}).GetDefaults(), fmt.Errorf("%s not found", id)
|
||||
}
|
||||
|
||||
// String returns the string representation of configuration
|
||||
func (cfg *Config) String() string {
|
||||
b, _ := json.MarshalIndent(cfg, "", " ")
|
||||
|
|
|
@ -97,20 +97,28 @@ func TestLoadFile(t *testing.T) {
|
|||
Timeout: utl.NewDuration(10 * time.Second),
|
||||
},
|
||||
},
|
||||
RegOpts: map[string]*model.RegOpts{
|
||||
"someregopts": {
|
||||
RegOpts: model.RegOpts{
|
||||
{
|
||||
Name: "myregistry",
|
||||
Selector: model.RegOptSelectorName,
|
||||
Username: "fii",
|
||||
Password: "bor",
|
||||
InsecureTLS: utl.NewFalse(),
|
||||
Timeout: utl.NewDuration(5 * time.Second),
|
||||
},
|
||||
"bintrayoptions": {
|
||||
{
|
||||
Name: "docker.io",
|
||||
Selector: model.RegOptSelectorImage,
|
||||
Username: "foo",
|
||||
Password: "bar",
|
||||
InsecureTLS: utl.NewFalse(),
|
||||
Timeout: utl.NewDuration(10 * time.Second),
|
||||
},
|
||||
"sensitive": {
|
||||
UsernameFile: "/run/secrets/username",
|
||||
PasswordFile: "/run/secrets/password",
|
||||
{
|
||||
Name: "docker.io/crazymax",
|
||||
Selector: model.RegOptSelectorImage,
|
||||
UsernameFile: "./fixtures/run_secrets_username",
|
||||
PasswordFile: "./fixtures/run_secrets_password",
|
||||
InsecureTLS: utl.NewFalse(),
|
||||
Timeout: utl.NewDuration(10 * time.Second),
|
||||
},
|
||||
|
@ -130,7 +138,7 @@ func TestLoadFile(t *testing.T) {
|
|||
WatchByDefault: utl.NewTrue(),
|
||||
},
|
||||
File: &model.PrdFile{
|
||||
Filename: "./fixtures/dummy.yml",
|
||||
Filename: "./fixtures/file.yml",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -191,18 +199,22 @@ func TestLoadEnv(t *testing.T) {
|
|||
{
|
||||
desc: "docker provider and regopts",
|
||||
environ: []string{
|
||||
"DIUN_REGOPTS_SENSITIVE_USERNAMEFILE=/run/secrets/username",
|
||||
"DIUN_REGOPTS_SENSITIVE_PASSWORDFILE=/run/secrets/password",
|
||||
"DIUN_REGOPTS_SENSITIVE_TIMEOUT=30s",
|
||||
"DIUN_REGOPTS_0_NAME=docker.io",
|
||||
"DIUN_REGOPTS_0_SELECTOR=image",
|
||||
"DIUN_REGOPTS_0_USERNAMEFILE=./fixtures/run_secrets_username",
|
||||
"DIUN_REGOPTS_0_PASSWORDFILE=./fixtures/run_secrets_password",
|
||||
"DIUN_REGOPTS_0_TIMEOUT=30s",
|
||||
"DIUN_PROVIDERS_DOCKER=true",
|
||||
},
|
||||
expected: &config.Config{
|
||||
Db: (&model.Db{}).GetDefaults(),
|
||||
Watch: (&model.Watch{}).GetDefaults(),
|
||||
RegOpts: map[string]*model.RegOpts{
|
||||
"sensitive": {
|
||||
UsernameFile: "/run/secrets/username",
|
||||
PasswordFile: "/run/secrets/password",
|
||||
RegOpts: model.RegOpts{
|
||||
{
|
||||
Name: "docker.io",
|
||||
Selector: model.RegOptSelectorImage,
|
||||
UsernameFile: "./fixtures/run_secrets_username",
|
||||
PasswordFile: "./fixtures/run_secrets_password",
|
||||
InsecureTLS: utl.NewFalse(),
|
||||
Timeout: utl.NewDuration(30 * time.Second),
|
||||
},
|
||||
|
@ -348,8 +360,8 @@ func TestLoadMixed(t *testing.T) {
|
|||
wantErr: false,
|
||||
},
|
||||
{
|
||||
desc: "file provider, regopts (file) and notif webhook env override",
|
||||
cfgfile: "./fixtures/config.file-regopts.yml",
|
||||
desc: "file provider and notif webhook env override",
|
||||
cfgfile: "./fixtures/config.file.yml",
|
||||
environ: []string{
|
||||
"DIUN_NOTIF_WEBHOOK_ENDPOINT=http://webhook.foo.com/sd54qad89azd5a",
|
||||
"DIUN_NOTIF_WEBHOOK_HEADERS_AUTHORIZATION=Token78910",
|
||||
|
@ -374,7 +386,7 @@ func TestLoadMixed(t *testing.T) {
|
|||
RegOpts: nil,
|
||||
Providers: &model.Providers{
|
||||
File: &model.PrdFile{
|
||||
Filename: "./fixtures/dummy.yml",
|
||||
Filename: "./fixtures/file.yml",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -8,4 +8,4 @@ notif:
|
|||
|
||||
providers:
|
||||
file:
|
||||
filename: "./fixtures/dummy.yml"
|
||||
filename: "./fixtures/file.yml"
|
|
@ -53,14 +53,18 @@ notif:
|
|||
timeout: 10s
|
||||
|
||||
regopts:
|
||||
someregopts:
|
||||
- name: "myregistry"
|
||||
username: fii
|
||||
password: bor
|
||||
timeout: 5s
|
||||
bintrayoptions:
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
sensitive:
|
||||
usernameFile: /run/secrets/username
|
||||
passwordFile: /run/secrets/password
|
||||
- name: "docker.io/crazymax"
|
||||
selector: image
|
||||
usernameFile: ./fixtures/run_secrets_username
|
||||
passwordFile: ./fixtures/run_secrets_password
|
||||
|
||||
providers:
|
||||
docker:
|
||||
|
@ -70,4 +74,4 @@ providers:
|
|||
kubernetes:
|
||||
watchByDefault: true
|
||||
file:
|
||||
filename: ./fixtures/dummy.yml
|
||||
filename: ./fixtures/file.yml
|
||||
|
|
|
@ -53,14 +53,16 @@ notif:
|
|||
timeout: 10s
|
||||
|
||||
regopts:
|
||||
someregopts:
|
||||
- name: "myregistry"
|
||||
timeout: 5s
|
||||
bintrayoptions:
|
||||
- name: "docker.io"
|
||||
selector: image
|
||||
username: foo
|
||||
password: bar
|
||||
sensitive:
|
||||
usernameFile: /run/secrets/username
|
||||
passwordFile: /run/secrets/password
|
||||
- name: "docker.io/crazymax"
|
||||
selector: image
|
||||
usernameFile: ./fixtures/run_secrets_username
|
||||
passwordFile: ./fixtures/run_secrets_username
|
||||
|
||||
providers:
|
||||
docker:
|
||||
|
@ -68,4 +70,4 @@ providers:
|
|||
watchStopped: true
|
||||
swarm: {}
|
||||
file:
|
||||
filename: ./fixtures/dummy.yml
|
||||
filename: ./fixtures/file.yml
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# noop
|
24
internal/config/fixtures/file.yml
Normal file
24
internal/config/fixtures/file.yml
Normal file
|
@ -0,0 +1,24 @@
|
|||
- name: docker.io/crazymax/nextcloud:latest
|
||||
regopt: myregistry
|
||||
- name: crazymax/swarm-cronjob
|
||||
watch_repo: true
|
||||
include_tags:
|
||||
- ^1\.2\..*
|
||||
- name: docker.io/portainer/portainer
|
||||
watch_repo: true
|
||||
max_tags: 10
|
||||
include_tags:
|
||||
- ^(0|[1-9]\d*)\..*
|
||||
- name: traefik
|
||||
watch_repo: true
|
||||
- name: alpine
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
variant: v8
|
||||
- name: docker.io/graylog/graylog:3.2.0
|
||||
- name: jacobalberty/unifi:5.9
|
||||
- name: crazymax/ddns-route53
|
||||
watch_repo: true
|
||||
include_tags:
|
||||
- ^1\..*
|
1
internal/config/fixtures/run_secrets_password
Normal file
1
internal/config/fixtures/run_secrets_password
Normal file
|
@ -0,0 +1 @@
|
|||
bar
|
1
internal/config/fixtures/run_secrets_username
Normal file
1
internal/config/fixtures/run_secrets_username
Normal file
|
@ -0,0 +1 @@
|
|||
foo
|
|
@ -28,7 +28,7 @@ func (c *Client) Migrate() error {
|
|||
|
||||
log.Info().Msgf("Database migration v%d...", version)
|
||||
if err := migration(c); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Database migration v%d failed", version))
|
||||
return errors.Wrapf(err, "Database migration v%d failed", version)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ package model
|
|||
type Image struct {
|
||||
Name string `yaml:"name,omitempty" json:",omitempty"`
|
||||
Platform ImagePlatform `yaml:"platform,omitempty" json:",omitempty"`
|
||||
RegOptsID string `yaml:"regopts_id,omitempty" json:",omitempty"`
|
||||
RegOpt string `yaml:"regopt,omitempty" json:",omitempty"`
|
||||
WatchRepo bool `yaml:"watch_repo,omitempty" json:",omitempty"`
|
||||
MaxTags int `yaml:"max_tags,omitempty" json:",omitempty"`
|
||||
IncludeTags []string `yaml:"include_tags,omitempty" json:",omitempty"`
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||
)
|
||||
|
||||
// RegOpts holds registry options configuration
|
||||
type RegOpts struct {
|
||||
// RegOpts holds slice of registry options
|
||||
type RegOpts []RegOpt
|
||||
|
||||
// RegOpt holds registry options configuration
|
||||
type RegOpt struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty" validate:"required"`
|
||||
Selector RegOptSelector `yaml:"selector,omitempty" json:"selector,omitempty" validate:"required,oneof=name image"`
|
||||
Username string `yaml:"username,omitempty" json:"username,omitempty" validate:"omitempty"`
|
||||
UsernameFile string `yaml:"usernameFile,omitempty" json:"usernameFile,omitempty" validate:"omitempty,file"`
|
||||
Password string `yaml:"password,omitempty" json:"password,omitempty" validate:"omitempty"`
|
||||
|
@ -16,15 +24,41 @@ type RegOpts struct {
|
|||
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
// RegOpt selector constants
|
||||
const (
|
||||
RegOptSelectorName = RegOptSelector("name")
|
||||
RegOptSelectorImage = RegOptSelector("image")
|
||||
)
|
||||
|
||||
// RegOptSelector holds registry options selector
|
||||
type RegOptSelector string
|
||||
|
||||
// GetDefaults gets the default values
|
||||
func (s *RegOpts) GetDefaults() *RegOpts {
|
||||
n := &RegOpts{}
|
||||
func (s *RegOpt) GetDefaults() *RegOpt {
|
||||
n := &RegOpt{}
|
||||
n.SetDefaults()
|
||||
return n
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values
|
||||
func (s *RegOpts) SetDefaults() {
|
||||
func (s *RegOpt) SetDefaults() {
|
||||
s.Selector = RegOptSelectorName
|
||||
s.InsecureTLS = utl.NewFalse()
|
||||
s.Timeout = utl.NewDuration(10 * time.Second)
|
||||
}
|
||||
|
||||
// Select returns a registry based on its selector
|
||||
func (s *RegOpts) Select(name string, image registry.Image) (*RegOpt, error) {
|
||||
for _, regOpt := range *s {
|
||||
if regOpt.Selector == RegOptSelectorName && name == regOpt.Name {
|
||||
return ®Opt, nil
|
||||
}
|
||||
if regOpt.Selector == RegOptSelectorImage && strings.HasPrefix(image.Name(), regOpt.Name) {
|
||||
return ®Opt, nil
|
||||
}
|
||||
}
|
||||
if len(name) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s not found", name)
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ func ValidateContainerImage(image string, labels map[string]string, watchByDef b
|
|||
|
||||
for key, value := range labels {
|
||||
switch key {
|
||||
case "diun.regopts_id":
|
||||
img.RegOptsID = value
|
||||
case "diun.regopt":
|
||||
img.RegOpt = value
|
||||
case "diun.watch_repo":
|
||||
if img.WatchRepo, err = strconv.ParseBool(value); err != nil {
|
||||
return img, fmt.Errorf("cannot parse %s value of label %s", value, key)
|
||||
|
|
|
@ -13,8 +13,8 @@ var (
|
|||
{
|
||||
Provider: "file",
|
||||
Image: model.Image{
|
||||
Name: "jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0",
|
||||
RegOptsID: "bintrayoptions",
|
||||
Name: "jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0",
|
||||
RegOpt: "bintrayoptions",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -30,8 +30,8 @@ var (
|
|||
{
|
||||
Provider: "file",
|
||||
Image: model.Image{
|
||||
Name: "docker.io/crazymax/nextcloud:latest",
|
||||
RegOptsID: "someregopts",
|
||||
Name: "docker.io/crazymax/nextcloud:latest",
|
||||
RegOpt: "myregistry",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- name: jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0
|
||||
regopts_id: bintrayoptions
|
||||
regopt: bintrayoptions
|
||||
- name: docker.bintray.io/jfrog/xray-server:2.8.6
|
||||
watch_repo: true
|
||||
max_tags: 50
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- name: docker.io/crazymax/nextcloud:latest
|
||||
regopts_id: someregopts
|
||||
regopt: myregistry
|
||||
- name: crazymax/swarm-cronjob
|
||||
watch_repo: true
|
||||
include_tags:
|
||||
|
|
|
@ -60,7 +60,7 @@ func ParseImage(parseOpts ParseImageOptions) (Image, error) {
|
|||
// Parse the image name and tag.
|
||||
named, err := reference.ParseNormalizedNamed(parseOpts.Name)
|
||||
if err != nil {
|
||||
return Image{}, errors.Wrap(err, fmt.Sprintf("parsing image %s failed", parseOpts.Name))
|
||||
return Image{}, errors.Wrapf(err, "parsing image %s failed", parseOpts.Name)
|
||||
}
|
||||
// Add the latest lag if they did not provide one.
|
||||
named = reference.TagNameOnly(named)
|
||||
|
|
|
@ -32,7 +32,7 @@ type Options struct {
|
|||
// New creates new docker registry client instance
|
||||
func New(opts Options) (*Client, error) {
|
||||
// Auth
|
||||
auth := &types.DockerAuthConfig{}
|
||||
var auth *types.DockerAuthConfig
|
||||
if opts.Username != "" {
|
||||
auth = &types.DockerAuthConfig{
|
||||
Username: opts.Username,
|
||||
|
@ -75,6 +75,16 @@ func (c *Client) newImage(ctx context.Context, imageStr string) (types.ImageClos
|
|||
return nil, errors.Wrap(err, "Invalid image name")
|
||||
}
|
||||
|
||||
if c.sysCtx.DockerAuthConfig == nil {
|
||||
c.sysCtx.DockerAuthConfig = &types.DockerAuthConfig{}
|
||||
// TODO: Seek credentials
|
||||
//auth, err := config.GetCredentials(c.sysCtx, reference.Domain(ref.DockerReference()))
|
||||
//if err != nil {
|
||||
// return nil, errors.Wrap(err, "Cannot get registry credentials")
|
||||
//}
|
||||
//*c.sysCtx.DockerAuthConfig = auth
|
||||
}
|
||||
|
||||
img, err := ref.NewImage(ctx, c.sysCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in a new issue