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
18 changes: 13 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,19 @@ Syncano Hosting
Syncano Hosting is a simple way to host your static files on Syncano servers.
The CLI supports it in the following way:

This command will list files for currently hosted website::
This command will list currently defined hostings in the instance::

syncano hosting list

This command will list files for currently hosted website (for `default` hosting)::

syncano hosting list files

This command will publish all files inside *<base_dir>* to the default Syncano Hosting instance.
When publishing the whole directory, the structure will be mapped on Syncano.::

syncano hosting publish <base_dir>

This command will unpublish currently published hosting::

syncano hosting unpublish


This command will permamently delete the hosting::

Expand All @@ -289,6 +289,14 @@ This command will update single file::

syncano hosting update hosting/file/path local/file/path

For each of the above command you can specify the domain to change just after hosting command, example::

syncano hosting --domain staging publish <base_dir>

Will create a new hosting which will be available under: `<instance_name>--staging.syncano.site`
If this hosting is also a default one, it will be available under: `<instance_name>.syncano.site`.


Custom Sockets
==============

Expand Down
27 changes: 20 additions & 7 deletions syncano_cli/hosting/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@

import click
from syncano_cli.base.command import BaseInstanceCommand
from syncano_cli.hosting.exceptions import NoDefaultHostingFoundException, PathNotFoundException, UnicodeInPathException
from syncano_cli.hosting.exceptions import NoHostingFoundException, PathNotFoundException, UnicodeInPathException


class HostingCommands(BaseInstanceCommand):

VALID_PATH_REGEX = re.compile(r'^(?!/)([a-zA-Z0-9\-\._]+/{0,1})+(?<!/)\Z')

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

def print_hostings(self, hostings):
click.echo('Defined hostings:')
self._print_separator()
click.echo('{0:30}{1:20}'.format('Label', 'Domains'))
self._print_separator()
for label, domains in hostings:
click.echo(
'{0:30}{1:20}'.format(
label,
', '.join(domains)
)
)

def list_hosting_files(self, domain):
hosting = self._get_hosting(domain=domain)
files_list = hosting.list_files()
Expand Down Expand Up @@ -58,7 +71,7 @@ def unpublish(self, domain):
hosting.save()
click.echo('INFO: Hosting `{}` unpublished.'.format(hosting.label))

def delete_hosting(self, domain, path=None):
def delete_hosting(self, domain):
hosting = self._get_hosting(domain=domain)
deleted_label = hosting.label
hosting.delete()
Expand Down Expand Up @@ -97,19 +110,19 @@ def _get_hosting(self, domain, is_new=False):
break

if not to_return and not is_new:
raise NoDefaultHostingFoundException()
raise NoHostingFoundException(format_args=[domain])
return to_return

def _validate_path(self, file_path):
if not self.VALID_PATH_REGEX.match(file_path):
raise UnicodeInPathException()

def print_hosting_files(self, hosting_files):
print('Hosting files:')
click.echo('Hosting files:')
self._print_separator()
for hosting_file in hosting_files:
print(hosting_file.path)
click.echo(hosting_file.path)

@staticmethod
def _print_separator():
print(79 * '-')
click.echo(79 * '-')
45 changes: 29 additions & 16 deletions syncano_cli/hosting/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,51 @@ def top_hosting():
@click.pass_context
@click.option('--config', help=u'Account configuration file.')
@click.option('--instance-name', help=u'Instance name.')
def hosting(ctx, config, instance_name):
@click.option('--domain', default='default')
def hosting(ctx, config, instance_name, domain):
"""
Handle hosting and hosting files. Allow to publish static pages to the Syncano Hosting.
"""
instance = get_instance(config, instance_name)
hosting_commands = HostingCommands(instance)
ctx.obj['hosting_commands'] = hosting_commands
ctx.obj['domain'] = domain


@hosting.command()
@click.pass_context
@click.argument('directory')
def publish(ctx, directory):
def publish(ctx, directory,):

validate_publish(directory)
domain = validate_domain() # prepared for user defined domains;
domain = ctx.obj['domain']
domain = validate_domain(domain) # prepared for user defined domains;
hosting_commands = ctx.obj['hosting_commands']
hosting_commands.publish(domain=domain, base_dir=directory)
click.echo("INFO: Your site published. Go to: https://{}.syncano.site".format(
hosting_commands.instance.name)
click.echo(
"INFO: Your site published. If default, go to: https://{instance_name}--{domain}.syncano.site. "
"Otherwise, go to: https://{instance_name}.syncano.site".format(
hosting_commands.instance.name,
domain=domain
)
)


@hosting.command()
@hosting.group(invoke_without_command=True)
@click.pass_context
def unpublish(ctx):
domain = validate_domain()
ctx.obj['hosting_commands'].unpublish(domain=domain)
def list(ctx):
if ctx.invoked_subcommand is None:
hosting_commands = ctx.obj['hosting_commands']
hosting_commands.print_hostings(
hostings=hosting_commands.list_hostings()
)


@hosting.command()
@list.command()
@click.pass_context
def list(ctx):
domain = validate_domain()
def files(ctx):
domain = ctx.obj['domain']
domain = validate_domain(domain)
hosting_commands = ctx.obj['hosting_commands']
hosting_commands.print_hosting_files(
hosting_files=hosting_commands.list_hosting_files(domain)
Expand All @@ -59,11 +70,12 @@ def list(ctx):
@click.pass_context
@click.argument('path', required=False)
def delete(ctx, path):
domain = validate_domain()
domain = ctx.obj['domain']
domain = validate_domain(domain)
hosting_commands = ctx.obj['hosting_commands']
if not path:
if click.confirm('Do you want to remove whole hosting?'):
hosting_commands.delete_hosting(domain=domain, path=path)
if click.confirm('Do you want to remove whole hosting, domain: {}?'.format(domain)):
hosting_commands.delete_hosting(domain=domain)
else:
raise Abort()
else:
Expand All @@ -75,5 +87,6 @@ def delete(ctx, path):
@click.argument('path')
@click.argument('file')
def update(ctx, path, file):
domain = validate_domain()
domain = ctx.obj['domain']
domain = validate_domain(domain)
ctx.obj['hosting_commands'].update_single_file(domain=domain, path=path, file=file)
4 changes: 2 additions & 2 deletions syncano_cli/hosting/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class UnicodeInPathException(CLIBaseException):
default_message = u'Unicode characters in path are not supported. Check the files names.'


class NoDefaultHostingFoundException(CLIBaseException):
default_message = u'No default hosting found. Exit.'
class NoHostingFoundException(CLIBaseException):
default_message = u'Hosting with domain `{}` - not found. Exit.'


class NotADirectoryException(CLIBaseException):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_execute_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_script_endpoint(self):
'execute', '{}_test_script_endpoint'.format(name_prefix)
], obj={})

self.assertEqual(result.output, '12\n')
self.assertIn('12\n', result.output)

def test_script_endpoint_with_payload(self):
name_prefix = 'payload_response'
Expand Down
78 changes: 41 additions & 37 deletions tests/test_hosting_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,69 @@

class HostingCommandsTest(BaseCLITest):

def test_hosting_commands(self):
def _base_test(self, domain):
# test publishing;
self._publish_files()
result = self._get_list_output()
self._publish_files(domain=domain)
result = self._get_list_files_output(domain=domain)
self.assertIn('index.html', result.output)
self.assertIn('css/page.css', result.output)

# test single file deletion;
self._delete_single_file('css/page.css')
result = self._get_list_output()
self._delete_single_file('css/page.css', domain=domain)
result = self._get_list_files_output(domain=domain)
self.assertNotIn('css/page.css', result.output)

# test update file which do not exist;
result = self.runner.invoke(cli, args=[
'hosting', 'update', 'css/page.css', 'tests/hosting_files_examples/css/page.css'
], obj={})
args = ['hosting', 'update', 'css/page.css', 'tests/hosting_files_examples/css/page.css']
self._extend_args(args, domain)
result = self.runner.invoke(cli, args=args, obj={})
self.assertIn('css/page.css', result.output)

# test hosting delete;
self._delete_hosting()
result = self._get_list_output()
self.assertIn('No default hosting found. Exit.', result.output)
self._delete_hosting(domain=domain)
result = self._get_list_files_output(domain=domain)
self.assertIn('Hosting with domain `{}` - not found. Exit.'.format(domain or 'default'), result.output)

# recreate hosting;
self._publish_files()

# test unpublish;
result = self.runner.invoke(cli, args=[
'hosting', 'unpublish'
], obj={})
self.assertIn('unpublished', result.output)
result = self._get_list_output()
self.assertIn('No default hosting found. Exit.', result.output)

def _get_list_output(self):
result = self.runner.invoke(cli, args=[
'hosting', 'list'
], obj={})
self._publish_files(domain=domain)

def test_hosting_commands(self):
self._base_test(domain=None) # default test

def test_hosting_with_domains(self):
self._base_test(domain='efbgh') # custom domain

def _get_list_files_output(self, domain=None):
args = ['hosting', 'list', 'files']
self._extend_args(args, domain)
result = self.runner.invoke(cli, args=args, obj={})
return result

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

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

def _delete_single_file(self, path):
result = self.runner.invoke(cli, args=[
'hosting', 'delete', path
], obj={})
def _delete_single_file(self, path, domain=None):
args = ['hosting', 'delete', path]
self._extend_args(args, domain)
result = self.runner.invoke(cli, args=args, obj={})

self.assertIn('deleted.', result.output)

def _delete_hosting(self):
result = self.runner.invoke(cli, args=[
'hosting', 'delete'
], input='y', obj={})
def _delete_hosting(self, domain=None):
args = ['hosting', 'delete']
self._extend_args(args, domain)
result = self.runner.invoke(cli, args=args, input='y', obj={})

self.assertIn('Hosting', result.output)
self.assertIn('deleted.', result.output)

@classmethod
def _extend_args(cls, args, domain):
if domain:
args.insert(1, '--domain')
args.insert(2, domain)