Skip to content
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

feat: add option --container-host to commands local start-api, local start-lambda and local invoke #2700

Merged
merged 13 commits into from
Mar 23, 2021

Conversation

xazhao
Copy link
Contributor

@xazhao xazhao commented Mar 10, 2021

Add an option --container-host to commands local start-api, local start-lambda and local invoke. Customers will be able to specify the host of local emulated containers to support more use cases.

Which issue(s) does this change fix?

#2492 #2436

Why is this change necessary?

Some customers are having issues to run sam local commands within containers.

How does it address the issue?

After some investigation, this line is the root cause that SAM CLI can not be run within containers. The localhost can not be recognized by the system when running in a container. With the new option --container-host, customers will be able to specify host of the local emulated container (e.g. host.docker.internal in Docker).
If the new option is not set, localhost will be used as default so it won't have impact on current use cases.

What side effects does this change have?

Checklist

  • Add input/output type hints to new functions/methods
  • Write design document (Do I need to write a design document?)
  • Write unit tests
  • Write/update functional tests
  • Write/update integration tests
    I didn't write integration tests for this feature. Basically the new option only make a small change to the URL of the container and unit tests show the correct URL is being called. It's very complicated to write integration test to simulate real use case since the feature will only be used when running in a container. Therefore I believe unit tests are sufficient for this change.
  • make pr passes
  • make update-reproducible-reqs if dependencies were changed
  • Write documentation

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@xazhao xazhao marked this pull request as draft March 11, 2021 00:02
@aahung
Copy link
Contributor

aahung commented Mar 11, 2021

I have a hard time coming up with a reasonable cli option name for this.

I wonder if this is the approach we should go. What are the possible values customers might specify using --localhost? If there are only 1 possibility other than localhost (host.docker.internal), maybe we can get away with a boolean flag instead of a string cli argument, something like --listen-to-docker-internal-localhost-hostname (way too verbose of course).

Or even further, we can detect whether host.docker.internal is resolvable and use it by default (we need to know what's the side effect).

@xazhao
Copy link
Contributor Author

xazhao commented Mar 11, 2021

As far as I know, docker.for.mac.localhost is another possible value. I think host.docker.internal would cover most cases though. It's a trade off between ease of use vs universal. I'm just thinking if there is another value that we don't know currently, we need to make this change again. So letting user specify the container host seems better to me. Let me know your opinion.

@hoffa
Copy link
Contributor

hoffa commented Mar 11, 2021

All communication with the Docker daemon happens through socket, and technically the listening end (hence the containers started by the daemon) could be anywhere, so the address the containers are listening on could also be anywhere.

@vitorfec
Copy link

All communication with the Docker daemon happens through socket, and technically the listening end (hence the containers started by the daemon) could be anywhere, so the address the containers are listening on could also be anywhere.

This is exactly my problem. I have the daemon on a different machine and need to specify that as the host

@hoffa hoffa changed the title WIP Feat:Add local start-api option --localhost to make it run in Docker WIP feat: add local start-api option --localhost to make it run in Docker Mar 11, 2021
@xazhao xazhao changed the title WIP feat: add local start-api option --localhost to make it run in Docker WIP feat: Add option --container-host to commands local start-api, local start-lambda and local invoke Mar 17, 2021
click.option(
"--container-host",
default="localhost",
help="Host of locally emulated Lambda container",
Copy link
Contributor

Choose a reason for hiding this comment

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

This help text should be more verbose and explain what are potential options, and when it needs to be specified. Looking at this from the user perspective, we want this to be clear/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! I'm thinking Host of locally emulated Lambda container. This option can be used for running SAM CLI in containers e.g. If you want to run SAM CLI in a Docker container, use this option with host.docker.internal. For SAM CLI running on your local machine, this option is not needed. This should be clear for customers who want to use this option. Let me know your thought.

Copy link
Contributor

Choose a reason for hiding this comment

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

host.docker.internal works in some situations such as for macOS, but doesn't unconditionally resolve to the actual machine running Docker out of the box. See docker/for-linux#264

Even if SAM CLI is running locally (not inside a container), the Docker daemon could be running remotely (see e.g. #2700 (comment)), requiring this argument.

Mentioning host.docker.internal is probably fine, but the main point is that this option is useful when the container ports aren't exposed on SAM CLI's localhost, regardless of where SAM CLI may run.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes you're right. The above message could be a little misleading for some use cases. I will try to make the help text more general with a detailed example like host.docker.internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Host of locally emulated Lambda container. This option can be used when the container ports aren't exposed on SAM CLI's localhost. e.g. If you want to run SAM CLI in a Docker container on Mac OS, use this option with host.docker.internal.
How is this help text?

Copy link
Contributor

Choose a reason for hiding this comment

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

I like it! I'd maybe amend a little bit to something like this:

Host of locally emulated Lambda container. This option is useful when the container ports aren't exposed on the same host SAM CLI runs on. For example, if you want to run SAM CLI in a Docker container on macOS, use this option with host.docker.internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Talked to Chris and we feel this looks better for customers to understand and it's pretty clear too:

Host of locally emulated Lambda container. This option is useful when the containers run on a different host than SAM CLI. For example, if you want to run SAM CLI in a Docker container on macOS, use this option with host.docker.internal

@xazhao xazhao marked this pull request as ready for review March 17, 2021 21:25
@hoffa hoffa changed the title WIP feat: Add option --container-host to commands local start-api, local start-lambda and local invoke feat: add option --container-host to commands local start-api, local start-lambda and local invoke Mar 17, 2021
Copy link
Contributor

@aahung aahung left a comment

Choose a reason for hiding this comment

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

It seems container_host can never be None because it has a default value in click. Therefore, the Optional for this might be misleading.

@@ -71,6 +72,7 @@ def __init__(
:param docker_client: Optional, a docker client to replace the default one loaded from env
:param container_opts: Optional, a dictionary containing the container options
:param additional_volumes: Optional list of additional volumes
:param string container_host: Optional. Host of locally emulated Lambda container
Copy link
Contributor

Choose a reason for hiding this comment

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

this cannot be optional anymore, because if customers do not specify a value, click will use the default value localhost.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same reason as my another comment below.

@@ -55,6 +55,7 @@ def __init__(
docker_client=None,
container_opts=None,
additional_volumes=None,
container_host=None,
Copy link
Contributor

Choose a reason for hiding this comment

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

same here, this is not optional

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_init_ here is also used in another its child class LambdaBuildContainer. The new option is not used in that class so I make it as optional.

Copy link
Contributor

Choose a reason for hiding this comment

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

if this is used by other classes (i'm assuming those other classes are instantiated outside of sam local suite, is it sam build?) , should we set a default value? this can get overridden when the actual option is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I know, container_host is only used in SAM LOCAL suite when one of its child class is instantiated. So whether or not setting the default value here makes no difference. If we could potentially expand the use of new option outside of sam local suite, then yes we should set default to localhost here. Considering there is no negative impact setting the default value, I will add the default value to the option here.

@@ -55,6 +55,7 @@ def __init__(
docker_client=None,
container_opts=None,
additional_volumes=None,
container_host=None,
Copy link
Contributor

Choose a reason for hiding this comment

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

if this is used by other classes (i'm assuming those other classes are instantiated outside of sam local suite, is it sam build?) , should we set a default value? this can get overridden when the actual option is used.

Copy link
Contributor

@aahung aahung left a comment

Choose a reason for hiding this comment

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

Although we store localhost (the default value) in two different places, it is acceptable to me due to it is very unlikely to change. Thanks for addressing the comments.

Copy link
Contributor

@hoffa hoffa left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

Copy link
Contributor

@mndeveci mndeveci left a comment

Choose a reason for hiding this comment

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

Great feature, thanks for the implementation 🎉

Can we also add some integration test cases to validate this new parameter as well?

@@ -43,6 +43,7 @@ def __init__(
aws_region: Optional[str] = None,
env_vars_values: Optional[Dict[Any, Any]] = None,
debug_context: Optional[DebugContext] = None,
container_host: Optional[str] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since there is a default value for the parameter, would this param be None anytime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This param won't be None in the current use case. The reasons I set it to None:

  • I'd like to have minimum impact on the current use case. If this class is initialized somewhere else, we shouldn't assume it's localhost unless it's passed from cli.
  • Match to the overall style.

)
),
click.option(
"--container-host",
Copy link
Contributor

Choose a reason for hiding this comment

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

For any security reasons, do we need to validate if this option valid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point. Let me check on this and get back to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For security reasons, there is nothing we can validate. There is a use case that docker daemon is running on a different host and the customer wants to use that host. We won't be able to understand if it's a good host provided by customer or just a bad actor.

@xazhao
Copy link
Contributor Author

xazhao commented Mar 23, 2021

For the integration test, please see my explanation in the summary.

I didn't write integration tests for this feature. Basically the new option only make a small change to the URL of the container and unit tests show the correct URL is being called. It's very complicated to write integration test to simulate real use case since the feature will only be used when running in a container. Therefore I believe unit tests are sufficient for this change.

@hanneswidrig
Copy link

Why did this get reverted in master?

@xazhao
Copy link
Contributor Author

xazhao commented Apr 12, 2021

@hanneswidrig We found a bug when using --container-host with warm containers. We're preparing the PR fixing the bug. Also --container-host doesn't work on remote Docker host, we want to support that use case as well.

moelasmar pushed a commit to moelasmar/aws-sam-cli that referenced this pull request Jul 1, 2021
…, local start-lambda and local invoke (aws#2700)" (aws#2794)

This reverts commit e653fe2.

Co-authored-by: Xia Zhao <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants