Skip to content

Commit

Permalink
Keep tags (#48)
Browse files Browse the repository at this point in the history
* rebase

* docs

* remove extra spaces

* remove extra spaces

* remove user install

* save 5s
  • Loading branch information
circa10a authored Nov 8, 2018
1 parent 8262046 commit 59ff1b2
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 16 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A python-based alternative to [watchtower](https://github.com/v2tec/watchtower)

## Overview

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.
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).

- 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.
- Limit your server ssh access
Expand Down Expand Up @@ -97,6 +97,9 @@ docker run --rm circa10a/ouroboros --help
- `--cleanup`, `-c` Remove the older docker image if a new one is found and updated.
- Default is `False`.
- Environment variable: `CLEANUP=true`
- `--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`.
- Default is `False`.
- Environment variable: `KEEPTAG=true`
### Private Registries
Expand All @@ -120,6 +123,16 @@ docker run -d --name ouroboros \

## Examples

### Monitor for updates for original tag
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.
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.
> Default is `False`
```bash
docker run -d --name ouroboros \
-v /var/run/docker.sock:/var/run/docker.sock \
circa10a/ouroboros --keep-tag
```

### Update containers on a remote host

Ouroboros can monitor things other than just local, pass the `--url` argument to update a system with the Docker API exposed.
Expand Down
16 changes: 8 additions & 8 deletions ouroboros/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,27 @@ def parse(sysargs):
parser.add_argument('-u', '--url', default=defaults.LOCAL_UNIX_SOCKET,
help='Url for tcp host (defaults to "unix://var/run/docker.sock")')

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

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

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

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

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

parser.add_argument('-c', '--cleanup', default=environ.get('CLEANUP') or False, dest="cleanup",
parser.add_argument('-c', '--cleanup', default=environ.get('CLEANUP') or False, dest='cleanup',
help='Remove old images after updating', action='store_true')

parser.add_argument('-k', '--keep-tag', default=environ.get('KEEPTAG') or False, dest='keep_tag',
help='Check for image updates of the same tag instead of pulling latest', action='store_true')
args = parser.parse_args(sysargs)

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

return args



1 change: 1 addition & 0 deletions ouroboros/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
LOGLEVEL = 'info'
RUNONCE = False
CLEANUP = False
KEEPTAG = False
4 changes: 3 additions & 1 deletion ouroboros/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ def check_credentials():
return {}


def pull_latest(image, api_client):
def pull_latest(image, keep_tag, api_client):
"""Return tag of latest image pulled"""
latest_image = image['RepoTags'][0].split(':')[0] + ':latest'
if keep_tag:
latest_image = image['RepoTags'][0]
log.debug(f'Pulling image: {latest_image}')
api_client.pull(latest_image, auth_config=check_credentials())
return api_client.inspect_image(latest_image)
Expand Down
2 changes: 1 addition & 1 deletion ouroboros/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def main(args, api_client):
current_image = api_client.inspect_image(running_container['Config']['Image'])

try:
latest_image = image.pull_latest(image=current_image, api_client=api_client)
latest_image = image.pull_latest(image=current_image, keep_tag=args.keep_tag, api_client=api_client)
except docker.errors.APIError as e:
log.error(e)
continue
Expand Down
2 changes: 1 addition & 1 deletion run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cd "$( dirname "$0" )"
function list_missing_modules {
comm -2 -3 \
<( sort ./requirements-dev.txt ) \
<( pip list format=columns | awk '$0=$1' | sort )
<( pip list --format=columns | awk '$0=$1' | sort )
}

# create associative array of missing modules
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def read_reqs(requirements):

setup(
name='ouroboros-cli',
version='0.2.2',
version='0.2.3',
description='Automatically update running docker containers',
long_description=readme(),
long_description_content_type='text/markdown',
Expand Down
5 changes: 2 additions & 3 deletions tests/integration/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_create_container():
def test_main(mocker, caplog):
mocker.patch('sys.argv', [''])
mocker.patch.dict('os.environ',
{'INTERVAL': '6',
{'INTERVAL': '1',
'LOGLEVEL': 'debug',
'RUNONCE': 'true',
'CLEANUP': 'true',
Expand All @@ -79,8 +79,7 @@ def test_container_updated(mocker):
f"{test_container_props['ports'][0]}/tcp"][0]['HostPort']
assert new_container['State']['Status'] == 'running'
assert new_container['Config']['Image'] == f'{test_repo}:latest'
assert new_container['Config']['Cmd'] == test_container_props['command'].split(
)
assert new_container['Config']['Cmd'] == test_container_props['command'].split()
assert test_container_props['environment'][0] in new_container['Config']['Env']
assert new_container['Mounts'][0]['Source'] == test_container_props['volumes'][0]
assert new_container['Mounts'][0]['Destination'] == test_container_mount_dest
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,24 @@ def test_cleanup_env_var(mocker, cleanup_env_var, cleanup_env_var_result):
mocker.patch('ouroboros.cli')
args = cli.parse([])
assert args.cleanup == cleanup_env_var_result


@pytest.mark.parametrize('keeptag_args, keeptag_result', [
(['-k', ], True),
(['--keep-tag', ], True)
])
def test_keeptag_args(mocker, keeptag_args, keeptag_result):
mocker.patch('ouroboros.cli')
args = cli.parse(keeptag_args)
assert args.keep_tag == keeptag_result


@pytest.mark.parametrize('keeptag_env_var, keeptag_env_var_result', [
({'KEEPTAG': 'true'}, 'true'),
({'_KEEPTAG': ''}, defaults.KEEPTAG),
])
def test_keeptag_env_var(mocker, keeptag_env_var, keeptag_env_var_result):
mocker.patch.dict('os.environ', keeptag_env_var)
mocker.patch('ouroboros.cli')
args = cli.parse([])
assert args.keep_tag == keeptag_env_var_result
2 changes: 2 additions & 0 deletions tests/unit/main_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def test_main_full(fake_namespace, fake_api, caplog):
fake_namespace.monitor = ["testName1"]
fake_namespace.ignore = ["derp"]
fake_namespace.cleanup = True
fake_namespace.keep_tag = True

fake_api.inspect_container.return_value = container_object # called twice
fake_api.containers.return_value = [container_object] # called twice
Expand Down Expand Up @@ -64,6 +65,7 @@ def test_main_exception(fake_namespace, fake_api, caplog):
fake_namespace.monitor = ["testName1"]
fake_namespace.ignore = ["derp"]
fake_namespace.cleanup = True
fake_namespace.keep_tag = True

fake_api.inspect_container.return_value = container_object # called twice
fake_api.containers.return_value = [container_object] # called twice
Expand Down

0 comments on commit 59ff1b2

Please sign in to comment.