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

Add descriptions to all configuration variables #702

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
77 changes: 51 additions & 26 deletions packages/opal-client/opal_client/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,46 @@ class EngineLogFormat(str, Enum):
class OpalClientConfig(Confi):
# opa client (policy store) configuration
POLICY_STORE_TYPE = confi.enum(
"POLICY_STORE_TYPE", PolicyStoreTypes, PolicyStoreTypes.OPA
"POLICY_STORE_TYPE", PolicyStoreTypes, PolicyStoreTypes.OPA,
description="The type of policy store to use (e.g., OPA, Cedar, etc.)"
)
POLICY_STORE_URL = confi.str(
"POLICY_STORE_URL", "http://localhost:8181",
description="The URL of the policy store (e.g., OPA agent)."
)
POLICY_STORE_URL = confi.str("POLICY_STORE_URL", "http://localhost:8181")

POLICY_STORE_AUTH_TYPE = confi.enum(
"POLICY_STORE_AUTH_TYPE", PolicyStoreAuth, PolicyStoreAuth.NONE
"POLICY_STORE_AUTH_TYPE", PolicyStoreAuth, PolicyStoreAuth.NONE,
description="The authentication type to use for the policy store (e.g., NONE, TOKEN, etc.)"
)
POLICY_STORE_AUTH_TOKEN = confi.str(
"POLICY_STORE_AUTH_TOKEN",
None,
description="the authentication (bearer) token OPAL client will use to "
description="The authentication (bearer) token OPAL client will use to "
"authenticate against the policy store (i.e: OPA agent).",
)
POLICY_STORE_AUTH_OAUTH_SERVER = confi.str(
"POLICY_STORE_AUTH_OAUTH_SERVER",
None,
description="the authentication server OPAL client will use to authenticate against for retrieving the access_token.",
description="The authentication server OPAL client will use to authenticate against for retrieving the access_token.",
)
POLICY_STORE_AUTH_OAUTH_CLIENT_ID = confi.str(
"POLICY_STORE_AUTH_OAUTH_CLIENT_ID",
None,
description="the client_id OPAL will use to authenticate against the OAuth server.",
description="The client_id OPAL will use to authenticate against the OAuth server.",
)
POLICY_STORE_AUTH_OAUTH_CLIENT_SECRET = confi.str(
"POLICY_STORE_AUTH_OAUTH_CLIENT_SECRET",
None,
description="the client secret OPAL will use to authenticate against the OAuth server.",
description="The client secret OPAL will use to authenticate against the OAuth server.",
)

POLICY_STORE_CONN_RETRY: ConnRetryOptions = confi.model(
"POLICY_STORE_CONN_RETRY",
ConnRetryOptions,
# defaults are being set according to ConnRetryOptions pydantic definitions (see class)
{},
description="retry options when connecting to the policy store (i.e. the agent that handles the policy, e.g. OPA)",
description="Retry options when connecting to the policy store (i.e. the agent that handles the policy, e.g. OPA)",
)
POLICY_UPDATER_CONN_RETRY: ConnRetryOptions = confi.model(
"POLICY_UPDATER_CONN_RETRY",
Expand All @@ -65,14 +70,14 @@ class OpalClientConfig(Confi):
"attempts": 5,
"wait_time": 1,
},
description="retry options when connecting to the policy source (e.g. the policy bundle server)",
description="Retry options when connecting to the policy source (e.g. the policy bundle server)",
)

DATA_STORE_CONN_RETRY: ConnRetryOptions = confi.model(
"DATA_STORE_CONN_RETRY",
ConnRetryOptions,
None,
description="DEPTRECATED - The old confusing name for DATA_UPDATER_CONN_RETRY, kept for backwards compatibilit (for now)",
description="DEPRECATED - The old confusing name for DATA_UPDATER_CONN_RETRY, kept for backwards compatibility (for now)",
)

DATA_UPDATER_CONN_RETRY: ConnRetryOptions = confi.model(
Expand All @@ -84,7 +89,7 @@ class OpalClientConfig(Confi):
"attempts": 5,
"wait_time": 1,
},
description="retry options when connecting to the base data source (e.g. an external API server which returns data snapshot)",
description="Retry options when connecting to the base data source (e.g. an external API server which returns data snapshot)",
)

POLICY_STORE_POLICY_PATHS_TO_IGNORE = confi.list(
Expand All @@ -102,17 +107,17 @@ class OpalClientConfig(Confi):
POLICY_STORE_TLS_CLIENT_CERT = confi.str(
"POLICY_STORE_TLS_CLIENT_CERT",
None,
description="path to the client certificate used for tls authentication with the policy store",
description="Path to the client certificate used for TLS authentication with the policy store",
)
POLICY_STORE_TLS_CLIENT_KEY = confi.str(
"POLICY_STORE_TLS_CLIENT_KEY",
None,
description="path to the client key used for tls authentication with the policy store",
description="Path to the client key used for TLS authentication with the policy store",
)
POLICY_STORE_TLS_CA = confi.str(
"POLICY_STORE_TLS_CA",
None,
description="path to the file containing the ca certificate(s) used for tls authentication with the policy store",
description="Path to the file containing the CA certificate(s) used for TLS authentication with the policy store",
)

EXCLUDE_POLICY_STORE_SECRETS = confi.bool(
Expand All @@ -132,49 +137,63 @@ def load_policy_store():
# opa runner configuration (OPA can optionally be run by OPAL) ----------------

# whether or not OPAL should run OPA by itself in the same container
INLINE_OPA_ENABLED = confi.bool("INLINE_OPA_ENABLED", True)
INLINE_OPA_ENABLED = confi.bool(
"INLINE_OPA_ENABLED", True,
description="Whether or not OPAL should run OPA by itself in the same container"
)

# if inline OPA is indeed enabled, user can pass cli options
# (configuration) that affects how OPA will run
INLINE_OPA_CONFIG = confi.model(
"INLINE_OPA_CONFIG",
OpaServerOptions,
{}, # defaults are being set according to OpaServerOptions pydantic definitions (see class)
description="cli options used when running `opa run --server` inline",
description="CLI options used when running `opa run --server` inline",
)

INLINE_OPA_LOG_FORMAT: EngineLogFormat = confi.enum(
"INLINE_OPA_LOG_FORMAT", EngineLogFormat, EngineLogFormat.NONE
"INLINE_OPA_LOG_FORMAT", EngineLogFormat, EngineLogFormat.NONE,
description="The log format to use for inline OPA logs"
)

# Cedar runner configuration (Cedar-engine can optionally be run by OPAL) ----------------

# whether or not OPAL should run the Cedar agent by itself in the same container
INLINE_CEDAR_ENABLED = confi.bool("INLINE_CEDAR_ENABLED", True)
INLINE_CEDAR_ENABLED = confi.bool(
"INLINE_CEDAR_ENABLED", True,
description="Whether or not OPAL should run the Cedar agent by itself in the same container"
)

# if inline Cedar is indeed enabled, user can pass cli options
# (configuration) that affects how the agent will run
INLINE_CEDAR_CONFIG = confi.model(
"INLINE_CEDAR_CONFIG",
CedarServerOptions,
{}, # defaults are being set according to CedarServerOptions pydantic definitions (see class)
description="cli options used when running the Cedar agent inline",
description="CLI options used when running the Cedar agent inline",
)

INLINE_CEDAR_LOG_FORMAT: EngineLogFormat = confi.enum(
"INLINE_CEDAR_LOG_FORMAT", EngineLogFormat, EngineLogFormat.NONE
"INLINE_CEDAR_LOG_FORMAT", EngineLogFormat, EngineLogFormat.NONE,
description="The log format to use for inline Cedar logs"
)

# configuration for fastapi routes
ALLOWED_ORIGINS = ["*"]

# general configuration for pub/sub clients
KEEP_ALIVE_INTERVAL = confi.int("KEEP_ALIVE_INTERVAL", 0)
KEEP_ALIVE_INTERVAL = confi.int(
"KEEP_ALIVE_INTERVAL", 0,
description="The interval (in seconds) for sending keep-alive messages"
)

# Opal Server general configuration -------------------------------------------

# opal server url
SERVER_URL = confi.str("SERVER_URL", "http://localhost:7002", flags=["-s"])
SERVER_URL = confi.str(
"SERVER_URL", "http://localhost:7002", flags=["-s"],
description="The URL of the OPAL server"
)
# opal server pubsub url
OPAL_WS_ROUTE = "/ws"
SERVER_WS_URL = confi.str(
Expand All @@ -184,16 +203,18 @@ def load_policy_store():
"http", "ws"
)
),
description="The WebSocket URL of the OPAL server"
)
SERVER_PUBSUB_URL = confi.str(
"SERVER_PUBSUB_URL", confi.delay("{SERVER_WS_URL}" + f"{OPAL_WS_ROUTE}")
"SERVER_PUBSUB_URL", confi.delay("{SERVER_WS_URL}" + f"{OPAL_WS_ROUTE}"),
description="The Pub/Sub URL of the OPAL server"
)

# opal server auth token
CLIENT_TOKEN = confi.str(
"CLIENT_TOKEN",
"THIS_IS_A_DEV_SECRET",
description="opal server auth token",
description="The authentication token for the OPAL server",
flags=["-t"],
)

Expand Down Expand Up @@ -229,7 +250,7 @@ def load_policy_store():
"POLICY_SUBSCRIPTION_DIRS",
["."],
delimiter=":",
description="directories in policy repo we should subscribe to",
description="Directories in the policy repo to subscribe to for policy code (rego) modules",
)

# Data updater configuration --------------------------------------------------
Expand Down Expand Up @@ -272,6 +293,7 @@ def load_policy_store():
"headers": {"content-type": "application/json"},
"process_data": False,
},
description="Configuration for the default update callback",
)

DEFAULT_UPDATE_CALLBACKS = confi.model(
Expand Down Expand Up @@ -305,7 +327,10 @@ def load_policy_store():

OPA_HEALTH_CHECK_POLICY_PATH = "engine/healthcheck/opal.rego"

SCOPE_ID = confi.str("SCOPE_ID", "default", description="OPAL Scope ID")
SCOPE_ID = confi.str(
"SCOPE_ID", "default",
description="OPAL Scope ID"
)

STORE_BACKUP_PATH = confi.str(
"STORE_BACKUP_PATH",
Expand Down
42 changes: 31 additions & 11 deletions packages/opal-common/opal_common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class OpalCommonConfig(Confi):
PROCESS_NAME = ""
# Logging
# - Log formatting
LOG_FORMAT_INCLUDE_PID = confi.bool("LOG_FORMAT_INCLUDE_PID", False)
LOG_FORMAT_INCLUDE_PID = confi.bool(
"LOG_FORMAT_INCLUDE_PID", False, description="Include process ID in log format"
)
LOG_FORMAT = confi.str(
"LOG_FORMAT",
confi.delay(
Expand Down Expand Up @@ -106,19 +108,27 @@ class OpalCommonConfig(Confi):
# Fetching Providers
# - where to load providers from
FETCH_PROVIDER_MODULES = confi.list(
"FETCH_PROVIDER_MODULES", ["opal_common.fetcher.providers"]
"FETCH_PROVIDER_MODULES", ["opal_common.fetcher.providers"],
description="List of modules to load fetch providers from"
)

# Fetching engine
# Max number of worker tasks handling fetch events concurrently
FETCHING_WORKER_COUNT = confi.int("FETCHING_WORKER_COUNT", 6)
FETCHING_WORKER_COUNT = confi.int(
"FETCHING_WORKER_COUNT", 6, description="Max number of worker tasks handling fetch events concurrently"
)
# Time in seconds to wait on the queued fetch task.
FETCHING_CALLBACK_TIMEOUT = confi.int("FETCHING_CALLBACK_TIMEOUT", 10)
FETCHING_CALLBACK_TIMEOUT = confi.int(
"FETCHING_CALLBACK_TIMEOUT", 10, description="Time in seconds to wait on the queued fetch task"
)
# Time in seconds to wait for queuing a new task (if the queue is full)
FETCHING_ENQUEUE_TIMEOUT = confi.int("FETCHING_ENQUEUE_TIMEOUT", 10)
FETCHING_ENQUEUE_TIMEOUT = confi.int(
"FETCHING_ENQUEUE_TIMEOUT", 10, description="Time in seconds to wait for queuing a new task (if the queue is full)"
)

GIT_SSH_KEY_FILE = confi.str(
"GIT_SSH_KEY_FILE", str(Path.home() / ".ssh/opal_repo_ssh_key")
"GIT_SSH_KEY_FILE", str(Path.home() / ".ssh/opal_repo_ssh_key"),
description="Path to the SSH key file for Git"
)

# Trust self signed certificates (Advanced Usage - only affects OPAL client) -----------------------------
Expand All @@ -139,11 +149,13 @@ class OpalCommonConfig(Confi):

# security
AUTH_PUBLIC_KEY_FORMAT = confi.enum(
"AUTH_PUBLIC_KEY_FORMAT", EncryptionKeyFormat, EncryptionKeyFormat.ssh
"AUTH_PUBLIC_KEY_FORMAT", EncryptionKeyFormat, EncryptionKeyFormat.ssh,
description="Format of the public key for authentication"
)
AUTH_PUBLIC_KEY = confi.delay(
lambda AUTH_PUBLIC_KEY_FORMAT=None: confi.public_key(
"AUTH_PUBLIC_KEY", default=None, key_format=AUTH_PUBLIC_KEY_FORMAT
"AUTH_PUBLIC_KEY", default=None, key_format=AUTH_PUBLIC_KEY_FORMAT,
description="Public key for authentication"
)
)
AUTH_JWT_ALGORITHM = confi.enum(
Expand All @@ -152,15 +164,23 @@ class OpalCommonConfig(Confi):
getattr(JWTAlgorithm, "RS256"),
description="jwt algorithm, possible values: see: https://pyjwt.readthedocs.io/en/stable/algorithms.html",
)
AUTH_JWT_AUDIENCE = confi.str("AUTH_JWT_AUDIENCE", "https://api.opal.ac/v1/")
AUTH_JWT_ISSUER = confi.str("AUTH_JWT_ISSUER", f"https://opal.ac/")
AUTH_JWT_AUDIENCE = confi.str(
"AUTH_JWT_AUDIENCE", "https://api.opal.ac/v1/",
description="Audience for JWT authentication"
)
AUTH_JWT_ISSUER = confi.str(
"AUTH_JWT_ISSUER", f"https://opal.ac/",
description="Issuer for JWT authentication"
)
POLICY_REPO_POLICY_EXTENSIONS = confi.list(
"POLICY_REPO_POLICY_EXTENSIONS",
[".rego"],
description="List of extensions to serve as policy modules",
)

ENABLE_METRICS = confi.bool("ENABLE_METRICS", False)
ENABLE_METRICS = confi.bool(
"ENABLE_METRICS", False, description="Enable metrics collection"
)

# optional APM tracing with datadog
ENABLE_DATADOG_APM = confi.bool(
Expand Down
23 changes: 23 additions & 0 deletions packages/opal-common/opal_common/tests/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import unittest
from opal_common.config import opal_common_config
from opal_client.config import opal_client_config
from opal_server.config import opal_server_config

class TestConfigDescriptions(unittest.TestCase):
vishwamartur marked this conversation as resolved.
Show resolved Hide resolved
def test_opal_common_config_descriptions(self):
for name, entry in opal_common_config.entries.items():
with self.subTest(name=name):
self.assertIsNotNone(entry.description, f"{name} is missing a description")

def test_opal_client_config_descriptions(self):
for name, entry in opal_client_config.entries.items():
with self.subTest(name=name):
self.assertIsNotNone(entry.description, f"{name} is missing a description")

def test_opal_server_config_descriptions(self):
for name, entry in opal_server_config.entries.items():
with self.subTest(name=name):
self.assertIsNotNone(entry.description, f"{name} is missing a description")

if __name__ == "__main__":
unittest.main()
Loading