RFC 0005: Stages and deployment identity
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- aws, nodejs, typescript
- Ambito
- cli, cloud, infrastructure
Direzione di ricerca
Start by reading the linked RFCs 0010 and 0007 and tracing the current deploy command, deployment state key, and runtimeModules configuration. The payload names no source files or tests, so locate their entry points before estimating the implementation. Done means deployments are keyed by app, stage, and entry, generated names and ownership tags distinguish stages, and the new required config and validation behave as described.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Abstract
Notation identifies a deployment by the path of its entry file, such as infra/api.ts. The same code can therefore only be deployed one way: there is no way to deploy it as both staging and prod, and moving the entry file turns every deployed resource into an orphan. A deployment needs an identity that separates deployments of the same code and stays the same when files move, and the current one doesn't. AWS names come straight from resource ids, so two deployments of the same code in one account also clash.
We will solve this by identifying a deployment by its app, its stage and the name of its entry. The app and the entry names come from a required project config file, the stage from the deploy command, and none of them depend on where files live.
https://github.com/user-attachments/assets/cc02de84-9969-4f3f-8370-7a1abd83c502
Video transcript
When you run notation deploy, you give it the path of an entry file: the file that declares your infra. Take an orders app, whose entry file is infra slash api dot ts. Notation uses this path as the deployment ID. The deployment ID is the key for the state of the deployment: Notation's record of each resource that this deployment created, like the IAM role for the orders API.
Problem 1. Say you want a staging copy of the orders app, next to prod. You deploy the same entry file a second time. The path is the same, so the deployment ID is the same, and Notation finds the state of prod. So the second deploy does not create a staging copy. It updates prod. Today, to get a second stage, you must copy the entry file to a new path, and keep two copies of the same infra.
Problem 2. RFC 0010 will make the deployment ID matter outside of state. Under RFC 0010, the engine will make the name of each resource, and the ownership tags on each resource, from the deployment ID. The tags tell the engine which deployment made a resource. Now say you keep the state of staging in a separate store, so that staging does not update prod. Both deployments still have the ID infra slash api dot ts. Today, the staging deploy asks AWS to create a role that prod already made, and AWS refuses: the role already exists. The deploy fails loudly. But under RFC 0010, both deployments make the same role name and the same tags. The staging deploy finds the role of prod, reads the tags, and decides that the role is its own. So staging takes over the role of prod, and changes it, without any error.
Problem 3. Say you rename the entry file from infra slash api dot ts to infra slash orders dot ts. The deployment ID changes with the path, so the next deploy finds no state. Notation treats every resource as new, and creates all of them again. The old resources stay in AWS, with nothing to manage them.
All three problems have one cause, and this is the key point of the RFC: the deployment ID describes where a file lives, not what you deploy and where you deploy it. You can deploy the same code many times. And you can move the entry file, but the deployment should stay the same. So the deployment ID needs parts that do not come from the file layout.
The fix starts with a config file. Each project will have one file called notation config dot ts, and it will be required. The app field names the app, here orders-app. The entries field maps entry names to entry files. Here the entry called orders points to infra slash api dot ts. The runtime modules setting moves here from package dot json. And the default stage field is optional. Without it, every deploy must name its stage.
The deploy command will take an entry name, never a path, and a stage. So you will run notation deploy orders, with the stage flag set to prod. Notation will key the state with three parts: the app, the stage and the entry. Here, that is orders-app, prod, orders. Now, if you rename the entry file, you change one path in the config. The entry name stays orders, so the state key stays the same, and the next deploy finds its state.
Now deploy the same entry with the stage flag set to staging. The state key will be orders-app, staging, orders: a different key, so staging gets its own state, and does not update prod. Generated names and ownership tags will use the app and the stage too. The role of prod will get a generated name (orders-app-prod-orders-api-role-7f3a) that starts with orders-app and prod, and a tag for the app and a tag for the stage. The role of staging will get a name (orders-app-staging-orders-api-role-c21e) that starts with orders-app and staging, and a stage tag of staging. So the staging deploy cannot find the role of prod, and if it did, the tags would show that the role belongs to prod.
The app, the entry names and the stage end up in resource names and tags, so they must be name-safe. A stage name may use only lowercase letters, digits and hyphens, with at most 32 characters, and no hyphen at the start or end. Because the config is TypeScript, a type checks the app, the entry names and the default stage while you edit. For example, an app called Orders underscore App fails in the editor, before any deploy. A check at runtime does the same, and also checks the stage that you give on the command line.
This change has costs. Every project will need a config file, with an app and named entries, even a project with one entry. Notation will not migrate old state. State under the old path key stays where it is, so the first deploy after the change treats an existing deployment as new. And infra code cannot read the stage, so infra that differs between stages is out of scope for this RFC.
Before, the path of the entry file was the deployment ID. One entry file could have only one deployment, two deployments with the same ID could take over each other's resources, and moving the entry file lost its state. With this RFC, the app, the stage and the entry name will form the deployment ID. You will deploy one codebase as many stages, each stage will get its own names and tags, and you can move files without losing state.
Problem
The deployment ID is the entry file's project-relative path. It keys the deployment's state, and AWS names come from resource ids alone. This has three costs.
- No stages. Deploying the same code twice produces the same deployment ID, so the second deploy updates the first. Separate stages need separate entry files that duplicate the infra.
- Stages can't share an account. A Lambda's function name is its id and its role is
<id>-role, so a second stage in the same account fails with "already exists". - Moving a file loses state. Renaming
infra/api.tschanges the deployment ID, so the next deploy finds no state, creates everything again, and leaves the old resources behind.
Proposed solution
Each project will have one config file, notation.config.ts. It is required.
// notation.config.ts
import { defineConfig } from "@notation/core"
export default defineConfig({
app: "orders-app",
entries: {
orders: "infra/api.ts",
},
runtimeModules: "src/**/*.fn.ts",
defaultStage: "dev",
})
appis required. It is never derived from thepackage.jsonname.entriesmaps entry names to entry files. It is required, even for a project with a single entry.runtimeModulesmoves here frompackage.json(see 0007).defaultStageis optional. There is no built-in default: without it,--stageis required.
The CLI takes an entry name, never a path, and the stage:
# before
notation deploy infra/api.ts # deploymentId: "infra/api.ts"
# after
notation deploy orders --stage prod # state key: "orders-app:prod:orders"
- State is keyed by
<app>:<stage>:<entry>, so one project can have several entries, and moving an entry file only changes its path in the config. - AWS names are scoped by app and stage, e.g.
orders-app-prod-todosfor a Lambda with idtodos, so stages and apps can share an account.
Names
app, entry names and stage names are name-safe, because they form the state key and appear in AWS names. A stage name is [a-z0-9-], at most 32 characters, with no leading or trailing hyphen.
Because the config is TypeScript, the NameSafe template-literal type checks app, the entry names and defaultStage at compile time:
defineConfig({ app: "Orders_App", entries: { orders: "infra/api.ts" } })
// Type error: "Orders_App" is not name-safe
A runtime check backs this up, and also checks the --stage given on the command line.
AWS names
The engine names each account-unique AWS resource <app>-<stage>-<id>, plus any suffix such as -role. This covers Lambda functions, IAM roles, SQS queues, EventBridge rules and API Gateway APIs. Names that only need to be unique within a parent, such as an inline policy, are not scoped. A name written directly into an @notation/aws.iac resource config is kept as written.
When a name is longer than the service allows, the engine cuts it and appends - and 8 hex characters of a hash of the full name. The name stays unique and is the same on every deploy.
Infra code still cannot read the stage. The engine passes app and stage to resource collection, and only the naming function reads them.
Rationale
The identity of a deployment is a fact about what is being deployed and where, not about how the source is laid out. Separating the two lets the same code be deployed many times, and gives an identity that can go into cloud resource names, where a file path would leak project structure.
A config file, rather than fields in package.json, can be type-checked, so a bad name fails in the editor instead of at deploy.
Consequences
- One codebase can be deployed as several stages, into one account or several.
- AWS names change, so existing resources are replaced. Because old state is not migrated, an upgraded project creates its resources under new names and leaves the old ones unmanaged: destroy old deployments before upgrading.
- Every project needs a
notation.config.ts, with anappand named entries. - There is no backwards compatibility. State under the old path-based key is not migrated, so a deploy treats an existing deployment as new.
- Infra code cannot read the stage. Stage-specific infra is out of scope.
- Lingua principale
- TypeScript
- Stelle
- 54
- Fork
- 1
- Merge medio
- 15h 22m
- PR unite (30g)
- 6
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di notationlabs/notation
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
notationlabs/notation#48 ·
-
RFC 0012: Runtime macrosApertaenhancement
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
notationlabs/notation#58 ·
-
RFC 0011: Cycle resolutionApertaenhancement
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
notationlabs/notation#57 ·
-
enhancement
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
notationlabs/notation#56 ·
-
enhancement
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
notationlabs/notation#55 ·
Tutte le issue di notationlabs/notation
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
oxidecomputer/skepsis#102 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
voidzero-dev/void#677 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
kunchenguid/gh-axi#247 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
lidge-jun/opencodex#8101 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno