mirror of
https://github.com/containers/podman.git
synced 2025-07-02 00:30:00 +08:00
Add support for BuildImage
Signed-off-by: Jhon Honce <jhonce@redhat.com> Closes: #908 Approved by: baude
This commit is contained in:
@ -38,7 +38,7 @@ class Client(object):
|
|||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _podman(uri, interface):
|
def _podman(uri, interface):
|
||||||
"""Context manager for API children to access varlink."""
|
"""Context manager for API workers to access varlink."""
|
||||||
client = VarlinkClient(address=uri)
|
client = VarlinkClient(address=uri)
|
||||||
try:
|
try:
|
||||||
iface = client.open(interface)
|
iface = client.open(interface)
|
||||||
|
@ -30,23 +30,18 @@ class Image(collections.UserDict):
|
|||||||
return super().__getitem__(key)
|
return super().__getitem__(key)
|
||||||
|
|
||||||
def _split_token(self, values=None, sep='='):
|
def _split_token(self, values=None, sep='='):
|
||||||
mapped = {}
|
return dict([v.split(sep, 1) for v in values if values])
|
||||||
if values:
|
|
||||||
for var in values:
|
|
||||||
k, v = var.split(sep, 1)
|
|
||||||
mapped[k] = v
|
|
||||||
return mapped
|
|
||||||
|
|
||||||
def create(self, *args, **kwargs):
|
def create(self, *args, **kwargs):
|
||||||
"""Create container from image.
|
"""Create container from image.
|
||||||
|
|
||||||
Pulls defaults from image.inspect()
|
Pulls defaults from image.inspect()
|
||||||
"""
|
"""
|
||||||
# Inialize config from parameters
|
|
||||||
with self._client() as podman:
|
with self._client() as podman:
|
||||||
details = self.inspect()
|
details = self.inspect()
|
||||||
|
|
||||||
# TODO: remove network settings once defaults implemented in service
|
# TODO: remove network settings once defaults implemented in service
|
||||||
|
# Inialize config from parameters, then add image information
|
||||||
config = Config(image_id=self.id, **kwargs)
|
config = Config(image_id=self.id, **kwargs)
|
||||||
config['command'] = details.containerconfig['cmd']
|
config['command'] = details.containerconfig['cmd']
|
||||||
config['env'] = self._split_token(details.containerconfig['env'])
|
config['env'] = self._split_token(details.containerconfig['env'])
|
||||||
@ -75,9 +70,8 @@ class Image(collections.UserDict):
|
|||||||
for r in podman.HistoryImage(self.id)['history']:
|
for r in podman.HistoryImage(self.id)['history']:
|
||||||
yield collections.namedtuple('HistoryDetail', r.keys())(**r)
|
yield collections.namedtuple('HistoryDetail', r.keys())(**r)
|
||||||
|
|
||||||
|
# Convert all keys to lowercase.
|
||||||
def _lower_hook(self):
|
def _lower_hook(self):
|
||||||
"""Convert all keys to lowercase."""
|
|
||||||
|
|
||||||
@functools.wraps(self._lower_hook)
|
@functools.wraps(self._lower_hook)
|
||||||
def wrapped(input):
|
def wrapped(input):
|
||||||
return {k.lower(): v for (k, v) in input.items()}
|
return {k.lower(): v for (k, v) in input.items()}
|
||||||
@ -127,14 +121,26 @@ class Images(object):
|
|||||||
for img in results['images']:
|
for img in results['images']:
|
||||||
yield Image(self._client, img['id'], img)
|
yield Image(self._client, img['id'], img)
|
||||||
|
|
||||||
def build(self, *args, **kwargs):
|
def build(self, dockerfile=None, tags=None, **kwargs):
|
||||||
"""Build container from image.
|
"""Build container from image.
|
||||||
|
|
||||||
See podman-build.1.md for kwargs details.
|
See podman-build.1.md for kwargs details.
|
||||||
"""
|
"""
|
||||||
|
if dockerfile is None:
|
||||||
|
raise ValueError('"dockerfile" is a required argument.')
|
||||||
|
elif not hasattr(dockerfile, '__iter__'):
|
||||||
|
raise ValueError('"dockerfile" is required to be an iter.')
|
||||||
|
|
||||||
|
if tags is None:
|
||||||
|
raise ValueError('"tags" is a required argument.')
|
||||||
|
elif not hasattr(tags, '__iter__'):
|
||||||
|
raise ValueError('"tags" is required to be an iter.')
|
||||||
|
|
||||||
|
config = Config(dockerfile=dockerfile, tags=tags, **kwargs)
|
||||||
with self._client() as podman:
|
with self._client() as podman:
|
||||||
# TODO: Need arguments
|
result = podman.BuildImage(config)
|
||||||
podman.BuildImage()
|
return self.get(result['image']['id']), \
|
||||||
|
(line for line in result['image']['logs'])
|
||||||
|
|
||||||
def delete_unused(self):
|
def delete_unused(self):
|
||||||
"""Delete Images not associated with a container."""
|
"""Delete Images not associated with a container."""
|
||||||
@ -163,4 +169,6 @@ class Images(object):
|
|||||||
|
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""Get Image from id."""
|
"""Get Image from id."""
|
||||||
return next((i for i in self.list() if i.id == id), None)
|
with self._client() as podman:
|
||||||
|
result = podman.GetImage(id)
|
||||||
|
return Image(self._client, result['image']['id'], result['image'])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
from datetime import datetime, timezone
|
||||||
from test.podman_testcase import PodmanTestCase
|
from test.podman_testcase import PodmanTestCase
|
||||||
|
|
||||||
import podman
|
import podman
|
||||||
@ -34,6 +35,7 @@ class TestImages(PodmanTestCase):
|
|||||||
i for i in self.images
|
i for i in self.images
|
||||||
if 'docker.io/library/alpine:latest' in i['repoTags']
|
if 'docker.io/library/alpine:latest' in i['repoTags']
|
||||||
] or []), None)
|
] or []), None)
|
||||||
|
|
||||||
return self.images
|
return self.images
|
||||||
|
|
||||||
def test_list(self):
|
def test_list(self):
|
||||||
@ -42,15 +44,29 @@ class TestImages(PodmanTestCase):
|
|||||||
self.assertIsNotNone(self.alpine_image)
|
self.assertIsNotNone(self.alpine_image)
|
||||||
|
|
||||||
def test_build(self):
|
def test_build(self):
|
||||||
with self.assertRaisesNotImplemented():
|
path = os.path.join(self.tmpdir, 'ctnr', 'Dockerfile')
|
||||||
self.pclient.images.build()
|
img, logs = self.pclient.images.build(
|
||||||
|
dockerfile=[path],
|
||||||
|
tags=['alpine-unittest'],
|
||||||
|
)
|
||||||
|
self.assertIsNotNone(img)
|
||||||
|
self.assertIn('localhost/alpine-unittest:latest', img.repoTags)
|
||||||
|
self.assertLess(
|
||||||
|
podman.datetime_parse(img.created), datetime.now(timezone.utc))
|
||||||
|
self.assertTrue(logs)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
|
img_details = self.alpine_image.inspect()
|
||||||
|
|
||||||
actual = self.alpine_image.container()
|
actual = self.alpine_image.container()
|
||||||
self.assertIsNotNone(actual)
|
self.assertIsNotNone(actual)
|
||||||
self.assertEqual(actual.status, 'configured')
|
self.assertEqual(actual.status, 'configured')
|
||||||
cntr = actual.start()
|
ctnr = actual.start()
|
||||||
self.assertIn(cntr.status, ['running', 'exited'])
|
self.assertIn(ctnr.status, ['running', 'exited'])
|
||||||
|
|
||||||
|
ctnr_details = ctnr.inspect()
|
||||||
|
for e in img_details.containerconfig['env']:
|
||||||
|
self.assertIn(e, ctnr_details.config['env'])
|
||||||
|
|
||||||
def test_export(self):
|
def test_export(self):
|
||||||
path = os.path.join(self.tmpdir, 'alpine_export.tar')
|
path = os.path.join(self.tmpdir, 'alpine_export.tar')
|
||||||
@ -60,6 +76,10 @@ class TestImages(PodmanTestCase):
|
|||||||
self.assertTrue(actual)
|
self.assertTrue(actual)
|
||||||
self.assertTrue(os.path.isfile(path))
|
self.assertTrue(os.path.isfile(path))
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
actual = self.pclient.images.get(self.alpine_image.id)
|
||||||
|
self.assertEqual(actual, self.alpine_image)
|
||||||
|
|
||||||
def test_history(self):
|
def test_history(self):
|
||||||
for count, record in enumerate(self.alpine_image.history()):
|
for count, record in enumerate(self.alpine_image.history()):
|
||||||
self.assertEqual(record.id, self.alpine_image.id)
|
self.assertEqual(record.id, self.alpine_image.id)
|
||||||
@ -102,8 +122,11 @@ class TestImages(PodmanTestCase):
|
|||||||
before = self.loadCache()
|
before = self.loadCache()
|
||||||
# create unused image, so we have something to delete
|
# create unused image, so we have something to delete
|
||||||
source = os.path.join(self.tmpdir, 'alpine_gold.tar')
|
source = os.path.join(self.tmpdir, 'alpine_gold.tar')
|
||||||
new_img = self.pclient.images.import_image(source, 'alpine2:latest',
|
new_img = self.pclient.images.import_image(
|
||||||
'unittest.test_import')
|
source,
|
||||||
|
'alpine2:latest',
|
||||||
|
'unittest.test_import',
|
||||||
|
)
|
||||||
after = self.loadCache()
|
after = self.loadCache()
|
||||||
|
|
||||||
self.assertEqual(len(before) + 1, len(after))
|
self.assertEqual(len(before) + 1, len(after))
|
||||||
|
@ -6,6 +6,13 @@ if [[ $(id -u) != 0 ]]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# setup path to find new binaries _NOT_ system binaries
|
||||||
|
if [[ ! -x ../../bin/podman ]]; then
|
||||||
|
echo 1>&2 Cannot find podman binary from libpod root directory. Run \"make binaries\"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export PATH=../../bin:$PATH
|
||||||
|
|
||||||
while getopts "vh" arg; do
|
while getopts "vh" arg; do
|
||||||
case $arg in
|
case $arg in
|
||||||
v ) VERBOSE='-v' ;;
|
v ) VERBOSE='-v' ;;
|
||||||
@ -14,8 +21,16 @@ while getopts "vh" arg; do
|
|||||||
done
|
done
|
||||||
shift $((OPTIND-1))
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
# aggressive cleanup as tests may crash leaving crap around
|
||||||
|
umount '^(shm|nsfs)'
|
||||||
|
umount '\/run\/netns'
|
||||||
|
rm -r "$1"
|
||||||
|
}
|
||||||
|
|
||||||
# Create temporary directory for storage
|
# Create temporary directory for storage
|
||||||
export TMPDIR=`mktemp -d /tmp/podman.XXXXXXXXXX`
|
export TMPDIR=`mktemp -d /tmp/podman.XXXXXXXXXX`
|
||||||
|
trap "cleanup $TMPDIR" EXIT
|
||||||
|
|
||||||
function umount {
|
function umount {
|
||||||
# xargs -r always ran once, so write any mount points to file first
|
# xargs -r always ran once, so write any mount points to file first
|
||||||
@ -25,26 +40,16 @@ function umount {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup {
|
|
||||||
umount '^(shm|nsfs)'
|
|
||||||
umount '\/run\/netns'
|
|
||||||
rm -fr ${TMPDIR}
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
# setup path to find new binaries _NOT_ system binaries
|
|
||||||
if [[ ! -x ../../bin/podman ]]; then
|
|
||||||
echo 1>&2 Cannot find podman binary from libpod root directory, Or, run \"make binaries\"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
export PATH=../../bin:$PATH
|
|
||||||
|
|
||||||
function showlog {
|
function showlog {
|
||||||
[ -s "$1" ] && (echo $1 =====; cat "$1"; echo)
|
[[ -s $1 ]] && cat <<-EOT
|
||||||
|
$1 =====
|
||||||
|
$(cat "$1")
|
||||||
|
|
||||||
|
EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
# Need a location to store the podman socket
|
# Need locations to store stuff
|
||||||
mkdir -p ${TMPDIR}/{podman,crio,crio-run,cni/net.d}
|
mkdir -p ${TMPDIR}/{podman,crio,crio-run,cni/net.d,ctnr}
|
||||||
|
|
||||||
# Cannot be done in python unittest fixtures. EnvVar not picked up.
|
# Cannot be done in python unittest fixtures. EnvVar not picked up.
|
||||||
export REGISTRIES_CONFIG_PATH=${TMPDIR}/registry.conf
|
export REGISTRIES_CONFIG_PATH=${TMPDIR}/registry.conf
|
||||||
@ -85,6 +90,17 @@ cat >$CNI_CONFIG_PATH/87-podman-bridge.conflist <<-EOT
|
|||||||
}
|
}
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
|
cat >$TMPDIR/ctnr/hello.sh <<-EOT
|
||||||
|
echo 'Hello, World'
|
||||||
|
EOT
|
||||||
|
|
||||||
|
cat >$TMPDIR/ctnr/Dockerfile <<-EOT
|
||||||
|
FROM alpine:latest
|
||||||
|
COPY ./hello.sh /tmp/hello.sh
|
||||||
|
RUN chmod 755 /tmp/hello.sh
|
||||||
|
ENTRYPOINT ["/tmp/hello.sh"]
|
||||||
|
EOT
|
||||||
|
|
||||||
export PODMAN_HOST="unix:${TMPDIR}/podman/io.projectatomic.podman"
|
export PODMAN_HOST="unix:${TMPDIR}/podman/io.projectatomic.podman"
|
||||||
PODMAN_ARGS="--storage-driver=vfs\
|
PODMAN_ARGS="--storage-driver=vfs\
|
||||||
--root=${TMPDIR}/crio\
|
--root=${TMPDIR}/crio\
|
||||||
|
Reference in New Issue
Block a user