-
Notifications
You must be signed in to change notification settings - Fork 86
Support 'eb deploy ... --archive <ZIP|dir>' #535
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,18 +10,20 @@ | |
| # 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. | ||
|
|
||
| from os import path, chdir, getcwd | ||
| from os import path, chdir, getcwd, makedirs | ||
| import zipfile | ||
| import datetime | ||
| from typing import Optional | ||
|
|
||
| from cement.utils.misc import minimal_logger | ||
|
|
||
| from ebcli.core import io, hooks, fileoperations | ||
| from ebcli.core.abstractcontroller import AbstractBaseController | ||
| from ebcli.lib import elasticbeanstalk, utils | ||
| from ebcli.objects.exceptions import InvalidOptionsError | ||
| from ebcli.lib import elasticbeanstalk | ||
| from ebcli.objects.environment import Environment | ||
| from ebcli.objects.exceptions import InvalidOptionsError, NotInitializedError | ||
| from ebcli.operations import commonops, deployops, composeops, statusops | ||
| from ebcli.resources.strings import strings, flag_text, alerts | ||
| from ebcli.resources.statics import platform_branch_lifecycle_states | ||
| from ebcli.resources.strings import strings, flag_text | ||
|
|
||
| LOG = minimal_logger(__name__) | ||
|
|
||
|
|
@@ -47,7 +49,8 @@ class Meta(AbstractBaseController.Meta): | |
| (['--source'], dict(help=flag_text['deploy.source'])), | ||
| (['-p', '--process'], dict( | ||
| action='store_true', help=flag_text['deploy.process'])), | ||
| ] | ||
| (['--archive'], dict(help=flag_text['deploy.archive'])),] | ||
|
|
||
| usage = AbstractBaseController.Meta.usage.replace('{cmd}', label) | ||
|
|
||
| def do_command(self): | ||
|
|
@@ -62,9 +65,28 @@ def do_command(self): | |
| self.message = self.app.pargs.message | ||
| self.staged = self.app.pargs.staged | ||
| self.source = self.app.pargs.source | ||
| self.app_name = self.get_app_name() | ||
| self.env_name = self.get_env_name() | ||
| self.archive = self.app.pargs.archive | ||
| if self.archive and not self.app.pargs.region: | ||
| raise InvalidOptionsError(strings['deploy.archivewithoutregion']) | ||
|
Comment on lines
+69
to
+70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is region required?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It comes down to the EB CLI idiom that (almost) every With Another solution to avoid forcing selection of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, and I think that's the right decision here |
||
| if self.source and self.archive: | ||
| raise InvalidOptionsError(strings['deploy.archivewithsource']) | ||
|
|
||
| self.env_name = self.app.pargs.environment_name | ||
| if self.archive and not self.env_name: | ||
| raise InvalidOptionsError(strings['deploy.archivewithoutenvname']) | ||
| elif not self.archive: | ||
| self.env_name = self.env_name or self.get_env_name() | ||
|
|
||
| if self.archive: | ||
| environment = elasticbeanstalk.get_environment(env_name=self.env_name) | ||
| self.app_name = environment.app_name | ||
| else: | ||
| self.app_name = self.get_app_name() | ||
|
|
||
| self.version = self.app.pargs.version | ||
| if self.version and self.archive: | ||
| raise InvalidOptionsError(strings['deploy.archivewithversion']) | ||
|
|
||
| self.label = self.app.pargs.label | ||
| self.process = self.app.pargs.process | ||
| group_name = self.app.pargs.env_group_suffix | ||
|
|
@@ -76,9 +98,15 @@ def do_command(self): | |
|
|
||
| process_app_versions = fileoperations.env_yaml_exists() or self.process | ||
|
|
||
| source_bundle_zip = None | ||
| if self.archive: | ||
| source_bundle_zip = get_or_create_source_bundle(archive=self.archive, label=self.label) | ||
| self.label = self.label or source_bundle_zip.split(path.sep)[-1] | ||
|
|
||
| deployops.deploy(self.app_name, self.env_name, self.version, self.label, | ||
| self.message, group_name=group_name, process_app_versions=process_app_versions, | ||
| staged=self.staged, timeout=self.timeout, source=self.source) | ||
| staged=self.staged, timeout=self.timeout, source=self.source, | ||
| source_bundle=source_bundle_zip) | ||
|
|
||
| def multiple_app_deploy(self): | ||
| missing_env_yaml = [] | ||
|
|
@@ -181,3 +209,25 @@ def compose_deploy(self): | |
| def _check_env_lifecycle_state(env_name): | ||
| env = elasticbeanstalk.get_environment(env_name=env_name) | ||
| statusops.alert_environment_status(env) | ||
|
|
||
|
|
||
| def get_or_create_source_bundle(archive: str, label: str=None) -> Optional[str]: | ||
| if archive and zipfile.is_zipfile(archive): | ||
| source_bundle_zip = archive | ||
| elif archive and path.isdir(archive): | ||
| upload_target_dir = archive | ||
| utcnow = str(datetime.datetime.now(datetime.UTC).timestamp()) | ||
| migrations_path = path.join(path.expanduser('~'), '.ebartifacts') | ||
| if label: | ||
| zip_file_name = f"{label}.zip" | ||
| else: | ||
| zip_file_name = f"archives-{utcnow}.zip" | ||
| source_bundle_zip = path.join(migrations_path, 'archives', zip_file_name) | ||
| makedirs(path.join(migrations_path, 'archives'), exist_ok=True) | ||
| fileoperations.zip_up_folder( | ||
| upload_target_dir, | ||
| source_bundle_zip | ||
| ) | ||
| else: | ||
| raise InvalidOptionsError(strings['deploy.archivemustbedirirzip']) | ||
| return source_bundle_zip | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| import time | ||
| from datetime import datetime, timedelta | ||
| import platform | ||
| import zipfile | ||
|
|
||
| from ebcli.core.fileoperations import _marker | ||
|
|
||
|
|
@@ -486,8 +487,9 @@ def create_dummy_app_version(app_name): | |
| None, None, warning=False) | ||
|
|
||
|
|
||
| def create_app_version(app_name, process=False, label=None, message=None, staged=False, build_config=None): | ||
| def create_app_version(app_name, process=False, label=None, message=None, staged=False, build_config=None, source_bundle=None): | ||
| cwd = os.getcwd() | ||
|
|
||
| fileoperations.ProjectRoot.traverse() | ||
| try: | ||
| if heuristics.directory_is_empty(): | ||
|
|
@@ -514,7 +516,6 @@ def create_app_version(app_name, process=False, label=None, message=None, staged | |
|
|
||
| if len(description) > 200: | ||
| description = description[:195] + '...' | ||
|
|
||
| artifact = fileoperations.get_config_setting('deploy', 'artifact') | ||
| if artifact: | ||
| file_name, file_extension = os.path.splitext(artifact) | ||
|
|
@@ -525,16 +526,41 @@ def create_app_version(app_name, process=False, label=None, message=None, staged | |
| else: | ||
| s3_bucket, s3_key = get_app_version_s3_location(app_name, version_label) | ||
|
|
||
| file_name = file_path = None, None | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: i think this is more clear as |
||
| if s3_bucket is None and s3_key is None: | ||
| file_name, file_path = _zip_up_project( | ||
| version_label, source_control, staged=staged) | ||
| else: | ||
| file_name = None | ||
| file_path = None | ||
| if not source_bundle: | ||
| file_name, file_path = _zip_up_project( | ||
| version_label, source_control, staged=staged) | ||
| elif zipfile.is_zipfile(source_bundle): | ||
| file_name, file_path = label, source_bundle | ||
|
|
||
| return handle_upload_target(app_name, | ||
| s3_bucket, | ||
| s3_key, | ||
| file_name, | ||
| file_path, | ||
| version_label, | ||
| description, | ||
| process, | ||
| build_config, | ||
| ) | ||
|
|
||
|
|
||
| def handle_upload_target( | ||
| app_name, | ||
| s3_bucket, | ||
| s3_key, | ||
| file_name, | ||
| file_path, | ||
| version_label, | ||
| description, | ||
| process, | ||
| build_config, | ||
| relative_to_project_root=True | ||
| ): | ||
| bucket = elasticbeanstalk.get_storage_location() if s3_bucket is None else s3_bucket | ||
| key = app_name + '/' + file_name if s3_key is None else s3_key | ||
|
|
||
| key = app_name + '/' + file_name if s3_key is None else s3_key | ||
| try: | ||
| s3.get_object_info(bucket, key) | ||
| io.log_info('S3 Object already exists. Skipping upload.') | ||
|
|
@@ -544,12 +570,17 @@ def create_app_version(app_name, process=False, label=None, message=None, staged | |
| ' Try uploading the Application Version again.') | ||
|
|
||
| io.log_info('Uploading archive to s3 location: ' + key) | ||
| s3.upload_application_version(bucket, key, file_path) | ||
| if relative_to_project_root: | ||
| s3.upload_application_version(bucket, key, file_path) | ||
| else: | ||
| s3.upload_application_version(bucket, key, file_path, relative_to_project_root=False) | ||
|
|
||
| fileoperations.delete_app_versions() | ||
| if not relative_to_project_root: | ||
| fileoperations.delete_app_versions() | ||
| io.log_info('Creating AppVersion ' + version_label) | ||
| return _create_application_version(app_name, version_label, description, | ||
| bucket, key, process, build_config=build_config) | ||
| bucket, key, process, build_config=build_config, | ||
| relative_to_project_root=relative_to_project_root) | ||
|
|
||
|
|
||
| def create_codecommit_app_version(app_name, process=False, label=None, message=None, build_config=None): | ||
|
|
@@ -672,15 +703,15 @@ def create_app_version_from_source( | |
| def _create_application_version(app_name, version_label, description, | ||
| bucket, key, process=False, warning=True, | ||
| repository=None, commit_id=None, | ||
| build_config=None): | ||
| build_config=None, relative_to_project_root=True): | ||
| """ | ||
| A wrapper around elasticbeanstalk.create_application_version that | ||
| handles certain error cases: | ||
| * application doesnt exist | ||
| * version already exists | ||
| * validates BuildSpec files for CodeBuild | ||
| """ | ||
| if build_config is not None: | ||
| if relative_to_project_root and build_config is not None: | ||
| buildspecops.validate_build_config(build_config) | ||
| while True: | ||
| try: | ||
|
|
@@ -725,6 +756,15 @@ def _zip_up_project(version_label, source_control, staged=False): | |
| return file_name, file_path | ||
|
|
||
|
|
||
| def _zip_up_project_at_location(version_label, upload_target_dir, zip_output_path): | ||
| file_name = version_label + '.zip' | ||
| fileoperations.zip_up_folder( | ||
| upload_target_dir, | ||
| zip_output_path, | ||
| ) | ||
| return file_name, zip_output_path | ||
|
|
||
|
|
||
| def update_environment(env_name, changes, nohang, remove=None, | ||
| template=None, timeout=None, template_body=None, | ||
| solution_stack_name=None, platform_arn=None): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -260,8 +260,7 @@ | |
| 'Tags may only contain letters, numbers, and the following ' | ||
| 'symbols: / _ . : + = - @', | ||
| 'tags.max': 'Elastic Beanstalk supports a maximum of 50 tags.', | ||
| 'deploy.invalidoptions': 'You cannot use the "--version" option with either the "--message" ' | ||
| 'or "--label" option.', | ||
|
|
||
| 'init.getvarsfromoldeb': 'You previous used an earlier version of eb. Getting options from ' | ||
| '.elasticbeanstalk/config.\n' | ||
| 'Credentials will now be stored in ~/.aws/config', | ||
|
|
@@ -324,12 +323,22 @@ | |
| 'region.china.credentials': | ||
| 'To use the China (Beijing) region, account credentials unique to the ' | ||
| 'China (Beijing) region must be used.', | ||
|
|
||
| 'deploy.notadirectory': 'The directory {module} does not exist.', | ||
| 'deploy.modulemissingenvyaml': | ||
| 'All specified modules require an env.yaml file.\n' | ||
| 'The following modules are missing this file: {modules}', | ||
| 'deploy.noenvname': | ||
| 'No environment name was specified in env.yaml for module {module}. Unable to deploy.', | ||
| 'deploy.invalidoptions': 'You cannot use the "--version" option with either the "--message" ' | ||
| 'or "--label" option.', | ||
| 'deploy.archivewithoutregion': 'You cannot use the "--archive" option without the "--region" option.', | ||
| 'deploy.archivewithoutenvname': 'You cannot use the "--archive" option without the environment name.', | ||
| 'deploy.archivewithversion': 'You cannot use the "--archive" option with the "--version" option for environment updates. ' | ||
| 'These are mutually exclusive methods for specifying application code.', | ||
| 'deploy.archivewithsource': 'You cannot use the "--archive" option with the "--source" option for environment updates. ' | ||
| 'These are mutually exclusive methods for specifying application code.', | ||
| 'deploy.archivemustbedirirzip': 'The "--archive" option requires a directory or ZIP file as an argument.', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: typo in |
||
| 'compose.noenvyaml': | ||
| 'The module {module} does not contain an env.yaml file. This module will be skipped.', | ||
| 'compose.novalidmodules': 'No valid modules were found. No environments will be created.', | ||
|
|
@@ -814,6 +823,7 @@ | |
| 'deploy.group_suffix': 'group suffix', | ||
| 'deploy.source': 'source of code to deploy directly; example source_location/repo/branch', | ||
| 'deploy.process': 'enable preprocessing of the application version', | ||
| 'deploy.archive': 'directory or ZIP file containing application version source code', | ||
|
|
||
| 'platformevents.version': 'version to retrieve events for', | ||
| 'events.follow': 'wait and continue to print events as they come', | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor thing but the description starts off by saying this is the environment name change but I think that's the other PR