Skip to content

Commit

Permalink
fix(vault): add support for HashiCorp Vault container (#366)
Browse files Browse the repository at this point in the history
Add support for a Vault container.
  • Loading branch information
f4z3r authored Apr 5, 2024
1 parent 9a89748 commit 1326278
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 1 deletion.
1 change: 1 addition & 0 deletions index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ testcontainers-python facilitates the use of Docker containers for functional an
modules/redis/README
modules/registry/README
modules/selenium/README
modules/vault/README
modules/weaviate/README

Getting Started
Expand Down
2 changes: 2 additions & 0 deletions modules/vault/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.. autoclass:: testcontainers.vault.VaultContainer
.. title:: testcontainers.vault.VaultContainer
74 changes: 74 additions & 0 deletions modules/vault/testcontainers/vault/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is 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 http.client import HTTPException
from urllib.request import urlopen

from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_container_is_ready


class VaultContainer(DockerContainer):
"""
Vault container.
Example:
.. doctest::
>>> from testcontainers.vault import VaultContainer
>>> import hvac
>>> with VaultContainer("hashicorp/vault:1.16.1") as vault_container:
... connection_url = vault_container.get_connection_url()
... client = hvac.Client(url=connection_url, token=vault_container.root_token)
... assert client.is_authenticated()
... # use root client to perform desired actions, e.g.
... policies = client.sys.list_acl_policies()
"""

def __init__(
self,
image: str = "hashicorp/vault:latest",
port: int = 8200,
root_token: str = "toor",
**kwargs,
) -> None:
super().__init__(image, **kwargs)
self.port = port
self.root_token = root_token
self.with_exposed_ports(self.port)
self.with_env("VAULT_DEV_ROOT_TOKEN_ID", self.root_token)

def get_connection_url(self) -> str:
"""
Get the connection URL used to connect to the Vault container.
Returns:
str: The address to connect to.
"""
host_ip = self.get_container_host_ip()
exposed_port = self.get_exposed_port(self.port)
return f"http://{host_ip}:{exposed_port}"

@wait_container_is_ready(HTTPException)
def _healthcheck(self) -> None:
url = f"{self.get_connection_url()}/v1/sys/health"
with urlopen(url) as res:
if res.status > 299:
raise HTTPException()

def start(self) -> "VaultContainer":
super().start()
self._healthcheck()
return self
41 changes: 41 additions & 0 deletions modules/vault/tests/test_vault.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import hvac
from testcontainers.vault import VaultContainer


def test_docker_run_vault():
config = VaultContainer("hashicorp/vault:1.16.1")
with config as vault:
url = vault.get_connection_url()
client = hvac.Client(url=url)
status = client.sys.read_health_status()
assert status.status_code == 200


def test_docker_run_vault_act_as_root():
config = VaultContainer("hashicorp/vault:1.16.1")
with config as vault:
url = vault.get_connection_url()
client = hvac.Client(url=url, token=vault.root_token)
assert client.is_authenticated()
assert client.sys.is_initialized()
assert not client.sys.is_sealed()

client.sys.enable_secrets_engine(
backend_type="kv",
path="secrets",
config={
"version": "2",
},
)
client.secrets.kv.v2.create_or_update_secret(
path="my-secret",
mount_point="secrets",
secret={
"pssst": "this is secret",
},
)
resp = client.secrets.kv.v2.read_secret(
path="my-secret",
mount_point="secrets",
)
assert resp["data"]["data"]["pssst"] == "this is secret"
20 changes: 19 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ packages = [
{ include = "testcontainers", from = "modules/redis" },
{ include = "testcontainers", from = "modules/registry" },
{ include = "testcontainers", from = "modules/selenium" },
{ include = "testcontainers", from = "modules/vault" },
{ include = "testcontainers", from = "modules/weaviate" }
]

Expand Down Expand Up @@ -125,6 +126,7 @@ rabbitmq = ["pika"]
redis = ["redis"]
registry = ["bcrypt"]
selenium = ["selenium"]
vault = []
weaviate = ["weaviate-client"]
chroma = ["chromadb-client"]

Expand All @@ -144,6 +146,7 @@ psycopg = "*"
cassandra-driver = "*"
pytest-asyncio = "0.23.5"
kafka-python-ng = "^2.2.0"
hvac = "*"

[[tool.poetry.source]]
name = "PyPI"
Expand Down Expand Up @@ -262,6 +265,7 @@ mypy_path = [
# "modules/rabbitmq",
# "modules/redis",
# "modules/selenium"
# "modules/vault"
# "modules/weaviate"
]
enable_error_code = [
Expand Down

0 comments on commit 1326278

Please sign in to comment.