Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(provider-generator): provided name does not need to match name in source #3305

Merged
merged 3 commits into from
Jan 4, 2024
Merged
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
69 changes: 55 additions & 14 deletions packages/@cdktf/provider-generator/lib/__tests__/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,29 @@ function directorySnapshot(root: string) {
return output;
}

function resourceTypesPresentInSnapshot(
snapshot: SynthOutput,
providerNameInPath: string
) {
const resources: string[] = [];
const files = Object.keys(snapshot);
for (const file of files) {
const match = file.match(
`/providers\/${providerNameInPath}\/(.*?)\/index\.ts/`
);
// avoids any not resources from being pushed
if (
match &&
!match[1].includes("/") &&
!match[1].includes("data-") &&
!match[1].includes("provider")
) {
resources.push(match[1]);
}
}
return resources;
}

describe("Provider", () => {
it("generates a provider", async () => {
const constraint = new TerraformProviderConstraint(
Expand Down Expand Up @@ -76,20 +99,9 @@ describe("Provider", () => {
await maker.generate([constraint]);
const snapshot = directorySnapshot(workdir);

const terraformResourceTypesPresent: string[] = [];
const files = Object.keys(snapshot);
for (const file of files) {
const match = file.match(/providers\/datadog\/(.*?)\/index\.ts/);
// avoids any not resources from being pushed
if (
match &&
!match[1].includes("/") &&
!match[1].includes("data-") &&
!match[1].includes("provider")
) {
terraformResourceTypesPresent.push(match[1]);
}
}
const terraformResourceTypesPresent: string[] =
resourceTypesPresentInSnapshot(snapshot, "datadog");

terraformResourceTypesPresent.forEach((resource) => {
let terraformResourceType = resource.replace(/-/g, "_");
if (!terraformResourceType.includes("datadog")) {
Expand All @@ -104,4 +116,33 @@ describe("Provider", () => {
});
});
}, 600_000);
it("has name in constraint that does not match resolved name in fqpn", async () => {
const constraint = new TerraformProviderConstraint({
name: "dockerr",
source: "registry.terraform.io/kreuzwerker/docker",
version: "3.0.2",
});
return await mkdtemp(async (workdir) => {
const jsiiPath = path.join(workdir, ".jsii");
const maker = new ConstructsMaker(
{
codeMakerOutput: workdir,
outputJsii: jsiiPath,
targetLanguage: Language.TYPESCRIPT,
},
process.env.CDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH
);
await maker.generate([constraint]);
const snapshot = directorySnapshot(workdir);

const terraformResourceTypesPresent: string[] =
resourceTypesPresentInSnapshot(snapshot, "dockerr");

terraformResourceTypesPresent.forEach((resource) => {
expect(
snapshot[`providers/dockerr/${resource}/index.ts`]
).toBeDefined();
});
});
}, 600_000);
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const isMatching = (
throw new Error(`can't handle ${terraformSchemaName}`);
}

return target.name === provider;
return target.name === provider || target.source === terraformSchemaName;
}
};

Expand Down Expand Up @@ -105,20 +105,29 @@ export class TerraformProviderGenerator {
await this.code.save(outdir);
}

public buildResourceModels(fqpn: FQPN): ResourceModel[] {
public buildResourceModels(
fqpn: FQPN,
constraint?: ConstructsMakerTarget
): ResourceModel[] {
const provider = this.schema.provider_schemas?.[fqpn];
if (!provider) {
throw new Error(`Can not find provider '${fqpn}' in schema`);
}

const resources = Object.entries(provider.resource_schemas || {}).map(
([type, resource]) =>
this.resourceParser.parse(fqpn, type, resource, "resource")
this.resourceParser.parse(fqpn, type, resource, "resource", constraint)
);

const dataSources = Object.entries(provider.data_source_schemas || {}).map(
([type, resource]) =>
this.resourceParser.parse(fqpn, `data_${type}`, resource, "data_source")
this.resourceParser.parse(
fqpn,
`data_${type}`,
resource,
"data_source",
constraint
)
);

return ([] as ResourceModel[]).concat(...resources, ...dataSources);
Expand All @@ -137,14 +146,16 @@ export class TerraformProviderGenerator {
providerVersion?: string,
constraint?: ConstructsMakerTarget
) {
const { name } = parseFQPN(fqpn);
const name = constraint?.name
? (constraint.name as ProviderName)
: parseFQPN(fqpn).name;
const provider = this.schema.provider_schemas?.[fqpn];
if (!provider) {
throw new Error(`Can not find provider '${fqpn}' in schema`);
}

const files: string[] = [];
this.buildResourceModels(fqpn).forEach((resourceModel) => {
this.buildResourceModels(fqpn, constraint).forEach((resourceModel) => {
if (constraint) {
resourceModel.providerVersionConstraint = constraint.version;
resourceModel.terraformProviderSource = constraint.source;
Expand All @@ -164,7 +175,8 @@ export class TerraformProviderGenerator {
fqpn,
`provider`,
provider.provider,
"provider"
"provider",
constraint
);
if (constraint) {
providerResource.providerVersionConstraint = constraint.version;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
AttributeType,
Block,
BlockType,
ConstructsMakerTarget,
isAttributeNestedType,
isNestedTypeAttribute,
Schema,
Expand Down Expand Up @@ -123,17 +124,27 @@ class Parser {
fqpn: FQPN,
type: string,
schema: Schema,
terraformSchemaType: string
terraformSchemaType: string,
constraint?: ConstructsMakerTarget
): ResourceModel {
const provider = parseFQPN(fqpn).name;
let baseName = type;
if (baseName.startsWith(`${provider}_`)) {
baseName = baseName.substr(provider.length + 1);

const providerNameFromConstraint = constraint
? (constraint.name as ProviderName)
: undefined;
const providerNameFromFQPN = parseFQPN(fqpn).name;

if (baseName.startsWith(`${providerNameFromFQPN}_`)) {
baseName = baseName.substr(providerNameFromFQPN.length + 1);
}

const providerName = providerNameFromConstraint
? providerNameFromConstraint
: providerNameFromFQPN;

const isProvider = terraformSchemaType === "provider";
if (isProvider) {
baseName = `${provider}_${baseName}`;
baseName = `${providerName}_${baseName}`;
if (!("attributes" in schema.block)) {
schema.block = {
attributes: {},
Expand All @@ -154,16 +165,16 @@ class Parser {
const className = this.uniqueClassName(toPascalCase(baseName));
// avoid naming collision - see https://github.com/hashicorp/terraform-cdk/issues/299
const configStructName = this.uniqueClassName(`${className}Config`);
const fileName = getFileName(provider, baseName);
const fileName = getFileName(providerName, baseName);

const filePath = `providers/${toSnakeCase(provider)}/${fileName}`;
const filePath = `providers/${toSnakeCase(providerName)}/${fileName}`;
let attributes = this.renderAttributesForBlock(
new Scope({
name: baseName,
isProvider,
parent: isProvider
? undefined
: new Scope({ name: provider, isProvider: true }),
: new Scope({ name: providerName, isProvider: true }),
}),
schema.block
);
Expand Down Expand Up @@ -664,14 +675,21 @@ export class ResourceParser {
fqpn: FQPN,
type: string,
schema: Schema,
terraformType: string
terraformType: string,
constraint?: ConstructsMakerTarget
): ResourceModel {
if (this.resources[type]) {
return this.resources[type];
}

const parser = new Parser(this.uniqueClassnames);
const resource = parser.resourceFrom(fqpn, type, schema, terraformType);
const resource = parser.resourceFrom(
fqpn,
type,
schema,
terraformType,
constraint
);
this.resources[type] = resource;
return resource;
}
Expand Down
Loading