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

chore: move host-config and endpoint settings to a specific modifiers #633

Merged
merged 41 commits into from
Feb 16, 2023

Conversation

mdelapenya
Copy link
Collaborator

@mdelapenya mdelapenya commented Nov 22, 2022

What does this PR do?

This PR addresses the need of separating concerns when creating a container, as we want to distinguish between basic and advanced settings.

With basic, we mean settings that are used by the vast majority of our users, exposed as part of the public API of Testcontainers for Go. And with advanced, we mean settings that should still be still available in the public API, but in a hidden manner.

Therefore, we are adding the concept of a Modifier to the ContainerRequest: a user will be able to define what advanced configurations should be processed before a container is created.

The advanced settings that we have identified at this moment are those that are related to 3 elements:

  1. the container.Config Docker type
  2. the container.HostConfig Docker type
  3. the network.EndpointSettings

Therefore, we have defined three modifiers: ConfigModifier, HostConfigModifier and EndpointSettingsModifier.

From Docker docs:

// HostConfig the non-portable Config structure of a container.
// Here, "non-portable" means "dependent of the host we are running on".
// Portable information *should* appear in Config.

On the contrary, we chose basic settings to be related to the portable information of a container, under the container.Config Docker type:

// Config contains the configuration data about a container.
// It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
// All fields added to this struct must be marked `omitempty` to keep getting
// predictable hashes from the old `v1Compatibility` configuration.

The technical implementation of this PR comes with:

  • we have created three new fields in the ContainerRequest that holds a function to pre-configure advanced settings for the container (hostConfig and endpointSettings): ConfigModifier, HostConfigModifier and EndpointSettingsModifier. These fields in the request are part of the public API of the container request.
  • the Modifiers functions are executed before the container is created using the Docker client.
  • the Modifiers function allow users modify the config, hostConfig and endpointSettings Docker types when they request a container, handy for advances use cases.
  • the fields that were present in the ContainerRequest related to the HostConfig, and the EndpointSettings have been Deprecated in favor of the different Modifiers.
  • the fields that relate to the "basic" settings, covered by the Config Docker type, are still present in the container request. The modifier will take precedence, as we are going to merge the struct in the modifier with the already created one (from the container request).
  • the library will internally check if a request includes each Modifier, and provide a default hook including the deprecated fields if it's not present in the request. This way we are keeping backwards compatibility until we remove those fields.
  • we have updated the existing tests, using the Modifiers when needed, removing the deprecated fields from the tests.
  • we have added unit tests for the preCreateModifierHook, to verify that all the fields in play are properly set.

Why is it important?

We want to allow users configure their containers, but helping them not to commit mistakes caused by a wrong configuration. If the public API directly exposes Docker types in a way that they are easy to consume, a user trying to configure a container with basic settings could be tempted to use advanced setting.

More info about this can be found in the #628 discussion.

Another benefit that comes here is reducing the size of the container request: we do not need to add fields to the public API to map a Docker type: anything can be modified with the Modifiers. We should provide a simple public API, but for advanced use cases, the modifiers assist.

Related issues

How to test this PR

Running the tests would not represent a change in the previous behavior.

Another test could be using a custom project that replaces the testcontainers-go dependency for the one containing this PR in the local workspace. The dependent project must compile and its tests must pass.

@mdelapenya mdelapenya added the enhancement New feature or request label Nov 22, 2022
@mdelapenya mdelapenya self-assigned this Nov 22, 2022
@mdelapenya mdelapenya requested a review from a team November 22, 2022 11:38
Copy link
Member

@kiview kiview left a comment

Choose a reason for hiding this comment

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

I tried to match the levels against the current Java implementations. Some are for sure up to discussion, for some we know about their usage, especially in the context of container modules.

Every container module is a direct use case.

container.go Outdated Show resolved Hide resolved
container.go Outdated Show resolved Hide resolved
container.go Outdated Show resolved Hide resolved
container.go Outdated
@@ -101,28 +102,29 @@ type ContainerRequest struct {
Cmd []string
Labels map[string]string
Mounts ContainerMounts
Tmpfs map[string]string
Tmpfs map[string]string // Deprecated: Use PreCreationHook instead
RegistryCred string
WaitingFor wait.Strategy
Name string // for specifying container name
Copy link
Member

Choose a reason for hiding this comment

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

I would deprecate this, risk for collisions and generally not needed in the context of TC usage.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed. But I'd do it as part of a new PR, to make it even clearer that passing a name could be seen as an antipattern from testing standpoint

container.go Outdated
@@ -101,28 +102,29 @@ type ContainerRequest struct {
Cmd []string
Labels map[string]string
Mounts ContainerMounts
Tmpfs map[string]string
Tmpfs map[string]string // Deprecated: Use PreCreationHook instead
RegistryCred string
WaitingFor wait.Strategy
Name string // for specifying container name
Hostname string
Copy link
Member

Choose a reason for hiding this comment

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

We don't have this in 1st level in Java. What are use cases?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ditto: used in one single test verifying that the hostname was changed.

container.go Outdated
Privileged bool // Deprecated: Use PreCreationHook instead. For starting privileged container
Networks []string // for specifying network names
NetworkAliases map[string][]string // for specifying network aliases
NetworkMode container.NetworkMode // Deprecated: Use PreCreationHook instead
Copy link
Member

Choose a reason for hiding this comment

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

We have this in 1st level, but I think it is not appropriate there.

container.go Outdated
NetworkMode container.NetworkMode // Deprecated: Use PreCreationHook instead
Resources container.Resources // Deprecated: Use PreCreationHook instead
Files []ContainerFile // files which will be copied when container starts
User string // for specifying uid:gid
Copy link
Member

Choose a reason for hiding this comment

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

Not in 1st level in Java, are there good use cases for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This User is not used at all by the library, only in a test that verifies that running id -u returns the defined user. I'd use a follow-up PR to discuss whether a field is actually in use or not.

container.go Outdated Show resolved Hide resolved
@mdelapenya mdelapenya changed the title chore: move host-config settings to a pre-create hook chore: move host-config settings to a pre-create modifier Nov 22, 2022
* main:
  Add toxiproxy example (testcontainers#643)
  Add spanner example (testcontainers#642)
  chore: sync governance files (testcontainers#641)
  Add pubsub example (testcontainers#640)
  chore: adjust generator for the docs site (testcontainers#639)
  Add datastore example (testcontainers#638)
  Add firestore example (testcontainers#637)
  fix: avoid panics when checking container state and container.raw is nil (#635)
  feat: provide a tool to generate examples from code (#618)
  chore: bump version in mkdocs (#630)
  docs: remove code snippets from main README (#631)
  docs: document replace directive for Docker Compose (#632)
* main: (44 commits)
  feat: support passing registry credentials to the reaper (testcontainers#647)
  fix: close response body in http strategy (testcontainers#718)
  chore: move e2e module to postgres example module (testcontainers#717)
  chore: bump containerd transitive dep in examples (testcontainers#715)
  chore(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14 (testcontainers#703)
  chore(deps): bump github.com/compose-spec/compose-go in /modules/compose (testcontainers#710)
  chore: bump testcontainers-go to 0.17.0 in examples (testcontainers#714)
  chore(deps): bump github.com/docker/compose/v2 in /modules/compose (testcontainers#711)
  chore: support running MySQL compose in ARM (testcontainers#712)
  chore: simplify compose replace directives (testcontainers#713)
  chore: add compose module to dependabot (testcontainers#709)
  chore: move compose code to a separate module (testcontainers#650)
  docs: refine onboarding process with quickstart guide (testcontainers#706)
  chore: move redis-specific tests to the example module (testcontainers#701)
  chore: bump transitive dependencies (#527)
  chore: reduce concurrent builds (testcontainers#702)
  chore: add mysql example (testcontainers#700)
  chore(deps): bump google.golang.org/api from 0.104.0 to 0.105.0 (testcontainers#699)
  chore(deps): bump google.golang.org/api in /examples/firestore (testcontainers#683)
  chore(deps): bump cloud.google.com/go/spanner in /examples/spanner (testcontainers#688)
  ...
@netlify
Copy link

netlify bot commented Jan 4, 2023

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 43998c8
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-go/deploys/63eccc214cc29700070585c0
😎 Deploy Preview https://deploy-preview-633--testcontainers-go.netlify.app/features/creating_container
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@mdelapenya mdelapenya changed the title chore: move host-config settings to a pre-create modifier chore: move host-config and endpoint settings to a specific modifiers Jan 4, 2023
@mdelapenya
Copy link
Collaborator Author

As discussed offline with @HofmeisterAn and @eddumelendez having another modifier for the container.Config docker type would be of interest, as then anybody will be able anything they need with advanced scenarios. At the same time we should document it for advanced settings.

@mdelapenya mdelapenya marked this pull request as ready for review February 15, 2023 12:12
@mdelapenya mdelapenya requested a review from a team as a code owner February 15, 2023 12:12
@@ -87,6 +87,17 @@ func TestIntegrationNginxLatestReturn(t *testing.T) {
}
```

### Advanced Settings

The aforementioned `GenericContainer` function and the `ContainerRequest` struct represent a straightforward manner to configure the containers, but you could need to create your containers with more advance settings regarding the config, host config and endpoint settings Docker types. For those more advance settings, _Testcontainers for Go_ offers a way to fully customise the container request and those internal Docker types. These customisations, called _modifiers_, will be applied just before the internal call to the Docker client to create the container.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
The aforementioned `GenericContainer` function and the `ContainerRequest` struct represent a straightforward manner to configure the containers, but you could need to create your containers with more advance settings regarding the config, host config and endpoint settings Docker types. For those more advance settings, _Testcontainers for Go_ offers a way to fully customise the container request and those internal Docker types. These customisations, called _modifiers_, will be applied just before the internal call to the Docker client to create the container.
The aforementioned `GenericContainer` function and the `ContainerRequest` struct represent a straightforward manner to configure the containers, but you might need to create your containers with more advance settings regarding the config, host config and endpoint settings Docker types. For those more advance settings, _Testcontainers for Go_ offers a way to fully customize the container request and access those internal Docker types. These customizations, called _modifiers_, will be applied just before the internal call to the Docker client to create the container.

<!--/codeinclude-->

!!!warning
The only special case where the modifiers are not applied last, is when there are no exposed ports in the container request and the container does not use a network mode from a container (e.g. `req.NetworkMode = container.NetworkMode("container:$CONTAINER_ID")`). In that case, _Testcontainers for Go_ will extract the ports from the underliying Docker image and export them.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
The only special case where the modifiers are not applied last, is when there are no exposed ports in the container request and the container does not use a network mode from a container (e.g. `req.NetworkMode = container.NetworkMode("container:$CONTAINER_ID")`). In that case, _Testcontainers for Go_ will extract the ports from the underliying Docker image and export them.
The only special case where the modifiers are not applied last, occurs when there are no exposed ports in the container request and the container does not use a network mode from a container (e.g. `req.NetworkMode = container.NetworkMode("container:$CONTAINER_ID")`). In that case, _Testcontainers for Go_ will implicitly extract the ports from the underlying Docker image and export them.

provider, err := NewDockerProvider()
require.Nil(t, err)

t.Run("No exposed ports", func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
t.Run("No exposed ports", func(t *testing.T) {
t.Run("No configured exposed ports should expose default port of image", func(t *testing.T) {

)
})

t.Run("No exposed ports and network mode IsContainer", func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
t.Run("No exposed ports and network mode IsContainer", func(t *testing.T) {
t.Run("No exposed ports and network mode IsContainer should not implicitly expose ports", func(t *testing.T) {

@mdelapenya mdelapenya merged commit 34481cf into testcontainers:main Feb 16, 2023
@mdelapenya mdelapenya deleted the pre-creation-hook branch February 21, 2023 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants