Creating a generator in Infrahub
Within Infrahub a generator is defined in an external repository. However, during development and troubleshooting it is easiest to start from your local computer and run the transform using infrahubctl generator.
The goal of this guide is to develop a Generator and add it to Infrahub, we will achieve this by following these steps.
- Identify the relevant data you want to extract from the database using a GraphQL query, that can take an input parameter to filter the data
- Write a Python script that uses the GraphQL query to read information from the system and generates new data based on the response
- Create an entry for the generator within an .infrahub.yml file.
- Create a Git repository
- Test the generator with infrahubctl
- Add the repository to Infrahub as an external repository
- Validate that the generator works by triggering it through a proposed change
Preparations
What your generator will look like will depend on what your schema looks like and the intended outcome. The generator described here will be very generic and also not useful in a real world scenario, it is only meant to describe how the generators work.
As the default Infrahub schema doesn't have a lot of object types to use as a test, we will illustrate how this could work by adding two nodes to the schema.
Load the following schema using the infrahubctl schema command.
# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json
---
version: '1.0'
nodes:
- name: Widget
namespace: Test
label: "Widget"
default_filter: name__value
display_labels:
- name__value
attributes:
- name: name
kind: Text
unique: true
- name: count
kind: Number
- name: Resource
namespace: Test
label: "Resource"
default_filter: name__value
display_labels:
- name__value
attributes:
- name: name
kind: Text
unique: true
Perform these steps in the frontend.
- Create two new widget objects
- One with the name
widget1
and count 1 - One with the name
widget2
and count 2
- One with the name
- Create a Standard group called "widgets"
- Add both of the created objects to the new group
1. Identify the relevant data
Here we define a GraphQL query that we will use to gather information.
query Widgets($name: String!) {
TestWidget(name__value: $name) {
edges {
node {
name {
value
}
count {
value
}
}
}
}
}
Create a local directory on your computer where we will store the generator files.
mkdir example_generator
Within that directory store the above GraphQL query as widget_query.gql.
2. Create a Python Generator
The Generator class needs to implement a generate
function that receives a data
parameter that contains the response from the GraphQL query.
The goal of this generator will be to create a number of resources that depends on the set count of the widgets.
from infrahub_sdk.generator import InfrahubGenerator
class WidgetGenerator(InfrahubGenerator):
async def generate(self, data: dict) -> None:
widget = data["TestWidget"]["edges"][0]["node"]
widget_name: str = widget["name"]["value"]
widget_count: str = widget["count"]["value"]
for count in range(1, widget_count + 1):
payload = {
"name": f"{widget_name.lower()}-{count}",
}
obj = await self.client.create(kind="TestResource", data=payload)
await obj.save(allow_upsert=True)
Store this class within a new file called widget_generator.py.
3. Create an .infrahub.yml file
The .infrahub.yml file allows you to tie the different components of a generator together into a working unit.
# yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json
---
generator_definitions:
- name: widget_generator
file_path: "widget_generator.py"
targets: widgets
query: widget_query
class_name: WidgetGenerator
parameters:
name: "name__value"
queries:
- name: widget_query
file_path: "widget_query.gql"
- Generator definitions
- Queries
This defines a generator definition with the following properties:
- name: a unique name for the generator
- file_path: the relative file path to the file containing the generator as seen from within a Git repository
- targets: the name of a group of which the members will be a target for this generator
- query: the name of the GraphQL query used within this generator
- parameters: the parameter to pass to the generator GraphQL query, in this case this we will pass the name of the object (widget) as the name parameter
- query: the name of the GraphQL query used within this generator
Here the name
refers to the query's name and file_path
should point to the GraphQL file within the repository.
See this topic for a full explanation of everything that can be defined in the .infrahub.yml
file.
4. Create a Git repository
Within the example_generator
folder you should now have 3 files:
widget_query.gql
: Contains the GraphQL querygenerator.py
: Contains the Python code for the generator.infrahub.yml
: Contains the definition for the generator
Before we can test our generator we must add the files to a local Git repository.
git init --initial-branch=main
git add .
git commit -m "First commit"
5. Test the generator using infrahubctl
Using infrahubctl you can first verify that the .infrahub.yml
file is formatted correctly by listing available generators.
Generators defined in repository: 1
widget_generator (widget_generator.py::Generator) Target: widgets
When running a generator with infrahubctl
the SDK tracking feature isn't used. The reason for this is that internally Infrahub uses the ID of the generator_definition to control the tracking, this isn't available from the outside. For this reason it is recommended to create test branches when developing generators and validating the results.
infrahubctl branch create test-branch1
Then we can try to run the generator within this branch.
infrahubctl generator widget_generator --branch=test-branch1 name=widget1
infrahubctl generator widget_generator --branch=test-branch1 name=widget2
Now you should see the tree TestResource objects within test-branch1
one for the first widget and two for the second one.
With this step completed you can add your repository to Infrahub and then the generators will be executed as part of the proposed change pipeline.