Skip to content

Commit

Permalink
Merge pull request #17 from jizhilong/customize-tag-names
Browse files Browse the repository at this point in the history
add support for customizing tag names.
  • Loading branch information
jizhilong committed Jul 15, 2017
2 parents acb74be + 94e3f78 commit de68bf5
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 55 deletions.
6 changes: 6 additions & 0 deletions .docker-make.yml
@@ -1,3 +1,8 @@
tag-names:
- name: datetime
type: datetime
value: '%Y%m%d%H%M'

builds:
test:
context: /
Expand All @@ -10,6 +15,7 @@ builds:
dockerfile: Dockerfile
pushes:
- 'always=jizhilong/docker-make:{fcommitid}'
- 'always=jizhilong/docker-make:{datetime}'
- 'on_tag=jizhilong/docker-make:{git_tag}'
- 'on_branch:master=jizhilong/docker-make:latest'
labels:
Expand Down
54 changes: 1 addition & 53 deletions README.md
Expand Up @@ -16,7 +16,7 @@ building,tagging,and pusing a bunch of related docker images.
* [two images-one for deploy, one for unit tests](#two-images-one-for-deploy-one-for-unit-tests)
* [several images-one as base with tools, others for different applications](#several-images-one-as-base-with-tools-others-for-different-applications)
- [CLI reference](#command-line-reference)
- [.docker-make.yml reference](#docker-makeyml)
- [.docker-make.yml reference](docs/yaml-configuration-reference.md)

## installation
### install via pip
Expand Down Expand Up @@ -243,55 +243,3 @@ optional arguments:
--dry-run print docker commands only
--no-push build only, dont push
```
## .docker-make.yml
### builds(reserved keyword)
`builds` is a reserved a keyword for docker-make, you should define your builds under this section.
### `dwait` and `dresponse` (essential, string)
names for your build.
### `context` (essential, string)
path to build context, relative to the root of the repo.
### `dockerfile` (essential, string)
Dockerfile for the build, relative to the context.
### `pushes` (optional, [string], default: [])
pushing rule for the built image, a single rule is composed in a form of '<push_mode>=<repo>:<tag_template>',
in which:
* `push_mode` defines when to push, choices include:
* `always`: always push the successfully built image.
* `on_tag`: push if built on a git tag.
* `on_branch:<branchname>`: push if built on branch `branchname`
* `repo` defines which repo to push to.
* `tag_template` is a python formattable string for generating the image tag, available template variables include:
* `date`: date of the built(e.g, 20160617)
* `scommitid`: a 7-char trunc of the corresponding git sha-1 commit id.
* `fcommitid`: full git commit id.
* `git_tag`: git tag name (if built on a git tag)
* `git_branch`: git branch name(if built on a git branch)
### `dockerignore` (optional, [string], default: [])
files or directories you want ignore in the context, during `docker build`
ref: [dockerignore](https://docs.docker.com/engine/reference/builder/#dockerignore-file)
### `labels` (optional, [string])
define labels applied to built image, each item should be with format '<key>="<value>"', with `<value>`
being a python template string, available template variables include:
* `scommitid`: a 7-char trunc of the corresponding git sha-1 commit id.
* `fcommitid`: full git commit id.
* `git_tag`: git tag name (if built on a git tag)
* `git_branch`: git branch name(if built on a git branch)
### `depends_on` (optional, [string], default: [])
which builds this build depends on, `docker-make` will build the depends first.
### `extract` (optional, [string], default: [])
define a list of source-destination pairs, with `source` point to a path of the newly built image, and `destination` being a filename on the host, `docker-make` will package `source` in a tar file, and copy the tar file to `destination`. Each item's syntax is similar to `docker run -v`
### `rewrite_from` (optional, string, default: '')
a build's name which should be available in `.docker-make.yml`, if supplied, `docker-make` will build `rewrite_from` first, and replace current build's Dockerfile's `FROM` with `rewrite_from`'s fresh image id.
2 changes: 2 additions & 0 deletions dmake/cli.py
Expand Up @@ -3,6 +3,7 @@

from dmake.errors import * # noqa
from dmake import utils
from dmake import template_args
import dmake.build

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -40,6 +41,7 @@ def _main():
LOG = logging.getLogger("docker-make")

try:
template_args.init_tag_names(args.dmakefile)
builds_order, builds_dict = utils.get_sorted_build_dicts_from_yaml(
args.dmakefile)
except ConfigurationError as e:
Expand Down
52 changes: 50 additions & 2 deletions dmake/template_args.py
Expand Up @@ -2,6 +2,8 @@
import logging
import subprocess

from dmake import utils


LOG = logging.getLogger(__name__)
_tag_template_args = None
Expand All @@ -15,9 +17,18 @@ def gen_args(self):
yield


class DateGenerator(TemplateArgsGenerator):
class DateTimeGenerator(TemplateArgsGenerator):
def __init__(self, name, format):
self.name = name
self.format = format

def gen_args(self):
yield 'date', datetime.datetime.now().strftime("%Y%m%d")
yield self.name, datetime.datetime.now().strftime(self.format)


class DateGenerator(DateTimeGenerator):
def __init__(self):
super(DateGenerator, self).__init__('date', '%Y%m%d')


class ExternalCmdGenerator(TemplateArgsGenerator):
Expand Down Expand Up @@ -103,3 +114,40 @@ def label_template_args(extra_generators=None):
generators.extend(extra_generators)
_label_template_args = _template_args(generators)
return _label_template_args


def init_tag_names(dmakefile):
data = utils.load_yaml(dmakefile)
configurations = data.get('tag-names', None)
extra_generators = create_extra_generators(configurations)
label_template_args(extra_generators)
tag_template_args(extra_generators)


def create_extra_generators(configurations):
if configurations is None:
return []

configurable_tag_name_generators = {
'datetime': DateTimeGenerator,
'cmd': ExternalCmdGenerator
}

tag_name_generators = []

for config in configurations:
if not validate_tag_name_config(config):
continue
name, type_, value = config['name'], config['type'], config['value']
cls = configurable_tag_name_generators.get(type_, None)
if cls is not None:
tag_name_generators.append(cls(name, value))
return tag_name_generators


def validate_tag_name_config(config):
for key in ('name', 'type', 'value'):
if key not in config:
LOG.warn("%s absent in %s", key, config)
return False
return True
68 changes: 68 additions & 0 deletions docs/yaml-configuration-reference.md
@@ -0,0 +1,68 @@
# reference for .docker-make.yml

## tag-names(essential, list of dict, default: [])
definition of customized tag names.

### `name` (essential, string)
name of the new customized tag, which can be referred in a tag template.

### `type` (essential, string)
type of the new customized tag which produces a tag name based on the value of `value` field, choices include:
* `datetime`
* `cmd`

### `value` (essential, string)
argument passed to the tag name generator specified by the `type` field:
* for `datetime` type, value is a Python datetime formatter, e.g '%Y%m%d%H%M'.
* for `cmd` type, value is a shell command, e.g. `echo hello-world`.


## builds(essential, dict, default: {})
definition of `docker-builds` and their relationships.

### name of build(e.g, `dwait` and `dresponse`) (essential, string)
names for your build.

### `context` (essential, string)
path to build context, relative to the root of the repo.

### `dockerfile` (essential, string)
Dockerfile for the build, relative to the context.

### `pushes` (optional, [string], default: [])
pushing rule for the built image, a single rule is composed in a form of '<push_mode>=<repo>:<tag_template>',
in which:
* `push_mode` defines when to push, choices include:
* `always`: always push the successfully built image.
* `on_tag`: push if built on a git tag.
* `on_branch:<branchname>`: push if built on branch `branchname`

* `repo` defines which repo to push to.

* `tag_template` is a python formattable string for generating the image tag, available template variables include:
* `date`: date of the built(e.g, 20160617)
* `scommitid`: a 7-char trunc of the corresponding git sha-1 commit id.
* `fcommitid`: full git commit id.
* `git_tag`: git tag name (if built on a git tag)
* `git_branch`: git branch name(if built on a git branch)

### `dockerignore` (optional, [string], default: [])
files or directories you want ignore in the context, during `docker build`
ref: [dockerignore](https://docs.docker.com/engine/reference/builder/#dockerignore-file)

### `labels` (optional, [string])
define labels applied to built image, each item should be with format '<key>="<value>"', with `<value>`
being a python template string, available template variables include:
* `scommitid`: a 7-char trunc of the corresponding git sha-1 commit id.
* `fcommitid`: full git commit id.
* `git_tag`: git tag name (if built on a git tag)
* `git_branch`: git branch name(if built on a git branch)

### `depends_on` (optional, [string], default: [])
which builds this build depends on, `docker-make` will build the depends first.

### `extract` (optional, [string], default: [])
define a list of source-destination pairs, with `source` point to a path of the newly built image, and `destination` being a filename on the host, `docker-make` will package `source` in a tar file, and copy the tar file to `destination`. Each item's syntax is similar to `docker run -v`

### `rewrite_from` (optional, string, default: '')
a build's name which should be available in `.docker-make.yml`, if supplied, `docker-make` will build `rewrite_from` first, and replace current build's Dockerfile's `FROM` with `rewrite_from`'s fresh image id.
68 changes: 68 additions & 0 deletions tests/test_template_args.py
Expand Up @@ -18,6 +18,17 @@ def test_date_generator(self, mocked_datetime):
self.assertEqual(v, '20160721')
mocked_datetime.now.assert_called_once()

@mock.patch('datetime.datetime')
def test_datetime_generator(self, mocked_datetime):
mocked_datetime.now.return_value = datetime(2016, 7, 21, 12, 23)
args_date = next(template_args.DateTimeGenerator(
'datetime', '%Y%m%d%H%M').gen_args(), None)
self.assertIsInstance(args_date, tuple)
k, v = args_date
self.assertEqual(k, 'datetime')
self.assertEqual(v, '201607211223')
mocked_datetime.now.assert_called_once()


class ExternalCmdGeneratorTests(unittest2.TestCase):

Expand Down Expand Up @@ -139,3 +150,60 @@ def test_label_template_args(self, mocked__template_args):
for obj, cls in zip(mocked__template_args.call_args[0][0],
generator_classes):
self.assertIsInstance(obj, cls)

def test_validate_tag_name_config(self):
func = template_args.validate_tag_name_config
self.assertTrue(func({
'type': 'cmd',
'name': 'dummy',
'value': 'echo dummt',
}))
self.assertFalse(func({
'name': 'dummy',
'value': 'echo dummt',
}))
self.assertFalse(func({
'type': 'cmd',
'value': 'echo dummt',
}))
self.assertFalse(func({
'type': 'cmd',
'name': 'dummy',
}))
self.assertFalse(func({
}))

def test_create_extra_generators(self):
configurations = [
{'type': 'cmd',
'name': 'dummy',
'value': 'echo dummt'},
]
result = template_args.create_extra_generators(configurations)
self.assertEqual(1, len(result))
self.assertIsInstance(result[0],
template_args.ExternalCmdGenerator)
configurations = [
{'type': 'datetime',
'name': 'time',
'value': '%H%M'},
{'type': 'notexist',
'name': 'dummy',
'value': 'dummy'}
]
result = template_args.create_extra_generators(configurations)
self.assertEqual(1, len(result))
self.assertIsInstance(result[0],
template_args.DateTimeGenerator)

@mock.patch('dmake.utils.load_yaml')
@mock.patch('dmake.template_args.label_template_args')
@mock.patch('dmake.template_args.tag_template_args')
def test_init_tag_names(self, patched_tag_template_args,
patched_label_template_args,
patched_load_yaml):
patched_load_yaml.return_value = {'tag-names': []}
template_args.init_tag_names('.docker-make.yml')
patched_load_yaml.assert_called_once_with('.docker-make.yml')
patched_label_template_args.assert_called_once_with([])
patched_tag_template_args.assert_called_once_with([])

0 comments on commit de68bf5

Please sign in to comment.