Code Generation (`pulumi-codegen`)
Relevant source files
The following files were used as context for generating this wiki page:
The code generation subsystem, also known as Pulumi CrossCode, is a core component of Pulumi's architecture. Its primary purpose is to enable multi-language support for a wide variety of cloud providers by translating a language-agnostic schema definition into language-specific SDKs. This allows developers to interact with cloud resources using their preferred programming languages like Go, Python, Node.js (TypeScript), and .NET (C#).
The pkg/codegen directory contains the logic for this system. It manages three main categories of output: provider SDKs, example programs, and documentation. The foundation of this system relies on shared concepts like the Pulumi Schema and Pulumi Configuration Language (PCL).
Sources: pkg/codegen/README.md:5-12
Overall Architecture
The code generation process starts with a Pulumi Schema file (schema.json or schema.yaml), which defines the resources, functions, and types for a given provider. The language-specific generators consume this schema to produce a complete, typed SDK for the target language.
This diagram illustrates the high-level flow of SDK generation.
This diagram shows the general workflow from a single Pulumi Schema to multiple language-specific SDKs.
Sources: pkg/codegen/go/gen.go, pkg/codegen/python/gen.go, pkg/codegen/nodejs/gen.go, pkg/codegen/dotnet/gen.go
Language-Specific SDK Generators
Each supported language has a dedicated generator package responsible for translating the schema into idiomatic code for that language. While the implementation details vary, they follow a similar pattern:
- Load Schema: The process begins by loading and binding the provider's schema definition.
- Build Module Context: The generator analyzes the schema to group resources, types, and functions into modules. A
modContext(or similarly named struct) is created for each module to manage its state, including names, types, and dependencies. - Generate Files: The generator iterates through the module contexts and generates source files for resources, functions, enums, and complex types.
- Generate Metadata: Finally, it creates language-specific project and dependency files like
go.mod,package.json,.csproj, andpyproject.toml.
Sources: pkg/codegen/go/gen.go:1938-1950, pkg/codegen/python/gen.go:1240-1254, pkg/codegen/nodejs/gen.go:1210-1223, pkg/codegen/dotnet/gen.go:1546-1558
Core Data Structure: modContext
The modContext (or pkgContext in Go) is a central data structure in each language generator. It holds all the necessary information to generate the code for a single module within the provider SDK.
This class diagram shows the typical structure of the context object used by the generators.
This diagram represents the common fields in the context struct across different language generators, such as the one in pkg/codegen/python/gen.go.
Sources: pkg/codegen/python/gen.go:65-85, pkg/codegen/dotnet/gen.go:150-173, pkg/codegen/go/gen.go:102-136
Go Generator
The Go generator is implemented in pkg/codegen/go/gen.go.
- Entry Point: The main function is
GeneratePackage. - Context: It uses a
pkgContextstruct to manage generation state for each Go package. - File Generation: It generates separate
.gofiles for resources, functions, enums, and input/output types. It also handles the generation ofdoc.go,init.gofor resource registration, and utility files. - Naming: It converts Pulumi module names (e.g.,
someModule) into Go package names (e.g.,somemodule). It also handles potential name collisions between types and resources. - Metadata: It creates a
go.modfile if theSupportPackoption is enabled in the package's Go language metadata. - Generics: The Go generator has a
Genericssetting inGoPackageInfowhich can benone,side-by-side, orgenerics-onlyto control the emission of generic-based SDKs.
Sources: pkg/codegen/go/gen.go:2308-2321, 2465-2495, 252-255
Python Generator
The Python generator is implemented in pkg/codegen/python/gen.go.
- Entry Point: The main function is
GeneratePackage. - Context: It uses a
modContextstruct. - File Generation: It creates an
__init__.pyfor each module, which exports the module's members. Resources, functions, and enums are generated into their own files (_enums.py,_inputs.py,outputs.py). - Naming: It converts Pulumi module names into Python-compliant module names (snake_case). It respects
moduleNameOverridesfrom the package info. - Metadata: It generates
setup.pyand, if enabled viaPyProject.Enabled, apyproject.tomlfile for package metadata and dependencies. - Input Types: A notable feature is the
InputTypessetting, which can beclassesorclasses-and-dicts, controlling whether to generateTypedDicttypes for inputs.
Sources: pkg/codegen/python/gen.go:1240-1254, 30-33, 563-570
Node.js (TypeScript) Generator
The Node.js generator is implemented in pkg/codegen/nodejs/gen.go.
- Entry Point: The main function is
GeneratePackage. - Context: It uses a
modContextstruct. - File Generation: It generates an
index.tsfile for each module that exports all its members. It also creates atypessubdirectory containinginput.ts,output.ts, andenums.ts. - Naming: It uses camelCase for file and function names and PascalCase for types and resources.
- Metadata: It generates
package.jsonwith dependencies and atsconfig.jsonfile for compilation.
Sources: pkg/codegen/nodejs/gen.go:1210-1223, 1145-1153
.NET (C#) Generator
The .NET generator is implemented in pkg/codegen/dotnet/gen.go.
- Entry Point: The main function is
GeneratePackage. - Context: It uses a
modContextstruct. - File Generation: It organizes code into namespaces. Resources, arguments (
*Args), and state (*State) types are typically generated in the same file. Input and output types are placed inInputsandOutputssub-namespaces. - Naming: It uses PascalCase for namespaces, classes, and properties, following C# conventions. It handles C# reserved keywords by prefixing them with
@. - Metadata: It generates a
.csprojfile for the project, including package references and other MSBuild properties. It also embeds apulumi-plugin.jsonfile.
Sources: pkg/codegen/dotnet/gen.go:1546-1558, 87-107, 1618-1627
Language-Specific Settings
Each language generator can be customized through a language-specific block in the Pulumi schema.
| Language | Struct | Key Settings | Source File |
|---|---|---|---|
| Go | GoPackageInfo | importBasePath, moduleToPackage, generateResourceContainerTypes, generics | pkg/codegen/go/gen.go |
| Python | PackageInfo | packageName, moduleNameOverrides, requires, pyproject | pkg/codegen/python/gen.go |
| Node.js | NodePackageInfo | packageName, moduleToPackage, dependencies, devDependencies | pkg/codegen/nodejs/gen.go |
| .NET | CSharpPackageInfo | packageName, namespaces, packageReferences, projectReferences | pkg/codegen/dotnet/gen.go |
Sources: pkg/codegen/go/gen.go, pkg/codegen/python/gen.go, pkg/codegen/nodejs/gen.go, pkg/codegen/dotnet/gen.go
Program Generation from PCL
Besides SDKs, the codegen system can also generate complete, runnable programs from the Pulumi Configuration Language (PCL). This is used by features like pulumi import and Pulumi AI to generate code from existing infrastructure or natural language prompts.
The process is handled by language-specific program generators, such as the one in pkg/codegen/nodejs/gen_program.go.
This diagram outlines the process of generating a TypeScript program from a PCL definition.
Sources: pkg/codegen/nodejs/gen_program.go:73-77, 431-443
The core of the program generator is a generator struct that walks the linearized graph of PCL nodes and emits corresponding code for each node type (Resource, ConfigVariable, LocalVariable, OutputVariable). A key challenge is handling dependencies and asynchronous operations, which sometimes requires switching the main program entry point to be async.
Sources: pkg/codegen/nodejs/gen_program.go:41-50, 110-135
CLI Documentation Generation
The codegen system is also used internally to generate documentation for the Pulumi CLI. The pkg/cmd/pulumi/markdown/gen.go file contains logic to generate Markdown files for each CLI command.
- It uses the
github.com/spf13/cobra/docpackage to generate the base Markdown. - It then post-processes the files to add YAML front matter for title, aliases, and meta descriptions, which are used by the documentation website.
- It adjusts heading levels (promoting
###to##) to ensure a consistent document structure.
Sources: pkg/cmd/pulumi/markdown/gen.go:90-128
Testing
The codegen system is rigorously tested using a baseline-driven approach. The testing framework is located in pkg/codegen/testing/test/.
- Test Driver: The
TestProgramCodegenfunction inprogram_driver.goorchestrates the tests. It iterates through a list ofProgramTeststructs, each defining a test case. - Test Case: A
ProgramTestspecifies a directory containing a PCL file (.pp) and any language-specific options, such as which languages to skip. - Execution: For each test case, the driver generates code for each target language and compares the output against a "golden" baseline file stored in the repository.
- Updating Baselines: If the generated code is expected to change, developers can run the tests with the
PULUMI_ACCEPT=trueenvironment variable to automatically update the baseline files.
Sources: pkg/codegen/testing/test/program_driver.go:211-298, pkg/codegen/testing/test/helpers.go:120-143
Conclusion
The pulumi-codegen system is a sophisticated and essential part of the Pulumi platform. It abstracts away the differences between cloud provider APIs and programming languages, providing a unified and idiomatic experience for developers. By leveraging a single source of truth (the Pulumi Schema), it ensures consistency and scalability, enabling Pulumi to rapidly expand its ecosystem of supported clouds and languages. The system's ability to generate not just SDKs but also full programs and documentation highlights its versatility and central role in the developer experience.