File Downloable Feature

It involves the possibility of having File URL in execution plans. These
URLs will not be downloaded in murano, but in murano-agent.
This feature is related to the blueprint
https://blueprints.launchpad.net/murano/+spec/conf-language-support
Change-Id: I01f32806f61ba424135ddb26ef87d7a046d93cf9
This commit is contained in:
Henar Muñoz Frutos 2015-02-02 11:51:16 +01:00
parent 42c320a085
commit 86bec233c5
7 changed files with 340 additions and 14 deletions

View File

@ -18,6 +18,7 @@ import datetime
import logging
import os
import types
import urlparse
import uuid
import eventlet.event
@ -228,31 +229,79 @@ class Agent(murano_object.MuranoObject):
files = {}
for file_id, file_descr in template['Files'].items():
files[file_descr['Name']] = file_id
for name, script in template.get('Scripts', {}).items():
if 'EntryPoint' not in script:
raise ValueError('No entry point in script ' + name)
script['EntryPoint'] = self._place_file(
scripts_folder, script['EntryPoint'],
template, files, resources)
if 'Files' in script:
for i in range(0, len(script['Files'])):
script['Files'][i] = self._place_file(
scripts_folder, script['Files'][i],
template, files, resources)
if 'Application' in script['Type']:
script['EntryPoint'] = self._place_file(
scripts_folder, script['EntryPoint'],
template, files, resources)
scripts_files = script['Files']
script['Files'] = []
for file in scripts_files:
file_id = self._place_file(scripts_folder, file,
template, files, resources)
if self._is_url(file):
script['Files'].append(file)
else:
script['Files'].append(file_id)
return template
def _place_file(self, folder, name, template, files, resources):
def _is_url(self, file):
file = self._get_url(file)
parts = urlparse.urlsplit(file)
if not parts.scheme or not parts.netloc:
return False
else:
return True
def _get_url(self, file):
if isinstance(file, dict):
return file.values()[0]
else:
return file
def _get_name(self, file):
if isinstance(file, dict):
name = file.keys()[0]
else:
name = file
if self._is_url(name):
name = name[name.rindex('/') + 1:len(name)]
return name
def _get_file(self, file):
if isinstance(file, dict):
return file.values()[0]
else:
return file
def _place_file(self, folder, file, template, files, resources):
name = self._get_name(file)
file = self._get_file(file)
file_id = uuid.uuid4().hex
if self._is_url(file):
template['Files'][file_id] = {
'Name': str(name),
'URL': file,
'Type': 'Downloadable'}
return file_id
use_base64 = False
if name.startswith('<') and name.endswith('>'):
if file.startswith('<') and file.endswith('>'):
use_base64 = True
name = name[1:len(name) - 1]
if name in files:
if '<' in name:
name = file[1:len(file) - 1]
if file in files:
return files[name]
file_id = uuid.uuid4().hex
body_type = 'Base64' if use_base64 else 'Text'
body = resources.string(os.path.join(folder, name))
body = resources.string(os.path.join(folder, file))
if use_base64:
body = body.encode('base64')

View File

View File

@ -0,0 +1,21 @@
FormatVersion: 2.0.0
Version: 1.0.0
Name: Deploy Telnet
Parameters:
appName: $appName
Body: |
return deploy(args.appName).stdout
Scripts:
deploy:
Type: Application
Version: 1.0.0
EntryPoint: deployTelnet.sh
Files:
- installer.sh
- common.sh
Options:
captureStdout: true
captureStderr: true

View File

@ -0,0 +1,21 @@
FormatVersion: 2.0.0
Version: 1.0.0
Name: Deploy Tomcat
Parameters:
appName: $appName
Body: |
return deploy(args.appName).stdout
Scripts:
deploy:
Type: Application
Version: 1.0.0
EntryPoint: deployTomcat.sh
Files:
- installer: <installer.sh>
- <common.sh>
Options:
captureStdout: true
captureStderr: true

View File

@ -0,0 +1,21 @@
FormatVersion: 2.0.0
Version: 1.0.0
Name: Deploy Chef
Parameters:
appName: $appName
Body: |
return deploy(args.appName).stdout
Scripts:
deploy:
Type: Chef
Version: 1.0.0
EntryPoint: cookbook/recipe
Files:
- https://github.com/tomcat.git
- java: https://github.com/java.git
Options:
captureStdout: true
captureStderr: true

View File

@ -0,0 +1,214 @@
# Copyright (c) 2015 Telefonica I+D
#
# 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.
import mock
import os
import yaml as yamllib
from murano.dsl import murano_class
from murano.dsl import object_store
import murano.engine.system.agent as agent
import murano.engine.system.resource_manager as resource
from murano.tests.unit import base
class TestExecutionPlan(base.MuranoTestCase):
def setUp(self):
super(TestExecutionPlan, self).setUp()
if hasattr(yamllib, 'CSafeLoader'):
self.yaml_loader = yamllib.CSafeLoader
else:
self.yaml_loader = yamllib.SafeLoader
self.mock_murano_class = mock.Mock(spec=murano_class.MuranoClass)
self.mock_murano_class.name = 'io.murano.system.Agent'
self.mock_murano_class.parents = []
self.mock_object_store = mock.Mock(spec=object_store.ObjectStore)
self.agent = agent.Agent(self.mock_murano_class, None,
self.mock_object_store, None)
self.resources = mock.Mock(spec=resource.ResourceManager)
self.resources.string.return_value = 'text'
self.uuids = ['ID1', 'ID2', 'ID3', 'ID4']
self.mock_uuid = self._stub_uuid(self.uuids)
self.addCleanup(mock.patch.stopall)
def _read(self, path):
execution_plan_dir = os.path.abspath(
os.path.join(__file__, '../execution_plans/')
)
with open(execution_plan_dir + "/" + path) as file:
return file.read()
def test_execution_plan_v2_application_type(self):
template = yamllib.load(
self._read('application.template'),
Loader=self.yaml_loader)
template = self.agent.buildExecutionPlan(template, self.resources)
self.assertEqual(template, self._get_application())
def test_execution_plan_v2_chef_type(self):
template = yamllib.load(
self._read('chef.template'),
Loader=self.yaml_loader)
template = self.agent.buildExecutionPlan(template, self.resources)
self.assertEqual(template, self._get_chef())
def test_execution_plan_v3_telnet_application(self):
template = yamllib.load(
self._read('DeployTelnet.template'),
Loader=self.yaml_loader)
template = self.agent.buildExecutionPlan(template, self.resources)
self.assertEqual(template, self._get_telnet_application())
def _get_application(self):
return {
'Action': 'Execute',
'Body': 'return deploy(args.appName).stdout\n',
'Files': {
self.uuids[1]: {
'Body': 'text',
'BodyType': 'Text',
'Name': 'deployTomcat.sh'
},
self.uuids[2]: {
'Body': 'dGV4dA==\n',
'BodyType': 'Base64',
'Name': 'installer'
},
self.uuids[3]: {
'Body': 'dGV4dA==\n',
'BodyType': 'Base64',
'Name': 'common.sh'
}
},
'FormatVersion': '2.0.0',
'ID': self.uuids[0],
'Name': 'Deploy Tomcat',
'Parameters': {
'appName': '$appName'
},
'Scripts': {
'deploy': {
'EntryPoint': self.uuids[1],
'Files': [
self.uuids[2],
self.uuids[3]
],
'Options': {
'captureStderr': True,
'captureStdout': True
},
'Type': 'Application',
'Version': '1.0.0'
}
},
'Version': '1.0.0'
}
def _get_chef(self):
return {
'Action': 'Execute',
'Body': 'return deploy(args.appName).stdout\n',
'Files': {
self.uuids[1]: {
'Name': 'tomcat.git',
'Type': 'Downloadable',
'URL': 'https://github.com/tomcat.git'
},
self.uuids[2]: {
'Name': 'java',
'Type': 'Downloadable',
'URL': 'https://github.com/java.git'
},
},
'FormatVersion': '2.0.0',
'ID': self.uuids[0],
'Name': 'Deploy Chef',
'Parameters': {
'appName': '$appName'
},
'Scripts': {
'deploy': {
'EntryPoint': 'cookbook/recipe',
'Files': [
'https://github.com/tomcat.git',
{'java': 'https://github.com/java.git'}
],
'Options': {
'captureStderr': True,
'captureStdout': True
},
'Type': 'Chef',
'Version': '1.0.0'
}
},
'Version': '1.0.0'
}
def _get_telnet_application(self):
return {
'Action': 'Execute',
'Body': 'return deploy(args.appName).stdout\n',
'Files': {
self.uuids[1]: {
'Body': 'text',
'BodyType': 'Text',
'Name': 'deployTelnet.sh'
},
self.uuids[2]: {
'Body': 'text',
'BodyType': 'Text',
'Name': 'installer.sh'
},
self.uuids[3]: {
'Body': 'text',
'BodyType': 'Text',
'Name': 'common.sh'
}
},
'FormatVersion': '2.0.0',
'ID': self.uuids[0],
'Name': 'Deploy Telnet',
'Parameters': {
'appName': '$appName'
},
'Scripts': {
'deploy': {
'EntryPoint': self.uuids[1],
'Files': [
self.uuids[2],
self.uuids[3]
],
'Options': {
'captureStderr': True,
'captureStdout': True
},
'Type': 'Application',
'Version': '1.0.0'
}
},
'Version': '1.0.0'
}
def _stub_uuid(self, values=[]):
class FakeUUID(object):
def __init__(self, v):
self.hex = v
mock_uuid4 = mock.patch('uuid.uuid4').start()
mock_uuid4.side_effect = [FakeUUID(v) for v in values]
return mock_uuid4