Skip to content

add the docker specific arg primitive. #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,39 @@ __Examples__
environment(variables={'PATH': '/usr/local/bin:$PATH'})
```

# arg
```python
arg(self, **kwargs)
```

The `arg` primitive sets the corresponding environment
variables during the build time of a docker container.
Singularity and "bash" containers does not have a strict version of the
ARG keyword found on Dockerfiles but is possible to simulate
the behavior of this keyword as a build time parameter for the
Singularity and bash containers using environment variables.

- __variables__: A dictionary of key / value pairs. The default is an
empty dictionary.

__Examples__

```python
arg(variables={'HTTP_PROXY':'proxy.example.com', 'NO_PROXY':'example.com'})
```

```bash
SINGULARITYENV_HTTP_PROXY="proxy.example.com" \
SINGULARITYENV_NO_PROXY="example.com \
singularity build image.sif recipe.def"
```

```bash
HTTP_PROXY="proxy.example.com" \
NO_PROXY="example.com \
recipe.sh
```

# label
```python
label(self, **kwargs)
Expand Down
3 changes: 2 additions & 1 deletion hpccm/primitives/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from __future__ import absolute_import

__all__ = ['baseimage', 'blob', 'comment', 'copy', 'environment', 'label',
__all__ = ['arg', 'baseimage', 'blob', 'comment', 'copy', 'environment', 'label',
'raw', 'runscript', 'shell', 'user', 'workdir']

from hpccm.primitives.baseimage import baseimage
Expand All @@ -28,3 +28,4 @@
from hpccm.primitives.shell import shell
from hpccm.primitives.user import user
from hpccm.primitives.workdir import workdir
from hpccm.primitives.arg import arg
93 changes: 93 additions & 0 deletions hpccm/primitives/arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=invalid-name, too-few-public-methods

"""Arg primitive"""

from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function

import logging # pylint: disable=unused-import

import hpccm.config

from hpccm.common import container_type

class arg(object):
"""The `arg` primitive sets the corresponding environment
variables during the build time of a docker container.
Singularity and "bash" containers does not have a strict version of the
ARG keyword found on Dockerfiles but is possible to simulate
the behavior of this keyword as a build time parameter for the
Singularity and bash containers using environment variables.

# Parameters

variables: A dictionary of key / value pairs. The default is an
empty dictionary.

# Examples

```python
arg(variables={'HTTP_PROXY':'proxy.example.com', 'NO_PROXY':'example.com'})

```bash
SINGULARITYENV_HTTP_PROXY="proxy.example.com" \
SINGULARITYENV_NO_PROXY="example.com \
singularity build image.sif recipe.def"
```

```bash
HTTP_PROXY="proxy.example.com" \
NO_PROXY="example.com \
recipe.sh"
```

"""
def __init__(self, **kwargs):
"""Initialize primitive"""
self.__variables = kwargs.get('variables', {})

def __str__(self):
"""String representation of the primitive"""
if self.__variables:
string = ""
num_vars = len(self.__variables)
variables = self.__variables
if hpccm.config.g_ctype == container_type.SINGULARITY:
if num_vars > 0:
string += "%post" + "\n"
for count, (key, val) in enumerate(sorted(variables.items())):
eol = "" if count == num_vars - 1 else "\n"
string += ' {0}=${{{0}:-"{1}"}}'.format(key, val) + eol
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the bash and Singularity args need to be exported? (I'm not sure)

return string
elif hpccm.config.g_ctype == container_type.BASH:
for count, (key, val) in enumerate(sorted(variables.items())):
eol = "" if count == num_vars - 1 else "\n"
string += '{0}=${{{0}:-"{1}"}}'.format(key, val) + eol
return string
elif hpccm.config.g_ctype == container_type.DOCKER:
for count, (key, val) in enumerate(sorted(variables.items())):
eol = "" if count == num_vars - 1 else "\n"
if val == "":
string += 'ARG {0}'.format(key) + eol
else:
string += 'ARG {0}={1}'.format(key, val) + eol
return string
else:
raise RuntimeError('Unknown container type')
else:
return ''
137 changes: 137 additions & 0 deletions test/test_arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=invalid-name, too-few-public-methods, bad-continuation

"""Test cases for the arg module"""

from __future__ import unicode_literals
from __future__ import print_function

import logging # pylint: disable=unused-import
import unittest

from helpers import bash, docker, invalid_ctype, singularity

from hpccm.primitives.arg import arg

class Test_arg(unittest.TestCase):
def setUp(self):
"""Disable logging output messages"""
logging.disable(logging.ERROR)

@docker
def test_empty(self):
"""No arg specified"""
e = arg()
self.assertEqual(str(e), '')

@invalid_ctype
def test_invalid_ctype(self):
"""Invalid container type specified"""
e = arg(variables={'A': 'B'})
with self.assertRaises(RuntimeError):
str(e)

@docker
def test_single_docker(self):
"""Single arg variable specified"""
e = arg(variables={'A': 'B'})
self.assertEqual(str(e), 'ARG A=B')

@docker
def test_single_docker_nodefault(self):
"""Single arg variable specified (no default value)"""
e = arg(variables={'A': ''})
self.assertEqual(str(e), 'ARG A')

@singularity
def test_single_singularity(self):
"""Single arg variable specified"""
e = arg(variables={'A': 'B'})
self.assertEqual(str(e), '%post\n A=${A:-"B"}')

@singularity
def test_single_singularity_nodefault(self):
"""Single arg variable specified"""
e = arg(variables={'A': ''})
self.assertEqual(str(e), '%post\n A=${A:-""}')

@bash
def test_single_bash(self):
"""Single arg variable specified"""
e = arg(variables={'A': 'B'})
self.assertEqual(str(e), 'A=${A:-"B"}')

@bash
def test_single_bash_nodefault(self):
"""Single arg variable specified"""
e = arg(variables={'A': ''})
self.assertEqual(str(e), 'A=${A:-""}')

@docker
def test_multiple_docker(self):
"""Multiple arg variables specified"""
e = arg(variables={'ONE': 1, 'TWO': 2, 'THREE': 3})
self.assertEqual(str(e),
'''ARG ONE=1
ARG THREE=3
ARG TWO=2''')

@docker
def test_multiple_docker_nodefault(self):
"""Multiple arg variables specified (no default value)"""
e = arg(variables={'ONE': '', 'TWO': '', 'THREE': ''})
self.assertEqual(str(e),
'''ARG ONE
ARG THREE
ARG TWO''')

@singularity
def test_multiple_singularity(self):
"""Multiple arg variables specified"""
e = arg(variables={'ONE': 1, 'TWO': 2, 'THREE': 3})
self.assertEqual(str(e),
'''%post
ONE=${ONE:-"1"}
THREE=${THREE:-"3"}
TWO=${TWO:-"2"}''')

@singularity
def test_multiple_singularity_nodefault(self):
"""Multiple arg variables specified"""
e = arg(variables={'ONE':"", 'TWO':"", 'THREE':""})
self.assertEqual(str(e),
'''%post
ONE=${ONE:-""}
THREE=${THREE:-""}
TWO=${TWO:-""}''')

@bash
def test_multiple_bash(self):
"""Multiple arg variables specified"""
e = arg(variables={'ONE': 1, 'TWO': 2, 'THREE': 3})
self.assertEqual(str(e),
'''ONE=${ONE:-"1"}
THREE=${THREE:-"3"}
TWO=${TWO:-"2"}''')

@bash
def test_multiple_bash_nodefault(self):
"""Multiple arg variables specified"""
e = arg(variables={'ONE': "", 'TWO': "", 'THREE': ""})
self.assertEqual(str(e),
'''ONE=${ONE:-""}
THREE=${THREE:-""}
TWO=${TWO:-""}''')