Repository navigation
v1: per-deployment country allowlist and release stage, add DE on dev - #398
Conversation
… v1-deployment-stage-filter
There was a problem hiding this comment.
Ah, I see now what's going on with this stage now: it's basically an implementation of feature flags.
It feels to me at first pass that this could be simplified by not having the separately configured routers and stages, but rather having both of these things simply as feature flags, and every route is tagged or gated behind the appropriate set of flags. So, the environment variable would become FEATURE_FLAGS and it would be set to e.g. "SITES,GB,DEV", and that would only enable the routes that are tagged with all of the above.
Some pseudocode example:
def feature_flags(*flags: str):
def decorator(func: Callable) -> Callable:
func._required_flags = flags
return func
return decorator
@router.get("/blah")
@feature_flags("GB", "DEV")
def wow_check_out_this_route():
return ...
# ... in the lifecycle ...
app.router.routes = [
route for route in app.router.routes
if not hasattr(route, "endpoint") or
all(flag in ACTIVE_FLAGS for flag in getattr(route.endpoint, "_required_flags", []))
]ACTIVE_FLAGS coming from the environment. Seems a bit cleaner to me than two different implementations of the same sort of idea. (And yes, I know I set up the separate routers in the first place! I should have used flags from the start!)
|
@devsjc I agree, the routers def feels like a clean cut feature flags situation – in hindsight! It was much more flexible than not having it before, so harder to see ahead of time if that makes you feel any better... I can see the consolidation angle on this suggested approach, but I do feel it loses some of the rigour here – the typings mostly... if countries are a specific limiting factor on a deployment, we can validate on boot, and not accidentally think we're un-gating something when we're not; then the countries all live in path params rather than the route themselves, so you'd have dynamic validation functions within the handlers regardless – The other thing route filtering doesn't reach is everything downstream of the spec that isn't a route: Also, obv this would be user error, but Gating on sites with a decorator fits better I reckon, that is a whole set of routes, and I'm not sure exactly how that will be authorised at a country level yet (read:sites vs. read:sites-gb) but cross that bridge. 😁 I get that this doesn't make it super straightforward to see exactly what an environment exposes in one place, but if they are configuring different things in slightly different ways, I think keeping these configs close but separate feels better. Rather than a single feature flag config string, I'm tempted to do kind of the inverse: keep separate, typed env vars, but make the entry point clearer, with a set function like But I think this would be a separate abstraction/Issue/PR as it wouldn't change the env vars, although I'll strip out the duplicate variable declaration here as it doesn't actually make sense in the same way as other config vars, and I'll make the environment names match TF to leave that coupled to |
|
I hadn't considered the dynamic appearance requirements of some of the routes according to a user's allowed scopes. I guess it depends on desired behaviour - if, as you say, you want a request for a country that doesn't exist to say "hey that country doesn't exist" as opposed to just returning nothing (mentioned to "look like a DP error" in the PR, but I don't think returning nothing for a non-existant country is truly looking like an error), then it makes sense to do what you've done and have it explicit and verifiable. Also, you're right in that the flags are prone to input error. As for the parsing of flags, that already is being done by the HOCON file, it is just missing the validation as mentioned (though that is in VALID_COUNTRY_CODES etc). But Happy to go with your solution, thanks for explaining. |
Pull Request
Description
Each v1 deployment can now say which countries it serves, and dev can show countries and models that prod doesn't. It's the groundwork for EU and India running from the same image, and it puts Germany 🇩🇪 on dev.
Countries per deployment.
V1_COUNTRIESis a comma-separated allowlist, e.g.GB,NL. The full catalogue incountry_config.pyis nowALL_COUNTRIES, andCOUNTRIESis what this deployment serves. Everything already readsCOUNTRIES(the OpenAPI enums, the path validation,/countries,/region-typesand cache warming), so they all narrow with it. A request for a country the deployment doesn't serve is a 422, and/v1/docsonly lists the countries it serves.Dev-only entries
Countries, region types and models can be tagged
stage="dev". WithV1_STAGE=prod, the default, they're hidden. WithV1_STAGE=dev, everything is served. Promoting something to prod is a one-line PR, plus adding the country to prod'sV1_COUNTRIESif it's a new country.Misconfigured config startup check
The server won't start on an unknown country code, an unknown stage, or a region type whose default model is hidden at that stage. An empty
/countrieswould look like a DP outage. The startup log shows what was selected:v1 serving countries ['DE', 'GB', 'NL'] at stage 'dev'.Both vars are read from the environment when
country_configis imported, because the OpenAPI enums are built at import time. They're also inserver.conflike every other setting, and startup fails if the two disagree, so a default can't be changed in one place only.Germany, dev only.
National plus the four TSOs (50Hertz, Amprion, TenneT, TransnetBW) as region type
tso, with a 15-minute time step,entsoe_defor observed generation, and theread:descope. Six models, named after their inputs like GB and NL:model_nameecmwf_mo_pv_sat(default)de_ecmwf_pv_mo_satecmwf_pvde_ecmwf_pvecmwfde_ecmwf_onlymode_mo_onlysat_8hde_sat_onlypvde_pv_onlyThe adjuster is only available nationally. The DP has no
_adjustforecasts for the TSOs, soadjustedis ignored there, like GBgsp. For now, every model is listed for every region type; we'll remove the ones that don't apply once it's clearer which ones run where.Needs the infra PR to set both vars explicitly in Terraform:
V1_COUNTRIESV1_STAGEGB,NL,DEdevGB,NLprodTip
The two PRs can be merged in either order. Nothing reads the vars before this is deployed, and without them this serves GB and NL by default at
prod, as per usual.This isn't access control; which data a deployment can reach is still decided by the DP it points at, and the
read:*scopes still determine who can see what.Testing
V1_COUNTRIES/V1_STAGEcombinations. Each checks the OpenAPI country enum and which countries return 200 or 422, and that bad values stop startup.entsoe_degeneration, and the TSO snapshot. Also ran it through the new UI locally.Checklist: