- Author(s): @yufeiminds
- Approver: @yufeiminds
- Status: Implemented
- Last updated: 2023-03-06
- Discussion at: #1
This proposal describes a solution to generate artifacts about resources and provide a unified way to do the following things:
- Define the template as a CUE package. Declare the definition of the template as a CUE package. Includes the template name, description, parameters, output, etc.
- Testing template use snapshot way. Provide a snapshot testing method to test the template.
- Create an engine to generate code. Create an engine to generate code from the template and the RMS.
- A001: API as Code Overview. Describe the "API as Code" concept and the goals of this specification.
- A002: Resource Management Specification. Describe the "API as Code" concept and the goals of this specification.
The core design:
- The inputs are the RMS package and CLI options collected by OpenRMS CLI.
- The outputs are the artifacts would be generated by the template.
- The diagnostic is errors would be raised when the template is rendered. It provides an error-handling mechanism for generating.
The syntax details can be found on CUE Lang.
Because the template is a CUE package, we can use the CUE package discovery mechanism to discover the template. We recommend hosting the package on GitHub.
More details of CUE package discovery can be found on CUE Package Discovering.
The template testing is snapshot testing. We should provide built-in test inputs (petstore
RMS package), implement a test-case discovery and snapshot diff mechanism, and help the template developer to write the test case quickly.
When a template developer creates a new template, the developer should write the test case. When a template snapshot testing is running, the following steps will be executed:
- Why use CUE as the template language?
Because CUE has a great programmatic API and a powerful type system, it can help us to validate the template, process the data in compile-time, and make the snippet re-useable by the package management tool.
Solutions comparasion:
- Instead of using a pure-template solution (such as a pure go template), CUE can inject custom data processing logic. We don't need to write the same logic repeatedly in a general-purpose programming language.
- Instead of using a pure-programming solution (such as pure go code), CUE can provide a unified way to define the template. It can help the template developer focus on the template logic without understanding the input DSL's Abstract Syntax Tree (AST).
- Why use snapshot testing?
Because it's easy to write and maintain, snapshot testing is a common way to test the template. Unlike the unit test, snapshot testing can test the template outputs. It's more suitable for template testing. It can also fit the user's expectations.
Suppose the resource developer creates a new template, such as Terraform. The template may be like the following:
- Declare required properties
Create terraform/manifest.cue
as the template package manifest:
package terraform
import (
template "github.com/GuanceCloud/iacker/pkg/template/v1"
)
inputs: template.#Inputs
diagnostics: [...template.#Diagnostic]
outputs: template.#Outputs
This snippet will import the built-in type annotation from the core package and validate the template's value types. It will help the template developer to write the correct template.
- Define the template
Create terraform/template.cue
as the template package manifest:
package terraform
import (
"strings"
gotemplate "text/template"
template "github.com/GuanceCloud/iacker/pkg/template/v1"
)
_templates: """
package {{ .lowername }}
// {{ .camelname }} holds the schema definition for the {{ .camelname }} entity.
type {{ .camelname }} struct {
// some fields
}
"""
for rsname, rs in inputs.resources {
outputs: files: "\(strings.ToLower(rsname)).go": template.#File & {
content: gotemplate.Execute(_templates, {
lowername: strings.ToLower(rsname),
camelname: rsname,
v: rs,
})
}
}
- Define the snapshot
Create terraform/snapshot/petstore
as the template snapshot folder. The test engine will diff the template outputs with files in this folder.
More examples can be found on template folder.