mirror of https://github.com/docker/docker-py.git
Add create_plugin implementation
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
cd05d8d53d
commit
e1ad3186ef
|
@ -5,3 +5,4 @@ include README.rst
|
|||
include LICENSE
|
||||
recursive-include tests *.py
|
||||
recursive-include tests/unit/testdata *
|
||||
recursive-include tests/integration/testdata *
|
||||
|
|
|
@ -26,21 +26,28 @@ class PluginApiMixin(object):
|
|||
self._raise_for_status(res)
|
||||
return True
|
||||
|
||||
def create_plugin(self, name, rootfs, manifest):
|
||||
@utils.minimum_version('1.25')
|
||||
def create_plugin(self, name, plugin_data_dir, gzip=False):
|
||||
"""
|
||||
Create a new plugin.
|
||||
|
||||
Args:
|
||||
name (string): The name of the plugin. The ``:latest`` tag is
|
||||
optional, and is the default if omitted.
|
||||
rootfs (string): Path to the plugin's ``rootfs``
|
||||
manifest (string): Path to the plugin's manifest file
|
||||
plugin_data_dir (string): Path to the plugin data directory.
|
||||
Plugin data directory must contain the ``config.json``
|
||||
manifest file and the ``rootfs`` directory.
|
||||
gzip (bool): Compress the context using gzip. Default: False
|
||||
|
||||
Returns:
|
||||
``True`` if successful
|
||||
"""
|
||||
# FIXME: Needs implementation
|
||||
raise NotImplementedError()
|
||||
url = self._url('/plugins/create')
|
||||
|
||||
with utils.create_archive(root=plugin_data_dir, gzip=gzip) as archv:
|
||||
res = self._post(url, params={'name': name}, data=archv)
|
||||
self._raise_for_status(res)
|
||||
return True
|
||||
|
||||
@utils.minimum_version('1.25')
|
||||
def disable_plugin(self, name):
|
||||
|
|
|
@ -100,20 +100,22 @@ class Plugin(Model):
|
|||
class PluginCollection(Collection):
|
||||
model = Plugin
|
||||
|
||||
def create(self, name, rootfs, manifest):
|
||||
def create(self, name, plugin_data_dir, gzip=False):
|
||||
"""
|
||||
Create a new plugin.
|
||||
|
||||
Args:
|
||||
name (string): The name of the plugin. The ``:latest`` tag is
|
||||
optional, and is the default if omitted.
|
||||
rootfs (string): Path to the plugin's ``rootfs``
|
||||
manifest (string): Path to the plugin's manifest file
|
||||
plugin_data_dir (string): Path to the plugin data directory.
|
||||
Plugin data directory must contain the ``config.json``
|
||||
manifest file and the ``rootfs`` directory.
|
||||
gzip (bool): Compress the context using gzip. Default: False
|
||||
|
||||
Returns:
|
||||
(:py:class:`Plugin`): The newly created plugin.
|
||||
"""
|
||||
self.client.api.create_plugin(name, rootfs, manifest)
|
||||
self.client.api.create_plugin(name, plugin_data_dir, gzip)
|
||||
return self.get(name)
|
||||
|
||||
def get(self, name):
|
||||
|
|
|
@ -6,7 +6,7 @@ from .utils import (
|
|||
create_host_config, parse_bytes, ping_registry, parse_env_file, version_lt,
|
||||
version_gte, decode_json_header, split_command, create_ipam_config,
|
||||
create_ipam_pool, parse_devices, normalize_links, convert_service_networks,
|
||||
format_environment
|
||||
format_environment, create_archive
|
||||
)
|
||||
|
||||
from .decorators import check_resource, minimum_version, update_headers
|
||||
|
|
|
@ -80,16 +80,35 @@ def decode_json_header(header):
|
|||
|
||||
|
||||
def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
|
||||
if not fileobj:
|
||||
fileobj = tempfile.NamedTemporaryFile()
|
||||
t = tarfile.open(mode='w:gz' if gzip else 'w', fileobj=fileobj)
|
||||
|
||||
root = os.path.abspath(path)
|
||||
exclude = exclude or []
|
||||
|
||||
for path in sorted(exclude_paths(root, exclude, dockerfile=dockerfile)):
|
||||
i = t.gettarinfo(os.path.join(root, path), arcname=path)
|
||||
return create_archive(
|
||||
files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),
|
||||
root=root, fileobj=fileobj, gzip=gzip
|
||||
)
|
||||
|
||||
|
||||
def build_file_list(root):
|
||||
files = []
|
||||
for dirname, dirnames, fnames in os.walk(root):
|
||||
for filename in fnames + dirnames:
|
||||
longpath = os.path.join(dirname, filename)
|
||||
files.append(
|
||||
longpath.replace(root, '', 1).lstrip('/')
|
||||
)
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def create_archive(root, files=None, fileobj=None, gzip=False):
|
||||
if not fileobj:
|
||||
fileobj = tempfile.NamedTemporaryFile()
|
||||
t = tarfile.open(mode='w:gz' if gzip else 'w', fileobj=fileobj)
|
||||
if files is None:
|
||||
files = build_file_list(root)
|
||||
for path in files:
|
||||
i = t.gettarinfo(os.path.join(root, path), arcname=path)
|
||||
if i is None:
|
||||
# This happens when we encounter a socket file. We can safely
|
||||
# ignore it and proceed.
|
||||
|
@ -102,13 +121,11 @@ def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
|
|||
|
||||
try:
|
||||
# We open the file object in binary mode for Windows support.
|
||||
f = open(os.path.join(root, path), 'rb')
|
||||
with open(os.path.join(root, path), 'rb') as f:
|
||||
t.addfile(i, f)
|
||||
except IOError:
|
||||
# When we encounter a directory the file object is set to None.
|
||||
f = None
|
||||
|
||||
t.addfile(i, f)
|
||||
|
||||
t.addfile(i, None)
|
||||
t.close()
|
||||
fileobj.seek(0)
|
||||
return fileobj
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import os
|
||||
|
||||
import docker
|
||||
import pytest
|
||||
|
||||
from .base import BaseAPIIntegrationTest, TEST_API_VERSION
|
||||
from ..helpers import requires_api_version
|
||||
|
||||
SSHFS = 'vieux/sshfs:latest'
|
||||
|
||||
|
||||
@requires_api_version('1.25')
|
||||
class PluginTest(BaseAPIIntegrationTest):
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
|
@ -24,6 +28,12 @@ class PluginTest(BaseAPIIntegrationTest):
|
|||
except docker.errors.APIError:
|
||||
pass
|
||||
|
||||
for p in self.tmp_plugins:
|
||||
try:
|
||||
self.client.remove_plugin(p, force=True)
|
||||
except docker.errors.APIError:
|
||||
pass
|
||||
|
||||
def ensure_plugin_installed(self, plugin_name):
|
||||
try:
|
||||
return self.client.inspect_plugin(plugin_name)
|
||||
|
@ -112,3 +122,14 @@ class PluginTest(BaseAPIIntegrationTest):
|
|||
assert filter(lambda x: x['status'] == 'Download complete', logs)
|
||||
assert self.client.inspect_plugin(SSHFS)
|
||||
assert self.client.enable_plugin(SSHFS)
|
||||
|
||||
def test_create_plugin(self):
|
||||
plugin_data_dir = os.path.join(
|
||||
os.path.dirname(__file__), 'testdata/dummy-plugin'
|
||||
)
|
||||
assert self.client.create_plugin(
|
||||
'docker-sdk-py/dummy', plugin_data_dir
|
||||
)
|
||||
self.tmp_plugins.append('docker-sdk-py/dummy')
|
||||
data = self.client.inspect_plugin('docker-sdk-py/dummy')
|
||||
assert data['Config']['Entrypoint'] == ['/dummy']
|
||||
|
|
|
@ -27,6 +27,7 @@ class BaseIntegrationTest(unittest.TestCase):
|
|||
self.tmp_folders = []
|
||||
self.tmp_volumes = []
|
||||
self.tmp_networks = []
|
||||
self.tmp_plugins = []
|
||||
|
||||
def tearDown(self):
|
||||
client = docker.from_env(version=TEST_API_VERSION)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "Dummy test plugin for docker python SDK",
|
||||
"documentation": "https://github.com/docker/docker-py",
|
||||
"entrypoint": ["/dummy"],
|
||||
"network": {
|
||||
"type": "host"
|
||||
},
|
||||
"interface" : {
|
||||
"types": ["docker.volumedriver/1.0"],
|
||||
"socket": "dummy.sock"
|
||||
},
|
||||
"env": [
|
||||
{
|
||||
"name":"DEBUG",
|
||||
"settable":["value"],
|
||||
"value":"0"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue