Skip to content

Commit 59ff1b2

Browse files
authored
Keep tags (#48)
* rebase * docs * remove extra spaces * remove extra spaces * remove user install * save 5s
1 parent 8262046 commit 59ff1b2

File tree

10 files changed

+54
-16
lines changed

10 files changed

+54
-16
lines changed

README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ A python-based alternative to [watchtower](https://github.com/v2tec/watchtower)
1515

1616
## Overview
1717

18-
Ouroboros will monitor all running docker containers or those you specify and update said containers to the latest available image in the remote registry using the `latest` tag with the same parameters that were used when the container was first created such as volume/bind mounts, docker network connections, environment variables, restart policies, entrypoints, commands, etc.
18+
Ouroboros will monitor all running docker containers or those you specify and update said containers to the latest available image in the remote registry using the `latest` tag with the same parameters that were used when the container was first created such as volume/bind mounts, docker network connections, environment variables, restart policies, entrypoints, commands, etc. While ouroboros updates images to `latest` by default, that can be [overridden](#Options) to only monitor updates of a specific tag. Similar to [watchtower](https://github.com/v2tec/watchtower).
1919

2020
- Push your image to your registry and simply wait a couple of minutes for ouroboros to find the new image and redeploy your container autonomously.
2121
- Limit your server ssh access
@@ -97,6 +97,9 @@ docker run --rm circa10a/ouroboros --help
9797
- `--cleanup`, `-c` Remove the older docker image if a new one is found and updated.
9898
- Default is `False`.
9999
- Environment variable: `CLEANUP=true`
100+
- `--keep-tag`, `-k` Only monitor if updates are made to the tag of the image that the container was created with instead of using `latest`.
101+
- Default is `False`.
102+
- Environment variable: `KEEPTAG=true`
100103
101104
### Private Registries
102105
@@ -120,6 +123,16 @@ docker run -d --name ouroboros \
120123

121124
## Examples
122125

126+
### Monitor for updates for original tag
127+
Instead of always updating to `latest` you can specify if you would like Ouroboros to only check for updates for your original container's image tag.
128+
e.g. If your container was start with `nginx:1.14-alpine` using `--keep-tag` will poll the docker registry and compare digests. If there is a new image for `nginx:1.14-alpine`, ouroboros will update your container using the newly patched version.
129+
> Default is `False`
130+
```bash
131+
docker run -d --name ouroboros \
132+
-v /var/run/docker.sock:/var/run/docker.sock \
133+
circa10a/ouroboros --keep-tag
134+
```
135+
123136
### Update containers on a remote host
124137

125138
Ouroboros can monitor things other than just local, pass the `--url` argument to update a system with the Docker API exposed.

ouroboros/cli.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,27 @@ def parse(sysargs):
5353
parser.add_argument('-u', '--url', default=defaults.LOCAL_UNIX_SOCKET,
5454
help='Url for tcp host (defaults to "unix://var/run/docker.sock")')
5555

56-
parser.add_argument('-i', '--interval', type=int, default=get_interval_env() or defaults.INTERVAL, dest="interval",
56+
parser.add_argument('-i', '--interval', type=int, default=get_interval_env() or defaults.INTERVAL, dest='interval',
5757
help='Interval in seconds between checking for updates (defaults to 300s)')
5858

59-
parser.add_argument('-m', '--monitor', nargs='+', default=environ.get('MONITOR') or [], dest="monitor",
59+
parser.add_argument('-m', '--monitor', nargs='+', default=environ.get('MONITOR') or [], dest='monitor',
6060
help='Which container to monitor (defaults to all running).')
6161

6262
parser.add_argument('-n', '--ignore', nargs='+', default=environ.get('IGNORE') or [], dest='ignore',
6363
help='Which container(s) to ignore.')
6464

6565
parser.add_argument('-l', '--loglevel', choices=['notset', 'debug', 'info', 'warn', 'error', 'critical'],
66-
dest="loglevel", default=environ.get('LOGLEVEL') or 'info',
66+
dest='loglevel', default=environ.get('LOGLEVEL') or 'info',
6767
help='Change logger mode (defaults to info)')
6868

69-
parser.add_argument('-r', '--runonce', default=environ.get('RUNONCE') or False, dest="run_once",
69+
parser.add_argument('-r', '--runonce', default=environ.get('RUNONCE') or False, dest='run_once',
7070
help='Only run ouroboros once then exit', action='store_true')
7171

72-
parser.add_argument('-c', '--cleanup', default=environ.get('CLEANUP') or False, dest="cleanup",
72+
parser.add_argument('-c', '--cleanup', default=environ.get('CLEANUP') or False, dest='cleanup',
7373
help='Remove old images after updating', action='store_true')
74+
75+
parser.add_argument('-k', '--keep-tag', default=environ.get('KEEPTAG') or False, dest='keep_tag',
76+
help='Check for image updates of the same tag instead of pulling latest', action='store_true')
7477
args = parser.parse_args(sysargs)
7578

7679
if not args.url:
@@ -80,6 +83,3 @@ def parse(sysargs):
8083
args.url = args.url if checkURI(args.url) else defaults.LOCAL_UNIX_SOCKET
8184

8285
return args
83-
84-
85-

ouroboros/defaults.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
LOGLEVEL = 'info'
55
RUNONCE = False
66
CLEANUP = False
7+
KEEPTAG = False

ouroboros/image.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ def check_credentials():
1313
return {}
1414

1515

16-
def pull_latest(image, api_client):
16+
def pull_latest(image, keep_tag, api_client):
1717
"""Return tag of latest image pulled"""
1818
latest_image = image['RepoTags'][0].split(':')[0] + ':latest'
19+
if keep_tag:
20+
latest_image = image['RepoTags'][0]
1921
log.debug(f'Pulling image: {latest_image}')
2022
api_client.pull(latest_image, auth_config=check_credentials())
2123
return api_client.inspect_image(latest_image)

ouroboros/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def main(args, api_client):
1616
current_image = api_client.inspect_image(running_container['Config']['Image'])
1717

1818
try:
19-
latest_image = image.pull_latest(image=current_image, api_client=api_client)
19+
latest_image = image.pull_latest(image=current_image, keep_tag=args.keep_tag, api_client=api_client)
2020
except docker.errors.APIError as e:
2121
log.error(e)
2222
continue

run_tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cd "$( dirname "$0" )"
1010
function list_missing_modules {
1111
comm -2 -3 \
1212
<( sort ./requirements-dev.txt ) \
13-
<( pip list format=columns | awk '$0=$1' | sort )
13+
<( pip list --format=columns | awk '$0=$1' | sort )
1414
}
1515

1616
# create associative array of missing modules

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def read_reqs(requirements):
1313

1414
setup(
1515
name='ouroboros-cli',
16-
version='0.2.2',
16+
version='0.2.3',
1717
description='Automatically update running docker containers',
1818
long_description=readme(),
1919
long_description_content_type='text/markdown',

tests/integration/main_test.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def test_create_container():
5959
def test_main(mocker, caplog):
6060
mocker.patch('sys.argv', [''])
6161
mocker.patch.dict('os.environ',
62-
{'INTERVAL': '6',
62+
{'INTERVAL': '1',
6363
'LOGLEVEL': 'debug',
6464
'RUNONCE': 'true',
6565
'CLEANUP': 'true',
@@ -79,8 +79,7 @@ def test_container_updated(mocker):
7979
f"{test_container_props['ports'][0]}/tcp"][0]['HostPort']
8080
assert new_container['State']['Status'] == 'running'
8181
assert new_container['Config']['Image'] == f'{test_repo}:latest'
82-
assert new_container['Config']['Cmd'] == test_container_props['command'].split(
83-
)
82+
assert new_container['Config']['Cmd'] == test_container_props['command'].split()
8483
assert test_container_props['environment'][0] in new_container['Config']['Env']
8584
assert new_container['Mounts'][0]['Source'] == test_container_props['volumes'][0]
8685
assert new_container['Mounts'][0]['Destination'] == test_container_mount_dest

tests/unit/cli_test.py

+21
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,24 @@ def test_cleanup_env_var(mocker, cleanup_env_var, cleanup_env_var_result):
145145
mocker.patch('ouroboros.cli')
146146
args = cli.parse([])
147147
assert args.cleanup == cleanup_env_var_result
148+
149+
150+
@pytest.mark.parametrize('keeptag_args, keeptag_result', [
151+
(['-k', ], True),
152+
(['--keep-tag', ], True)
153+
])
154+
def test_keeptag_args(mocker, keeptag_args, keeptag_result):
155+
mocker.patch('ouroboros.cli')
156+
args = cli.parse(keeptag_args)
157+
assert args.keep_tag == keeptag_result
158+
159+
160+
@pytest.mark.parametrize('keeptag_env_var, keeptag_env_var_result', [
161+
({'KEEPTAG': 'true'}, 'true'),
162+
({'_KEEPTAG': ''}, defaults.KEEPTAG),
163+
])
164+
def test_keeptag_env_var(mocker, keeptag_env_var, keeptag_env_var_result):
165+
mocker.patch.dict('os.environ', keeptag_env_var)
166+
mocker.patch('ouroboros.cli')
167+
args = cli.parse([])
168+
assert args.keep_tag == keeptag_env_var_result

tests/unit/main_unit_test.py

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def test_main_full(fake_namespace, fake_api, caplog):
3333
fake_namespace.monitor = ["testName1"]
3434
fake_namespace.ignore = ["derp"]
3535
fake_namespace.cleanup = True
36+
fake_namespace.keep_tag = True
3637

3738
fake_api.inspect_container.return_value = container_object # called twice
3839
fake_api.containers.return_value = [container_object] # called twice
@@ -64,6 +65,7 @@ def test_main_exception(fake_namespace, fake_api, caplog):
6465
fake_namespace.monitor = ["testName1"]
6566
fake_namespace.ignore = ["derp"]
6667
fake_namespace.cleanup = True
68+
fake_namespace.keep_tag = True
6769

6870
fake_api.inspect_container.return_value = container_object # called twice
6971
fake_api.containers.return_value = [container_object] # called twice

0 commit comments

Comments
 (0)