Skip to content
This repository was archived by the owner on Oct 30, 2018. It is now read-only.
Merged
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
19 changes: 19 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,25 @@ Run migration
This command will run the synchronization process between Parse and Syncano. Sit comfortably in your chair and read
the output.

Syncano Hosting
---------------

Syncano Hosting is a simple way to host the static files. CLI supports it in the following way:

::

syncano hosting <instance_name> --list-files

This command will list files in hosting which match the default hosting.

::

syncano hosting <instance_name> --publish <base_dir>

This command will publish all files inside <base_dir> and will publish it to the Syncano Hosting (default one).
The whole directory structure - will be mapped in Syncano Hosting.


Tips & Troubleshooting
----------------------

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
url='https://github.com/Syncano/syncano-cli',
packages=find_packages(),
license='MIT',
install_requires=['syncano>=5.1.0', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'],
install_requires=['syncano>=5.3.0', 'PyYaml>=3.11', 'watchdog>=0.8.3', 'click>=6.6'],
test_suite='tests',
entry_points="""
[console_scripts]
Expand Down
1 change: 1 addition & 0 deletions syncano_cli/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
4 changes: 3 additions & 1 deletion syncano_cli/execute/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

import click
from syncano.exceptions import SyncanoDoesNotExist
from syncano_cli.base.connection import create_connection
from syncano_cli.config import ACCOUNT_CONFIG_PATH
from syncano_cli.logger import get_logger

from .connection import create_connection
from .utils import print_response

LOG = get_logger('syncano-execute')
Expand All @@ -27,6 +28,7 @@ def execute(config, instance_name, script_endpoint_name, payload):
"""
Execute script endpoint in given instance
"""
config = config or ACCOUNT_CONFIG_PATH
try:
connection = create_connection(config)
instance = connection.Instance.please.get(instance_name)
Expand Down
1 change: 1 addition & 0 deletions syncano_cli/hosting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
59 changes: 59 additions & 0 deletions syncano_cli/hosting/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
import os
import sys

import click
from syncano_cli.base.connection import create_connection
from syncano_cli.config import ACCOUNT_CONFIG_PATH
from syncano_cli.hosting.utils import HostingCommands
from syncano_cli.logger import get_logger

LOG = get_logger('syncano-hosting')


@click.group()
def top_hosting():
pass


@top_hosting.command()
@click.option('--config', help=u'Account configuration file.')
@click.argument('instance_name', envvar='SYNCANO_INSTANCE')
@click.option('--list-files', is_flag=True, help='List files within the hosting.')
@click.option('--publish', type=str, help='Publish files from the local directory to the Syncano Hosting.')
def hosting(config, instance_name, list_files, publish):
"""
Execute script endpoint in given instance
"""

def validate_domain(provided_domain=None):
return 'default' if not provided_domain else provided_domain

def validate_publish(base_dir):
if not os.path.isdir(base_dir):
LOG.error('You should provide a project root directory here.')
sys.exit(1)

config = config or ACCOUNT_CONFIG_PATH
try:
connection = create_connection(config)
instance = connection.Instance.please.get(name=instance_name)

hosting_commands = HostingCommands(instance)

if list_files:
domain = validate_domain()
LOG.info('List the hosting files: {} in instance: {}'.format(domain, instance_name))
hosting_files = hosting_commands.list_hosting_files(domain=domain)
hosting_commands.print_hosting_files(hosting_files)

if publish:
domain = validate_domain()
LOG.info('Publish the hosting files: {} in instance: {}'.format(domain, instance_name))
validate_publish(base_dir=publish)
uploaded_files = hosting_commands.publish(domain=domain, base_dir=publish)
hosting_commands.print_hosting_files(uploaded_files)

except Exception as e:
LOG.error(e)
sys.exit(1)
75 changes: 75 additions & 0 deletions syncano_cli/hosting/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
import os
import sys
import time

from syncano_cli.logger import get_logger

LOG = get_logger('syncano-hosting')


class HostingCommands(object):

def __init__(self, instance):
self.instance = instance

def list_hosting(self):
return [
(hosting.label, hosting.domains) for hosting in self.instance.hostings.all()
]

def list_hosting_files(self, domain):
hosting = self._get_hosting(domain=domain)
if not hosting:
LOG.warn('No default hosting found. Exit.')
sys.exit(1)

files_list = hosting.list_files()
return files_list

def publish(self, domain, base_dir):
uploaded_files = []
hosting = self._get_hosting(domain=domain)
if not hosting:
# create a new hosting if no default is present;
hosting = self.create_hosting(label='Default hosting', domain=domain)

for folder, subs, files in os.walk(base_dir):
path = folder.split(base_dir)[1][1:] # skip the /
for single_file in files:
if path:
file_path = '{}/{}'.format(path, single_file)
else:
file_path = single_file

sys_path = os.path.join(folder, single_file)
with open(sys_path, 'rb') as upload_file:
LOG.info('Uploading file: {}'.format(file_path))
hosting.upload_file(path=file_path, file=upload_file)

uploaded_files.append(file_path)
time.sleep(0.02) # avoid throttling;
return uploaded_files

def create_hosting(self, label, domain):
hosting = self.instance.hostings.create(
label=label,
domains=[domain]
)
return hosting

def _get_hosting(self, domain):
hostings = self.instance.hostings.all()
for hosting in hostings:
if domain in hosting.domains:
return hosting

def print_hosting_files(self, hosting_files):
print('Hosting files:')
self._print_separator()
for file_path in hosting_files:
print(file_path)

@staticmethod
def _print_separator():
print(79 * '-')
2 changes: 2 additions & 0 deletions syncano_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from syncano.exceptions import SyncanoException
from syncano_cli.commands import top_level
from syncano_cli.execute.commands import top_execute
from syncano_cli.hosting.commands import top_hosting
from syncano_cli.parse_to_syncano.commands import top_migrate
from syncano_cli.sync.commands import top_sync

Expand All @@ -18,6 +19,7 @@
top_sync,
top_migrate,
top_execute,
top_hosting,
])


Expand Down
4 changes: 4 additions & 0 deletions tests/hosting_files_examples/css/page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

h1 {
color: #124500;
}
9 changes: 9 additions & 0 deletions tests/hosting_files_examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>My first HTML document</title>
</head>
<body>
<P>Hello world!
</body>
</html>
24 changes: 24 additions & 0 deletions tests/test_hosting_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from syncano_cli.main import cli
from tests.base import BaseCLITest


class HostingCommandsTest(BaseCLITest):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does it create a new instance? Or is the instance common for all tests and gets cleaned up afterwards?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

BaseCLITest inherits from InstanceMixin:

class InstanceMixin(object):

    @classmethod
    def setUpClass(cls):
        super(InstanceMixin, cls).setUpClass()

        cls.instance = cls.connection.Instance.please.create(
            name='test_cli_i%s' % cls.generate_hash()[:10],
            description='IntegrationTest %s' % datetime.now(),
        )

    @classmethod
    def tearDownClass(cls):
        cls.instance.delete()
        super(InstanceMixin, cls).tearDownClass()

So it's a instance common for all test - and then I need to correct the publish test then :) Good catch.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we could remove the publish_files test as test_file_list is kind of depending on that functionality anyway


def test_file_list(self):
self._publish_files()

result = self.runner.invoke(cli, args=[
'hosting', self.instance.name, '--list-files'
])

self.assertIn('index.html', result.output)
self.assertIn('css/page.css', result.output)

def _publish_files(self):
result = self.runner.invoke(cli, args=[
'hosting', self.instance.name, '--publish', 'tests/hosting_files_examples'
], obj={})

self.assertIn('index.html', result.output)
self.assertIn('css/page.css', result.output)