diff --git a/providers/openfeature-provider-flagd/README.md b/providers/openfeature-provider-flagd/README.md index 9cbb865e..fbade51f 100644 --- a/providers/openfeature-provider-flagd/README.md +++ b/providers/openfeature-provider-flagd/README.md @@ -81,7 +81,7 @@ The default options can be defined in the FlagdProvider constructor. |--------------------------|--------------------------------|----------------------------|-------------------------------|---------------------| | resolver_type | FLAGD_RESOLVER | enum - `rpc`, `in-process` | rpc | | | host | FLAGD_HOST | str | localhost | rpc & in-process | -| port | FLAGD_PORT | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | +| port | FLAGD_SYNC_PORT (in-process), FLAGD_PORT (rpc or fallback) | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | | tls | FLAGD_TLS | bool | false | rpc & in-process | | cert_path | FLAGD_SERVER_CERT_PATH | String | null | rpc & in-process | | deadline | FLAGD_DEADLINE_MS | int | 500 | rpc & in-process | diff --git a/providers/openfeature-provider-flagd/openfeature/test-harness b/providers/openfeature-provider-flagd/openfeature/test-harness index b507289c..ff2fbe6c 160000 --- a/providers/openfeature-provider-flagd/openfeature/test-harness +++ b/providers/openfeature-provider-flagd/openfeature/test-harness @@ -1 +1 @@ -Subproject commit b507289c45fca9c2d312c7231929e5b95eae62bb +Subproject commit ff2fbe6c6584953cb2753ae9188d1cee14f7f57f diff --git a/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/config.py b/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/config.py index b0da3264..5834004e 100644 --- a/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/config.py +++ b/providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/config.py @@ -81,7 +81,7 @@ def env_or_default( @dataclasses.dataclass class Config: - def __init__( # noqa: PLR0913 + def __init__( # noqa: PLR0913, PLR0915 self, host: str | None = None, port: int | None = None, @@ -166,25 +166,29 @@ def __init__( # noqa: PLR0913 else resolver ) - default_port = ( - DEFAULT_PORT_RPC - if self.resolver is ResolverType.RPC - else DEFAULT_PORT_IN_PROCESS - ) - - self.port: int = ( - int(env_or_default(ENV_VAR_PORT, default_port, cast=int)) - if port is None - else port - ) - - self.port = ( - int(env_or_default(ENV_VAR_SYNC_PORT, self.port, cast=int)) - if sync_port is None and port is None - else sync_port - if sync_port is not None - else self.port - ) + # Port configuration with FLAGD_SYNC_PORT support for in-process mode + if port is None: + is_rpc = self.resolver is ResolverType.RPC + # Use FLAGD_SYNC_PORT for in-process/file if set, otherwise fallback to FLAGD_PORT + use_sync = not is_rpc and os.environ.get(ENV_VAR_SYNC_PORT) + self.port = int( + env_or_default( + ENV_VAR_SYNC_PORT if use_sync else ENV_VAR_PORT, + DEFAULT_PORT_RPC if is_rpc else DEFAULT_PORT_IN_PROCESS, + cast=int, + ) + ) + else: + self.port = port + + if sync_port is not None: + self.port = sync_port + elif ( + port is None + and self.resolver is not ResolverType.RPC + and os.environ.get(ENV_VAR_SYNC_PORT) is not None + ): + self.port = int(env_or_default(ENV_VAR_SYNC_PORT, self.port, cast=int)) self.offline_flag_source_path = ( env_or_default( diff --git a/providers/openfeature-provider-flagd/tests/test_config.py b/providers/openfeature-provider-flagd/tests/test_config.py index 510009d0..a671f8c1 100644 --- a/providers/openfeature-provider-flagd/tests/test_config.py +++ b/providers/openfeature-provider-flagd/tests/test_config.py @@ -23,6 +23,7 @@ ENV_VAR_PORT, ENV_VAR_RETRY_BACKOFF_MS, ENV_VAR_STREAM_DEADLINE_MS, + ENV_VAR_SYNC_PORT, ENV_VAR_TLS, CacheType, Config, @@ -147,3 +148,56 @@ def test_uses_arguments_over_environments_and_defaults(monkeypatch, resolver_typ assert config.retry_backoff_ms == retry_backoff assert config.stream_deadline_ms == stream_deadline assert config.tls is tls + + +def test_in_process_uses_sync_port(monkeypatch): + """Test that FLAGD_SYNC_PORT is used for in-process mode when set.""" + sync_port = 9999 + monkeypatch.setenv(ENV_VAR_SYNC_PORT, str(sync_port)) + + config = Config(resolver=ResolverType.IN_PROCESS) + assert config.port == sync_port + assert config.resolver == ResolverType.IN_PROCESS + + +def test_in_process_fallback_to_port(monkeypatch): + """Test that FLAGD_PORT is used as fallback when FLAGD_SYNC_PORT is not set.""" + port = 7777 + monkeypatch.setenv(ENV_VAR_PORT, str(port)) + + config = Config(resolver=ResolverType.IN_PROCESS) + assert config.port == port + assert config.resolver == ResolverType.IN_PROCESS + + +def test_in_process_sync_port_priority(monkeypatch): + """Test that FLAGD_SYNC_PORT takes priority over FLAGD_PORT when both are set.""" + sync_port = 9999 + port = 7777 + monkeypatch.setenv(ENV_VAR_SYNC_PORT, str(sync_port)) + monkeypatch.setenv(ENV_VAR_PORT, str(port)) + + config = Config(resolver=ResolverType.IN_PROCESS) + assert config.port == sync_port # FLAGD_SYNC_PORT should win + assert config.resolver == ResolverType.IN_PROCESS + + +def test_rpc_ignores_sync_port(monkeypatch): + """Test that RPC mode uses FLAGD_PORT and ignores FLAGD_SYNC_PORT.""" + sync_port = 9999 + port = 7777 + monkeypatch.setenv(ENV_VAR_SYNC_PORT, str(sync_port)) + monkeypatch.setenv(ENV_VAR_PORT, str(port)) + + config = Config(resolver=ResolverType.RPC) + assert config.port == port # RPC should use FLAGD_PORT + assert config.resolver == ResolverType.RPC + + +def test_in_process_default_port_when_no_env_var(monkeypatch): + """Test that in-process mode uses default port when neither env var is set.""" + monkeypatch.delenv(ENV_VAR_SYNC_PORT, raising=False) + monkeypatch.delenv(ENV_VAR_PORT, raising=False) + config = Config(resolver=ResolverType.IN_PROCESS) + assert config.port == DEFAULT_PORT_IN_PROCESS + assert config.resolver == ResolverType.IN_PROCESS