Kaydet (Commit) e1ad3186 authored tarafından Joffrey F's avatar Joffrey F

Add create_plugin implementation

Signed-off-by: 's avatarJoffrey F <joffrey@docker.com>
üst cd05d8d5
......@@ -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
......
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)
......
{
"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"
}
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment