Skip to content

Commit ef57388

Browse files
authored
Small improvements (#45)
cli build validation, removing "missing project name" message, reading vault_secret from deployment_config
1 parent d5c62e5 commit ef57388

File tree

7 files changed

+106
-52
lines changed

7 files changed

+106
-52
lines changed

bigflow/build.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88

99
import xmlrunner
1010

11-
from .cli import walk_workflows, import_deployment_config, _valid_datetime
11+
from .cli import walk_workflows, import_deployment_config, _valid_datetime, SETUP_VALIDATION_MESSAGE
1212
from .dagbuilder import generate_dag_file
1313
from .resources import read_requirements, find_all_resources
1414
from .utils import resolve, now
1515
from .version import get_version
1616
from .utils import run_process
1717

1818

19+
1920
__all__ = [
2021
'project_setup',
2122
'auto_configuration'
@@ -150,6 +151,7 @@ def finalize_options(self) -> None:
150151
pass
151152

152153
def run(self) -> None:
154+
print(SETUP_VALIDATION_MESSAGE)
153155
_valid_datetime(self.start_time)
154156
if self.build_package or self.should_run_whole_build():
155157
print('Building the pip package')

bigflow/cli.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from bigflow.resources import find_file
1818
from .utils import run_process
1919

20+
SETUP_VALIDATION_MESSAGE = 'BigFlow setup is valid.'
21+
2022

2123
def resolve(path: Path) -> str:
2224
return str(path.absolute())
@@ -219,8 +221,7 @@ def cli_run(project_package: str,
219221

220222

221223
def _parse_args(project_name: Optional[str], args) -> Namespace:
222-
project_name_description = build_project_name_description(project_name)
223-
parser = argparse.ArgumentParser(description=f'Welcome to BigFlow CLI. {project_name_description}'
224+
parser = argparse.ArgumentParser(description=f'Welcome to BigFlow CLI.'
224225
'\nType: bigflow {run,deploy-dags,deploy-image,deploy,build,build-dags,build-image,build-package} -h to print detailed help for a selected command.')
225226
subparsers = parser.add_subparsers(dest='operation',
226227
required=True,
@@ -455,12 +456,16 @@ def _resolve_property(args, property_name):
455456

456457

457458
def _cli_deploy_dags(args):
459+
try:
460+
vault_secret = _resolve_property(args, 'vault_secret')
461+
except ValueError:
462+
vault_secret = None
458463
deploy_dags_folder(dags_dir=_resolve_dags_dir(args),
459464
dags_bucket=_resolve_property(args, 'dags_bucket'),
460465
clear_dags_folder=args.clear_dags_folder,
461466
auth_method=args.auth_method,
462467
vault_endpoint=_resolve_vault_endpoint(args),
463-
vault_secret=args.vault_secret,
468+
vault_secret=vault_secret,
464469
project_id=_resolve_property(args, 'gcp_project_id')
465470
)
466471

@@ -471,6 +476,10 @@ def _load_image_from_tar(image_tar_path: str):
471476

472477
def _cli_deploy_image(args):
473478
docker_repository = _resolve_property(args, 'docker_repository')
479+
try:
480+
vault_secret = _resolve_property(args, 'vault_secret')
481+
except ValueError:
482+
vault_secret = None
474483
if args.image_tar_path:
475484
build_ver = _decode_version_number_from_file_name(Path(args.image_tar_path))
476485
image_id = load_image_from_tar(args.image_tar_path)
@@ -482,23 +491,23 @@ def _cli_deploy_image(args):
482491
auth_method=args.auth_method,
483492
docker_repository=docker_repository,
484493
vault_endpoint=_resolve_vault_endpoint(args),
485-
vault_secret=args.vault_secret)
494+
vault_secret=vault_secret)
486495

487496

488497
def _cli_build_image(args):
489-
check_if_project_setup_exists()
498+
validate_project_setup()
490499
cmd = 'python project_setup.py build_project --build-image' + (' --export-image-to-file' if args.export_image_to_file else '')
491500
run_process(cmd)
492501

493502

494503
def _cli_build_package():
495-
check_if_project_setup_exists()
504+
validate_project_setup()
496505
cmd = 'python project_setup.py build_project --build-package'
497506
run_process(cmd)
498507

499508

500509
def _cli_build_dags(args):
501-
check_if_project_setup_exists()
510+
validate_project_setup()
502511
cmd = ['python', 'project_setup.py', 'build_project', '--build-dags']
503512
if args.workflow:
504513
cmd.append('--workflow')
@@ -510,7 +519,7 @@ def _cli_build_dags(args):
510519

511520

512521
def _cli_build(args):
513-
check_if_project_setup_exists()
522+
validate_project_setup()
514523
cmd = ['python', 'project_setup.py', 'build_project']
515524
if args.workflow:
516525
cmd.append('--workflow')
@@ -527,6 +536,14 @@ def check_if_project_setup_exists():
527536
find_file('project_setup.py', Path('.'), 1)
528537

529538

539+
def validate_project_setup():
540+
check_if_project_setup_exists()
541+
cmd = ['python', 'project_setup.py', 'build_project', '--validate-setup']
542+
output = run_process(cmd)
543+
if SETUP_VALIDATION_MESSAGE not in output:
544+
raise ValueError('The project_setup.py is invalid. Check the documentation how to create a valid project_setup.py: https://github.com/allegro/bigflow/blob/master/docs/build.md')
545+
546+
530547
def cli(raw_args) -> None:
531548
project_name = read_project_name_from_setup()
532549
parsed_args = _parse_args(project_name, raw_args)

bf renamed to scripts/bf

File renamed without changes.

scripts/bigflow

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
5+
from bigflow.cli import cli
6+
7+
cli(sys.argv[1:])

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def run(self):
3939

4040
setuptools.setup(
4141
name="bigflow",
42-
version="1.0.dev3",
42+
version="1.0.dev4",
4343
author=u"Chi",
4444
author_email="[email protected]",
4545
description="BigQuery client wrapper with clean API",
@@ -58,7 +58,7 @@ def run(self):
5858
extras_require={
5959
'stackdriver': stackdriver_extras_require
6060
},
61-
scripts=["bf"],
61+
scripts=["scripts/bf", "scripts/bigflow"],
6262
cmdclass={
6363
'build_and_install_wheel': BuildAndInstallWheelCommand
6464
}

test/test_build.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44
import subprocess
55
from unittest import TestCase, mock
6-
from bigflow.cli import walk_module_files
6+
from bigflow.cli import walk_module_files, SETUP_VALIDATION_MESSAGE
77
from bigflow.version import get_version
88
from bigflow.build import now, get_docker_image_id, build_docker_image_tag, \
99
clear_image_leftovers, clear_package_leftovers, clear_dags_leftovers, auto_configuration, \
@@ -147,6 +147,13 @@ def test_should_build_project_artifacts(self):
147147
self.assertFalse(image_leftovers_exist())
148148
self.assertFalse(dags_leftovers_exist())
149149

150+
def test_should_validate_project_setup(self):
151+
# expected
152+
self.assertTrue(SETUP_VALIDATION_MESSAGE in self.test_project.run_build('python project_setup.py build_project'))
153+
self.assertTrue(SETUP_VALIDATION_MESSAGE in self.test_project.run_build('python project_setup.py build_project --build-dags'))
154+
self.assertTrue(SETUP_VALIDATION_MESSAGE in self.test_project.run_build('python project_setup.py build_project --build-image'))
155+
self.assertTrue(SETUP_VALIDATION_MESSAGE in self.test_project.run_build('python project_setup.py build_project --build-package'))
156+
150157

151158
class BuildPackageCommandE2E(SetupTestCase):
152159
def test_should_execute_build_package_command(self):

test/test_cli.py

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ def test_should_call_cli_deploy_dags_command__with_defaults_and_with_implicit_de
306306
deployment_config = Config(name='dev',
307307
properties={
308308
'gcp_project_id': 'my-gcp-project-id',
309-
'dags_bucket': 'my-dags-bucket'
309+
'dags_bucket': 'my-dags-bucket',
310+
'vault_secret': 'secret'
310311
})
311312
''')
312313

@@ -320,7 +321,7 @@ def test_should_call_cli_deploy_dags_command__with_defaults_and_with_implicit_de
320321
dags_dir=self._expected_default_dags_dir(),
321322
project_id='my-gcp-project-id',
322323
vault_endpoint=None,
323-
vault_secret=None)
324+
vault_secret='secret')
324325

325326
dc_file.unlink()
326327

@@ -334,12 +335,14 @@ def test_should_call_cli_deploy_dags_command_for_different_environments(self, de
334335
deployment_config = Config(name='dev',
335336
properties={
336337
'gcp_project_id': 'my-gcp-dev-project-id',
337-
'dags_bucket': 'my-dags-dev-bucket'
338+
'dags_bucket': 'my-dags-dev-bucket',
339+
'vault_secret': 'secret-dev'
338340
})\
339341
.add_configuration(name='prod',
340342
properties={
341343
'gcp_project_id': 'my-gcp-prod-project-id',
342-
'dags_bucket': 'my-dags-prod-bucket'
344+
'dags_bucket': 'my-dags-prod-bucket',
345+
'vault_secret': 'secret-prod'
343346
})
344347
345348
''')
@@ -354,7 +357,7 @@ def test_should_call_cli_deploy_dags_command_for_different_environments(self, de
354357
dags_dir=self._expected_default_dags_dir(),
355358
project_id='my-gcp-dev-project-id',
356359
vault_endpoint=None,
357-
vault_secret=None)
360+
vault_secret='secret-dev')
358361

359362
# when
360363
cli(['deploy-dags', '--config', 'dev'])
@@ -366,7 +369,7 @@ def test_should_call_cli_deploy_dags_command_for_different_environments(self, de
366369
dags_dir=self._expected_default_dags_dir(),
367370
project_id='my-gcp-dev-project-id',
368371
vault_endpoint=None,
369-
vault_secret=None)
372+
vault_secret='secret-dev')
370373

371374
# when
372375
cli(['deploy-dags', '--config', 'prod'])
@@ -378,7 +381,7 @@ def test_should_call_cli_deploy_dags_command_for_different_environments(self, de
378381
dags_dir=self._expected_default_dags_dir(),
379382
project_id='my-gcp-prod-project-id',
380383
vault_endpoint=None,
381-
vault_secret=None)
384+
vault_secret='secret-prod')
382385

383386
dc_file.unlink()
384387

@@ -393,16 +396,16 @@ def test_should_call_cli_deploy_dags_command__when_parameters_are_given_by_expli
393396
properties={
394397
'gcp_project_id': 'my-another-gcp-project-id',
395398
'vault_endpoint': 'my-another-vault-endpoint',
396-
'dags_bucket': 'my-another-dags-bucket'
399+
'dags_bucket': 'my-another-dags-bucket',
400+
'vault_secret': 'secrett'
397401
})
398402
''')
399403

400404
#when
401405
cli(['deploy-dags',
402406
'--deployment-config-path', dc_file.as_posix(),
403407
'--dags-dir', '/tmp/my-dags-dir',
404-
'--auth-method', 'service_account',
405-
'--vault-secret', 'secrett'
408+
'--auth-method', 'service_account'
406409
])
407410

408411
#then
@@ -539,8 +542,6 @@ def test_should_call_both_deploy_methods_with_deploy_command(self, deploy_docker
539542
# when
540543
cli(['deploy', '-v', '0.0.2'])
541544

542-
543-
544545
# then
545546
deploy_dags_folder_mock.assert_called_with(auth_method='local_account',
546547
clear_dags_folder=False,
@@ -608,6 +609,20 @@ def test_should_call_cli_build_image_command(self, _cli_build_image_mock):
608609
# then
609610
_cli_build_image_mock.assert_called_with(Namespace(operation='build-image', export_image_to_file=True))
610611

612+
@mock.patch('bigflow.cli.run_process')
613+
@mock.patch('bigflow.cli.validate_project_setup')
614+
def test_should_call_build_command_through_CLI(
615+
self, validate_project_setup_mock, run_process_mock):
616+
# given
617+
validate_project_setup_mock.return_value = '.'
618+
619+
# when
620+
cli(['build'])
621+
622+
# then
623+
self.assertEqual(run_process_mock.call_count, 1)
624+
run_process_mock.assert_any_call('python project_setup.py build_project'.split(' '))
625+
611626
@mock.patch('bigflow.cli._cli_build_package')
612627
def test_should_call_cli_build_package_command(self, _cli_build_package_mock):
613628
# when
@@ -645,58 +660,50 @@ def test_should_call_cli_build_command(self, _cli_build_mock):
645660
_cli_build_mock.assert_called_with(Namespace(operation='build', export_image_to_file=True,
646661
start_time='2020-01-01 00:00:00', workflow='some_workflow'))
647662

648-
@mock.patch('bigflow.cli.check_if_project_setup_exists')
649663
@mock.patch('bigflow.cli.run_process')
650-
def test_should_call_build_command_through_CLI(self, run_process_mock, check_if_project_setup_exists_mock):
651-
# given
652-
check_if_project_setup_exists_mock.return_value = TEST_PROJECT_PATH
653-
654-
# when
655-
cli(['build'])
656-
657-
# then
658-
self.assertEqual(run_process_mock.call_count, 1)
659-
run_process_mock.assert_any_call('python project_setup.py build_project --build-dags --build-image --build-package'.format(str(TEST_PROJECT_PATH)))
660-
661-
@mock.patch('bigflow.cli.check_if_project_setup_exists')
662-
@mock.patch('bigflow.cli.run_process')
663-
def test_should_call_build_package_command_through_CLI(self, run_process_mock, check_if_project_setup_exists_mock):
664-
# given
665-
check_if_project_setup_exists_mock.return_value = TEST_PROJECT_PATH
666-
664+
@mock.patch('bigflow.cli.validate_project_setup')
665+
def test_should_call_build_package_command_through_CLI(self, validate_project_setup_mock, run_process_mock):
667666
# when
668667
cli(['build-package'])
669668

670669
# then
671670
self.assertEqual(run_process_mock.call_count, 1)
672671
run_process_mock.assert_any_call('python project_setup.py build_project --build-package')
673672

674-
@mock.patch('bigflow.cli.check_if_project_setup_exists')
675673
@mock.patch('bigflow.cli.run_process')
676-
def test_should_call_build_command_through_CLI(self, run_process_mock, check_if_project_setup_exists_mock):
677-
# given
678-
check_if_project_setup_exists_mock.return_value = TEST_PROJECT_PATH
679-
674+
@mock.patch('bigflow.cli.find_file')
675+
@mock.patch('bigflow.cli.validate_project_setup')
676+
def test_should_call_build_image_command_through_CLI(self, validate_project_setup_mock, find_file_mock, run_process_mock):
680677
# when
681678
cli(['build-image'])
682679

683680
# then
684681
self.assertEqual(run_process_mock.call_count, 1)
685682
run_process_mock.assert_any_call('python project_setup.py build_project --build-image')
686683

687-
@mock.patch('bigflow.cli.check_if_project_setup_exists')
688684
@mock.patch('bigflow.cli.run_process')
689-
def test_should_call_build_dags_command_through_CLI(self, run_process_mock, check_if_project_setup_exists_mock):
690-
# given
691-
check_if_project_setup_exists_mock.return_value = TEST_PROJECT_PATH
692-
685+
@mock.patch('bigflow.cli.validate_project_setup')
686+
def test_should_call_build_dags_command_through_CLI(self, validate_project_setup_mock, run_process_mock):
693687
# when
694688
cli(['build-dags'])
695689

696690
# then
697691
self.assertEqual(run_process_mock.call_count, 1)
698692
run_process_mock.assert_any_call('python project_setup.py build_project --build-dags'.split(' '))
699693

694+
@mock.patch('bigflow.cli.run_process')
695+
@mock.patch('bigflow.cli.validate_project_setup')
696+
def test_should_validate_project_setup_before_build(
697+
self, validate_project_setup_mock, run_process_mock):
698+
# when
699+
cli(['build'])
700+
cli(['build-image'])
701+
cli(['build-dags'])
702+
cli(['build-package'])
703+
704+
# then
705+
self.assertEqual(validate_project_setup_mock.call_count, 4)
706+
700707
def _expected_default_dags_dir(self):
701708
return (Path(os.getcwd()) / '.dags').as_posix()
702709

@@ -707,3 +714,17 @@ def _touch_file(self, file_name: str, content: str = ''):
707714
f.write_text(content)
708715
return f
709716

717+
718+
class ValidateProjectSetupTestCase(TestCase):
719+
720+
@mock.patch('bigflow.cli.check_if_project_setup_exists')
721+
@mock.patch('bigflow.cli.run_process')
722+
def test_should_raise_error_if_no_expected_message_found_in_setup_output(
723+
self, run_process_mock, check_if_project_setup_exists_mock):
724+
# given
725+
run_process_mock.return_value = 'Unexpected message'
726+
727+
# then
728+
with self.assertRaises(ValueError) as e:
729+
# when
730+
validate_project_setup()

0 commit comments

Comments
 (0)