Skip to content

feat(cli): no-docker option #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2023
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
33 changes: 26 additions & 7 deletions python/rpdk/python/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ def __init__(self):
self.package_name = None
self.package_root = None
self._use_docker = None
self._no_docker = None
self._protocol_version = "2.0.0"

def _init_from_project(self, project):
self.namespace = tuple(s.lower() for s in project.type_info)
self.package_name = "_".join(self.namespace)
self._use_docker = project.settings.get("use_docker")
# Check config file for (legacy) 'useDocker' and use_docker settings
self._use_docker = project.settings.get("useDocker") or project.settings.get(
"use_docker"
)
self.package_root = project.root / "src"

def _init_settings(self, project):
Expand All @@ -78,14 +82,29 @@ def _init_settings(self, project):
".resource", ".test_entrypoint"
)

self._use_docker = self._use_docker or input_with_validation(
"Use docker for platform-independent packaging (Y/n)?\n",
validate_no,
"This is highly recommended unless you are experienced \n"
"with cross-platform Python packaging.",
)
# If use_docker specified in .rpdk-config file or cli switch
# Ensure only 1 is true, with preference to use_docker
if project.settings.get("use_docker") is True:
self._use_docker = True
self._no_docker = False
# If no_docker specified in .rpdk-config file or cli switch
elif project.settings.get("no_docker") is True:
self._use_docker = False
self._no_docker = True
else:
# If neither no_docker nor use_docker specified in .rpdk-config
# file or cli switch, prompt to use containers or not
self._use_docker = input_with_validation(
"Use docker for platform-independent packaging (Y/n)?\n",
validate_no,
"This is highly recommended unless you are experienced \n"
"with cross-platform Python packaging.",
)
self._no_docker = not self._use_docker

# project.settings will get saved into .rpdk-config by cloudformation-cli
project.settings["use_docker"] = self._use_docker
project.settings["no_docker"] = self._no_docker
project.settings["protocolVersion"] = self._protocol_version

def init(self, project):
Expand Down
11 changes: 10 additions & 1 deletion python/rpdk/python/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ def setup_subparser(subparsers, parents, python_version, python_version_number):
)
parser.set_defaults(language=python_version)

parser.add_argument(
group = parser.add_mutually_exclusive_group()

group.add_argument(
"-d",
"--use-docker",
action="store_true",
Expand All @@ -18,6 +20,13 @@ def setup_subparser(subparsers, parents, python_version, python_version_number):
with cross-platform Python packaging.""",
)

group.add_argument(
"--no-docker",
action="store_true",
help="""Generally not recommended unless you are experienced
with cross-platform Python packaging""",
)

return parser


Expand Down
144 changes: 141 additions & 3 deletions tests/plugin/codegen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,32 @@ def resource_project(tmp_path):
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
)
with patch_plugins, patch_wizard:
project.init(TYPE_NAME, PythonLanguagePlugin.NAME)
project.init(
TYPE_NAME,
PythonLanguagePlugin.NAME,
settings={"use_docker": False, "no_docker": True},
)
return project


@pytest.fixture
def resource_project_use_docker(tmp_path):
project = Project(root=tmp_path)

patch_plugins = patch.dict(
"rpdk.core.plugin_registry.PLUGIN_REGISTRY",
{PythonLanguagePlugin.NAME: lambda: PythonLanguagePlugin},
clear=True,
)
patch_wizard = patch(
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
)
with patch_plugins, patch_wizard:
project.init(
TYPE_NAME,
PythonLanguagePlugin.NAME,
settings={"use_docker": True, "no_docker": False},
)
return project


Expand All @@ -125,7 +150,32 @@ def hook_project(tmp_path):
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
)
with patch_plugins, patch_wizard:
project.init_hook(TYPE_NAME, PythonLanguagePlugin.NAME)
project.init_hook(
TYPE_NAME,
PythonLanguagePlugin.NAME,
settings={"use_docker": False, "no_docker": True},
)
return project


@pytest.fixture
def hook_project_use_docker(tmp_path):
project = Project(root=tmp_path)

patch_plugins = patch.dict(
"rpdk.core.plugin_registry.PLUGIN_REGISTRY",
{PythonLanguagePlugin.NAME: lambda: PythonLanguagePlugin},
clear=True,
)
patch_wizard = patch(
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
)
with patch_plugins, patch_wizard:
project.init_hook(
TYPE_NAME,
PythonLanguagePlugin.NAME,
settings={"use_docker": True, "no_docker": False},
)
return project


Expand Down Expand Up @@ -172,6 +222,7 @@ def test__remove_build_artifacts_file_not_found(tmp_path):
def test_initialize_resource(resource_project):
assert resource_project.settings == {
"use_docker": False,
"no_docker": True,
"protocolVersion": "2.0.0",
}

Expand Down Expand Up @@ -209,8 +260,53 @@ def test_initialize_resource(resource_project):
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())


def test_initialize_resource_use_docker(resource_project_use_docker):
assert resource_project_use_docker.settings == {
"use_docker": True,
"no_docker": False,
"protocolVersion": "2.0.0",
}

files = get_files_in_project(resource_project_use_docker)
assert set(files) == {
".gitignore",
".rpdk-config",
"README.md",
"foo-bar-baz.json",
"requirements.txt",
f"{os.path.join('example_inputs', 'inputs_1_create.json')}",
f"{os.path.join('example_inputs', 'inputs_1_invalid.json')}",
f"{os.path.join('example_inputs', 'inputs_1_update.json')}",
"example_inputs",
"src",
f"{os.path.join('src', 'foo_bar_baz')}",
f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}",
f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}",
"template.yml",
}

assert "__pycache__" in files[".gitignore"].read_text()
assert SUPPORT_LIB_NAME in files["requirements.txt"].read_text()

readme = files["README.md"].read_text()
assert resource_project_use_docker.type_name in readme
assert SUPPORT_LIB_PKG in readme
assert "handlers.py" in readme
assert "models.py" in readme

assert resource_project_use_docker.entrypoint in files["template.yml"].read_text()

# this is a rough check the generated Python code is valid as far as syntax
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}"].read_text())
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())


def test_initialize_hook(hook_project):
assert hook_project.settings == {"use_docker": False, "protocolVersion": "2.0.0"}
assert hook_project.settings == {
"use_docker": False,
"no_docker": True,
"protocolVersion": "2.0.0",
}

files = get_files_in_project(hook_project)
assert set(files) == {
Expand Down Expand Up @@ -242,6 +338,43 @@ def test_initialize_hook(hook_project):
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())


def test_initialize_hook_use_docker(hook_project_use_docker):
assert hook_project_use_docker.settings == {
"use_docker": True,
"no_docker": False,
"protocolVersion": "2.0.0",
}

files = get_files_in_project(hook_project_use_docker)
assert set(files) == {
".gitignore",
".rpdk-config",
"README.md",
"foo-bar-baz.json",
"requirements.txt",
"src",
f"{os.path.join('src', 'foo_bar_baz')}",
f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}",
f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}",
"template.yml",
}

assert "__pycache__" in files[".gitignore"].read_text()
assert SUPPORT_LIB_NAME in files["requirements.txt"].read_text()

readme = files["README.md"].read_text()
assert hook_project_use_docker.type_name in readme
assert SUPPORT_LIB_PKG in readme
assert "handlers.py" in readme
assert "models.py" in readme

assert hook_project_use_docker.entrypoint in files["template.yml"].read_text()

# this is a rough check the generated Python code is valid as far as syntax
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}"].read_text())
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())


def test_generate_resource(resource_project):
resource_project.load_schema()
before = get_files_in_project(resource_project)
Expand Down Expand Up @@ -406,6 +539,7 @@ def test__pip_build_called_process_error(tmp_path):

def test__build_pip(plugin):
plugin._use_docker = False
plugin._no_docker = True

patch_pip = patch.object(plugin, "_pip_build", autospec=True)
patch_docker = patch.object(plugin, "_docker_build", autospec=True)
Expand All @@ -418,6 +552,7 @@ def test__build_pip(plugin):

def test__build_docker(plugin):
plugin._use_docker = True
plugin._no_docker = False

patch_pip = patch.object(plugin, "_pip_build", autospec=True)
patch_docker = patch.object(plugin, "_docker_build", autospec=True)
Expand All @@ -431,6 +566,7 @@ def test__build_docker(plugin):
# Test _build_docker on Linux/Unix-like systems
def test__build_docker_posix(plugin):
plugin._use_docker = True
plugin._no_docker = False

patch_pip = patch.object(plugin, "_pip_build", autospec=True)
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)
Expand All @@ -456,6 +592,7 @@ def test__build_docker_posix(plugin):
# Test _build_docker on Windows
def test__build_docker_windows(plugin):
plugin._use_docker = True
plugin._no_docker = False

patch_pip = patch.object(plugin, "_pip_build", autospec=True)
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)
Expand All @@ -481,6 +618,7 @@ def test__build_docker_windows(plugin):
# Test _build_docker if geteuid fails
def test__build_docker_no_euid(plugin):
plugin._use_docker = True
plugin._no_docker = False

patch_pip = patch.object(plugin, "_pip_build", autospec=True)
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)
Expand Down
Loading