mirror of
https://github.com/coder/registry.git
synced 2026-06-03 21:18:15 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83a82345de |
@@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type terraformLifecycleCondition struct {
|
||||
// Any terraform expression that evaluates to a boolean
|
||||
expression string
|
||||
errorMessage *string
|
||||
}
|
||||
|
||||
type terraformLifecycle struct {
|
||||
createBeforeDestroy bool
|
||||
preventDestroy bool
|
||||
ignoreChanges []string
|
||||
replaceTriggeredBy []string
|
||||
preCondition *terraformLifecycleCondition
|
||||
postCondition *terraformLifecycleCondition
|
||||
}
|
||||
|
||||
type terraformForEachKind string
|
||||
|
||||
const (
|
||||
terraformForEachKindMap terraformForEachKind = "map"
|
||||
terraformForEachKindSet terraformForEachKind = "set"
|
||||
)
|
||||
|
||||
type terraformForEach struct {
|
||||
kind terraformForEachKind
|
||||
// If the kind is "map", all values should be guaranteed to be a definite
|
||||
// string for each key. If it's "set", all values should be nil
|
||||
values map[string]*string
|
||||
}
|
||||
|
||||
type terraformVariableKind string
|
||||
|
||||
const (
|
||||
terraformVariableKindString terraformVariableKind = "string"
|
||||
terraformVariableKindNumber terraformVariableKind = "number"
|
||||
terraformVariableKindBool terraformVariableKind = "bool"
|
||||
terraformVariableKindList terraformVariableKind = "list"
|
||||
terraformVariableKindMap terraformVariableKind = "map"
|
||||
terraformVariableKindSet terraformVariableKind = "set"
|
||||
// This value kind is incredibly rare. It corresponds to any variable with
|
||||
// a null value but no defined type
|
||||
terraformVariableKindUnknown terraformVariableKind = "unknown"
|
||||
)
|
||||
|
||||
type terraformVariable struct {
|
||||
kind terraformVariableKind
|
||||
value any
|
||||
}
|
||||
|
||||
// coderTerraformModule represents the values that can be parsed from a
|
||||
// Terraform module that are relevant to a Coder deployment. Most of the fields
|
||||
// are derived from the main Terraform spec, but some additional Coder-specific
|
||||
// fields are appended, too, for convenience
|
||||
type coderTerraformModule struct {
|
||||
// Corresponds to the optional `lifecycle` metadata field available to all
|
||||
// resource types
|
||||
Lifecycle *terraformLifecycle `json:"lifecycle"`
|
||||
// Corresponds to the optional `for_each` metadata field available to all
|
||||
// resource types
|
||||
ForEach *terraformForEach `json:"for_each"`
|
||||
// Corresponds to the `source` field in a module block
|
||||
ModuleSource string `json:"module_source"`
|
||||
// Corresponds to the `version` field in Terraform module blocks. Note that
|
||||
// while the Terraform spec marks this field as optional, Coder requires
|
||||
// that one always be defined.
|
||||
Version string `json:"version"`
|
||||
// Corresponds to the optional `provider` field in a module block
|
||||
Provider *string `json:"provider"`
|
||||
// Corresponds to optional `depends_on` field for module blocks
|
||||
DependsOn []string `json:"depends_on"`
|
||||
// Corresponds to the `count` field for any Terraform resource type. It
|
||||
// defines the number of resource instances to create when using Terraform
|
||||
// Apply.
|
||||
InstanceCount int `json:"instance_count"`
|
||||
// Corresponds to `agent_id`` field in a module block. Terraform doesn't
|
||||
// have any built-in concept of an agent_id, but it's needed to make a
|
||||
// module work with a Coder deployment
|
||||
AgentID string `json:"agent_id"`
|
||||
// Captures all other arbitrary values defined for a Terraform module block.
|
||||
// Note that while Terraform itself has you define all other fields at the
|
||||
// same level as the well-known/official fields, they've been isolated into
|
||||
// a map for the Go struct definition to improve type-safety
|
||||
Values map[string]terraformVariable `json:"values"`
|
||||
// The raw Terraform snippet used to derive the coderTerraformModule struct
|
||||
SourceCode string `json:"source_code"`
|
||||
}
|
||||
|
||||
var _ json.Marshaler = &coderTerraformModule{}
|
||||
|
||||
func (ctmCopy coderTerraformModule) MarshalJSON() ([]byte, error) {
|
||||
if ctmCopy.Lifecycle != nil {
|
||||
lCopy := &terraformLifecycle{
|
||||
createBeforeDestroy: ctmCopy.Lifecycle.createBeforeDestroy,
|
||||
preventDestroy: ctmCopy.Lifecycle.preventDestroy,
|
||||
ignoreChanges: ctmCopy.Lifecycle.ignoreChanges,
|
||||
replaceTriggeredBy: ctmCopy.Lifecycle.replaceTriggeredBy,
|
||||
preCondition: ctmCopy.Lifecycle.preCondition,
|
||||
postCondition: ctmCopy.Lifecycle.postCondition,
|
||||
}
|
||||
// Make sure that both slices always get serialized as JSON null if
|
||||
// they're empty. Serializing as an empty JSON array has no extra
|
||||
// semantics
|
||||
if len(lCopy.ignoreChanges) == 0 {
|
||||
lCopy.ignoreChanges = nil
|
||||
}
|
||||
if len(lCopy.replaceTriggeredBy) == 0 {
|
||||
lCopy.replaceTriggeredBy = nil
|
||||
}
|
||||
ctmCopy.Lifecycle = lCopy
|
||||
}
|
||||
|
||||
if ctmCopy.ForEach != nil {
|
||||
feCopy := &terraformForEach{
|
||||
kind: ctmCopy.ForEach.kind,
|
||||
values: ctmCopy.ForEach.values,
|
||||
}
|
||||
// Make sure that the values map is NEVER serialized as JSON null
|
||||
if feCopy.values == nil {
|
||||
feCopy.values = make(map[string]*string)
|
||||
}
|
||||
ctmCopy.ForEach = feCopy
|
||||
}
|
||||
|
||||
if len(ctmCopy.DependsOn) == 0 {
|
||||
ctmCopy.DependsOn = nil
|
||||
}
|
||||
|
||||
if ctmCopy.Values == nil {
|
||||
ctmCopy.Values = make(map[string]terraformVariable)
|
||||
}
|
||||
for k, tfValue := range ctmCopy.Values {
|
||||
switch tfValue.kind {
|
||||
case terraformVariableKindString, terraformVariableKindBool, terraformVariableKindNumber, terraformVariableKindUnknown:
|
||||
continue
|
||||
case terraformVariableKindSet, terraformVariableKindList:
|
||||
recast, ok := tfValue.value.([]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to process terraform variable %q of kind %q as set/list", k, tfValue.kind)
|
||||
}
|
||||
if recast == nil {
|
||||
ctmCopy.Values[k] = terraformVariable{
|
||||
kind: tfValue.kind,
|
||||
value: []any{},
|
||||
}
|
||||
}
|
||||
case terraformVariableKindMap:
|
||||
recast, ok := tfValue.value.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to process terraform variable %q of kind %q as map", k, tfValue.kind)
|
||||
}
|
||||
if recast == nil {
|
||||
ctmCopy.Values[k] = terraformVariable{
|
||||
kind: tfValue.kind,
|
||||
value: make(map[string]any),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(ctmCopy)
|
||||
}
|
||||
Reference in New Issue
Block a user