Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class TerraformProviderCodegen extends AbstractGoCodegen {
protected String providerAddress = "registry.terraform.io/example/example";
protected String providerVersion = "0.1.0";

// Track which tags qualify as resources vs data sources so we can
// delete non-qualifying files in postProcessFile.
private final Set<String> resourceNames = new HashSet<>();
private final Set<String> dataSourceNames = new HashSet<>();

@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
Expand Down Expand Up @@ -122,6 +127,9 @@ public TerraformProviderCodegen() {
public void processOpts() {
super.processOpts();

// Enable postProcessFile so we can delete non-qualifying API files
this.setEnablePostProcessFile(true);

if (additionalProperties.containsKey(PROVIDER_NAME)) {
providerName = additionalProperties.get(PROVIDER_NAME).toString();
}
Expand Down Expand Up @@ -272,6 +280,14 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
objectMap.put("hasDelete", deleteOp != null);
objectMap.put("hasList", listOp != null);

// A tag qualifies as a Terraform resource only if it has a create
// operation; without one the resource cannot be instantiated.
// A tag qualifies as a data source if it has a read (show) operation.
boolean isResource = (createOp != null);
boolean isDataSource = (readOp != null);
objectMap.put("isResource", isResource);
objectMap.put("isDataSource", isDataSource);

// Store CRUD operation details at tag level for simplified template access
if (createOp != null) {
objectMap.put("createMethod", createOp.vendorExtensions.get("x-terraform-http-method"));
Expand Down Expand Up @@ -312,6 +328,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
objectMap.put("resourceName", resourceName);
objectMap.put("resourceClassName", camelize(resourceName));

if (isResource) {
resourceNames.add(resourceName);
}
if (isDataSource) {
dataSourceNames.add(resourceName);
}

// Detect ID field from read operation path params
String idField = "id";
if (readOp != null && readOp.pathParams != null && !readOp.pathParams.isEmpty()) {
Expand Down Expand Up @@ -354,7 +377,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
attr.put("goType", prop.dataType);
attr.put("terraformType", goTypeToTerraformType(prop.dataType));
attr.put("terraformAttrType", goTypeToTerraformAttrType(prop.dataType, prop));
attr.put("isRequired", prop.required);
attr.put("isRequired", prop.required && !prop.isReadOnly);
attr.put("isComputed", prop.isReadOnly);
attr.put("isOptional", !prop.required && !prop.isReadOnly);
attr.put("description", prop.description != null ? prop.description : "");
Expand Down Expand Up @@ -407,11 +430,19 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
if ("id".equalsIgnoreCase(prop.baseName)) {
idModelGoName = camelize(prop.baseName);
idModelGoType = prop.dataType;
idResolved = true;
break;
}
}
}

if (!idResolved) {
LOGGER.warn("Could not resolve ID field '{}' in response model '{}' for resource '{}'. "
+ "The generated code will not compile. Add an '{}' property to the response "
+ "schema or use x-terraform-id-field to specify the correct property.",
idField, responseModel, resourceName, idField);
}

break;
}
}
Expand Down Expand Up @@ -482,6 +513,42 @@ public ModelsMap postProcessModels(ModelsMap objs) {
return objs;
}

@Override
public void postProcessFile(File file, String fileType) {
super.postProcessFile(file, fileType);
if (file == null || !file.exists() || !"api".equals(fileType)) {
return;
}

String fileName = file.getName();

if (fileName.endsWith("_resource.go")) {
String tag = fileName.substring(0, fileName.length() - "_resource.go".length());
if (!resourceNames.contains(tag)) {
LOGGER.info("Skipping non-resource file: {}", file.getAbsolutePath());
file.delete();
return;
}
}

if (fileName.endsWith("_data_source.go")) {
String tag = fileName.substring(0, fileName.length() - "_data_source.go".length());
if (!dataSourceNames.contains(tag)) {
LOGGER.info("Skipping non-data-source file: {}", file.getAbsolutePath());
file.delete();
return;
}
}

if (fileName.endsWith("_model.go")) {
String tag = fileName.substring(0, fileName.length() - "_model.go".length());
if (!resourceNames.contains(tag) && !dataSourceNames.contains(tag)) {
LOGGER.info("Skipping unused model file: {}", file.getAbsolutePath());
file.delete();
}
}
}

private String goTypeToTerraformType(String goType) {
if (goType == null) return "types.String";
switch (goType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module {{gitHost}}/{{gitUserId}}/{{gitRepoId}}
module github.com/teamlumos/knowledge-server

go 1.24.0

Expand All @@ -7,5 +7,28 @@ toolchain go1.24.4
require (
github.com/hashicorp/terraform-plugin-framework v1.17.0
github.com/hashicorp/terraform-plugin-log v0.10.0
github.com/hashicorp/terraform-plugin-testing v1.14.0
)

require (
github.com/fatih/color v1.16.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-plugin v1.7.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/terraform-plugin-go v0.29.0 // indirect
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.9 // indirect
)
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ func (p *{{providerName}}Provider) Configure(ctx context.Context, req provider.C

func (p *{{providerName}}Provider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
{{#apiInfo}}{{#apis}}{{#operations}} New{{resourceClassName}}Resource,
{{/operations}}{{/apis}}{{/apiInfo}} }
{{#apiInfo}}{{#apis}}{{#operations}}{{#isResource}} New{{resourceClassName}}Resource,
{{/isResource}}{{/operations}}{{/apis}}{{/apiInfo}} }
}

func (p *{{providerName}}Provider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
{{#apiInfo}}{{#apis}}{{#operations}} New{{resourceClassName}}DataSource,
{{/operations}}{{/apis}}{{/apiInfo}} }
{{#apiInfo}}{{#apis}}{{#operations}}{{#isDataSource}} New{{resourceClassName}}DataSource,
{{/isDataSource}}{{/operations}}{{/apis}}{{/apiInfo}} }
}
Loading