diff --git a/apps/terraform/convertor/TerraformConvertor.ts b/apps/terraform/convertor/TerraformConvertor.ts new file mode 100644 index 00000000..d2ffdd76 --- /dev/null +++ b/apps/terraform/convertor/TerraformConvertor.ts @@ -0,0 +1,32 @@ +import { ResourceManager } from '../type/ResourceManager'; +import { CodeGenerator } from '../type/TerraformGenerator'; +import { NCloudProvider } from '../model/NCloudProvider'; +import { CloudCanvasNode } from '../interface/CloudCanvasNode'; +import { parseToNCloudModel } from '../util/resourceParser'; + +export class TerraformConvertor { + private readonly resourceManager: ResourceManager; + private readonly codeGenerator: CodeGenerator; + private readonly provider: NCloudProvider; + + constructor(provider: NCloudProvider) { + this.provider = provider; + this.resourceManager = new ResourceManager(); + this.codeGenerator = new CodeGenerator(this.resourceManager); + } + + addResourceFromJson(jsonData: { nodes?: CloudCanvasNode[] }): void { + jsonData.nodes?.forEach(node => { + try { + const resource = parseToNCloudModel(node); + this.resourceManager.addResource(resource); + } catch (error) { + console.warn(`Skipping unsupported node type: ${node.type}`); + } + }); + } + + generate(): string { + return this.codeGenerator.generateCode(this.provider); + } +} \ No newline at end of file diff --git a/apps/terraform/enum/ResourcePriority.ts b/apps/terraform/enum/ResourcePriority.ts new file mode 100644 index 00000000..f1f46f34 --- /dev/null +++ b/apps/terraform/enum/ResourcePriority.ts @@ -0,0 +1,11 @@ +export enum ResourcePriority { + VPC = 1, + NETWORK_ACL = 2, + SUBNET = 3, + ACG = 4, + ACG_RULE = 5, + LOGIN_KEY = 6, + NETWORK_INTERFACE = 7, + SERVER = 8, + PUBLIC_IP = 9 +} \ No newline at end of file diff --git a/apps/terraform/interface/ACG.ts b/apps/terraform/interface/ACG.ts new file mode 100644 index 00000000..7775794c --- /dev/null +++ b/apps/terraform/interface/ACG.ts @@ -0,0 +1,6 @@ +export interface ACG { + id: string; + name: string; + vpcNo: string; + description: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/ACGRule.ts b/apps/terraform/interface/ACGRule.ts new file mode 100644 index 00000000..62cfac62 --- /dev/null +++ b/apps/terraform/interface/ACGRule.ts @@ -0,0 +1,6 @@ +export interface ACGRule { + protocol: string; + ipBlock: string; + portRange: string; + description: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/CloudCanvasNode.ts b/apps/terraform/interface/CloudCanvasNode.ts new file mode 100644 index 00000000..69a47251 --- /dev/null +++ b/apps/terraform/interface/CloudCanvasNode.ts @@ -0,0 +1,6 @@ +export interface CloudCanvasNode { + id: string; + type: string; + name: string; + properties: { [key: string]: any }; +} \ No newline at end of file diff --git a/apps/terraform/interface/FileOption.ts b/apps/terraform/interface/FileOption.ts new file mode 100644 index 00000000..9eee3c0f --- /dev/null +++ b/apps/terraform/interface/FileOption.ts @@ -0,0 +1,3 @@ +export interface FileOption { + log?: boolean; +} \ No newline at end of file diff --git a/apps/terraform/interface/NCloudModel.ts b/apps/terraform/interface/NCloudModel.ts new file mode 100644 index 00000000..fb7956c6 --- /dev/null +++ b/apps/terraform/interface/NCloudModel.ts @@ -0,0 +1,8 @@ +import { ResourcePriority } from '../enum/ResourcePriority'; + +export interface NCloudModel { + name: string; + serviceType: string; + priority: ResourcePriority; + getProperties(): { [key: string]: any }; +} \ No newline at end of file diff --git a/apps/terraform/interface/NetworkACL.ts b/apps/terraform/interface/NetworkACL.ts new file mode 100644 index 00000000..e83d9808 --- /dev/null +++ b/apps/terraform/interface/NetworkACL.ts @@ -0,0 +1,5 @@ +export interface NetworkACL { + id: string; + name: string; + vpcNo: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/NetworkInterface.ts b/apps/terraform/interface/NetworkInterface.ts new file mode 100644 index 00000000..cabcd519 --- /dev/null +++ b/apps/terraform/interface/NetworkInterface.ts @@ -0,0 +1,6 @@ +export interface NetworkInterface { + id: string; + name: string; + subnetNo: string; + accessControlGroups: string[]; +} \ No newline at end of file diff --git a/apps/terraform/interface/Provider.ts b/apps/terraform/interface/Provider.ts new file mode 100644 index 00000000..0f2a984d --- /dev/null +++ b/apps/terraform/interface/Provider.ts @@ -0,0 +1,6 @@ +export interface Provider { + accessKey: string; + secretKey: string; + region: string; + site: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/PublicIp.ts b/apps/terraform/interface/PublicIp.ts new file mode 100644 index 00000000..0f265b85 --- /dev/null +++ b/apps/terraform/interface/PublicIp.ts @@ -0,0 +1,5 @@ +export interface PublicIp { + id: string; + publicIp: string; + serverInstanceNo: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/Server.ts b/apps/terraform/interface/Server.ts new file mode 100644 index 00000000..fb1940ad --- /dev/null +++ b/apps/terraform/interface/Server.ts @@ -0,0 +1,9 @@ +export interface Server { + id: string; + name: string; + subnetNo: string; + serverImageProductCode: string; + serverProductCode: string; + loginKeyName: string; + networkInterfaceNo: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/Subnet.ts b/apps/terraform/interface/Subnet.ts new file mode 100644 index 00000000..2d3ef458 --- /dev/null +++ b/apps/terraform/interface/Subnet.ts @@ -0,0 +1,10 @@ +export interface Subnet { + id: string; + name: string; + vpcNo: string; + subnet: string; + zone: string; + networkAclNo: string; + subnetType: string; + usageType: string; +} \ No newline at end of file diff --git a/apps/terraform/interface/VPC.ts b/apps/terraform/interface/VPC.ts new file mode 100644 index 00000000..9500e524 --- /dev/null +++ b/apps/terraform/interface/VPC.ts @@ -0,0 +1,10 @@ +export interface VPC { + id: string; + name: string; + region: string; + ipv4CidrBlock: string; + defaultNetworkAclNo: string; + defaultAccessControlGroupNo: string; + defaultPublicRouteTableNo: string; + defaultPrivateRouteTableNo: string; +} \ No newline at end of file diff --git a/apps/terraform/main.ts b/apps/terraform/main.ts new file mode 100644 index 00000000..f65cf53d --- /dev/null +++ b/apps/terraform/main.ts @@ -0,0 +1,36 @@ +import { CloudCanvasNode } from './interface/CloudCanvasNode'; +import { NCloudProvider } from './model/NCloudProvider'; +import { TerraformConvertor } from './convertor/TerraformConvertor'; +import { sampleNodes } from './sample/sampleData'; +import { saveTerraformFiles } from './util/file'; + +async function generateTerraformCode(): Promise { + const provider = new NCloudProvider({ + accessKey: "var.access_key", + secretKey: "var.secret_key", + region: "var.region", + site: "public" + }); + + const converter = new TerraformConvertor(provider); + converter.addResourceFromJson({ nodes: sampleNodes }); + + return converter.generate(); +} + +async function main() { + try { + const terraformCode = await generateTerraformCode(); + await saveTerraformFiles(terraformCode, { log: true }); + + } catch (error) { + if (error instanceof Error) { + console.error('Error generating Terraform configuration:', error.message); + } else { + console.error('An unknown error occurred'); + } + process.exit(1); + } +} + +main().catch(console.error); diff --git a/apps/terraform/model/NCloudACG.ts b/apps/terraform/model/NCloudACG.ts new file mode 100644 index 00000000..6a7c1c20 --- /dev/null +++ b/apps/terraform/model/NCloudACG.ts @@ -0,0 +1,26 @@ +import { ACG } from '../interface/ACG'; +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudACG implements ACG, NCloudModel { + id: string; + name: string; + vpcNo: string; + description: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_access_control_group'; + this.priority = ResourcePriority.ACG; + Object.assign(this, json); + } + + getProperties() { + return { + name: this.name, + vpc_no: "VPC_ID_PLACEHOLDER", + description: this.description + }; + } +} diff --git a/apps/terraform/model/NCloudACGRule.ts b/apps/terraform/model/NCloudACGRule.ts new file mode 100644 index 00000000..ea3b7092 --- /dev/null +++ b/apps/terraform/model/NCloudACGRule.ts @@ -0,0 +1,34 @@ +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudACGRule implements NCloudModel { + name: string; + protocol: string; + ipBlock: string; + portRange: string; + description: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_access_control_group_rule'; + this.priority = ResourcePriority.ACG_RULE; + this.name = json.name || 'acg-rule'; + this.protocol = json.protocol; + this.ipBlock = json.ipBlock; + this.portRange = json.portRange; + this.description = json.description; + } + + getProperties() { + return { + access_control_group_no: "ACG_ID_PLACEHOLDER", + inbound: { + protocol: this.protocol, + ip_block: this.ipBlock, + port_range: this.portRange, + description: this.description + } + }; + } +} diff --git a/apps/terraform/model/NCloudLoginKey.ts b/apps/terraform/model/NCloudLoginKey.ts new file mode 100644 index 00000000..0dd88052 --- /dev/null +++ b/apps/terraform/model/NCloudLoginKey.ts @@ -0,0 +1,20 @@ +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudLoginKey implements NCloudModel { + name: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_login_key'; + this.priority = ResourcePriority.LOGIN_KEY; + this.name = json.name; + } + + getProperties() { + return { + key_name: this.name + }; + } +} diff --git a/apps/terraform/model/NCloudNetworkACL.ts b/apps/terraform/model/NCloudNetworkACL.ts new file mode 100644 index 00000000..8bef2f72 --- /dev/null +++ b/apps/terraform/model/NCloudNetworkACL.ts @@ -0,0 +1,25 @@ +import { NetworkACL } from '../interface/NetworkACL'; +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudNetworkACL implements NetworkACL, NCloudModel { + id: string; + name: string; + vpcNo: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_network_acl'; + this.priority = ResourcePriority.NETWORK_ACL; + this.id = json.id; + this.name = json.name || 'nacl'; + this.vpcNo = json.vpcNo; + } + + getProperties() { + return { + vpc_no: "VPC_ID_PLACEHOLDER" + }; + } +} diff --git a/apps/terraform/model/NCloudNetworkInterface.ts b/apps/terraform/model/NCloudNetworkInterface.ts new file mode 100644 index 00000000..aa55f485 --- /dev/null +++ b/apps/terraform/model/NCloudNetworkInterface.ts @@ -0,0 +1,27 @@ +import { NetworkInterface } from '../interface/NetworkInterface'; +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudNetworkInterface implements NetworkInterface, NCloudModel { + id: string; + name: string; + subnetNo: string; + accessControlGroups: string[]; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_network_interface'; + this.priority = ResourcePriority.NETWORK_INTERFACE; + Object.assign(this, json); + } + + getProperties() { + return { + subnet_no: 'SUBNET_ID_PLACEHOLDER', + name: this.name, + access_control_groups: ['ACG_ID_PLACEHOLDER'] + }; + } +} + diff --git a/apps/terraform/model/NCloudProvider.ts b/apps/terraform/model/NCloudProvider.ts new file mode 100644 index 00000000..67f5b152 --- /dev/null +++ b/apps/terraform/model/NCloudProvider.ts @@ -0,0 +1,44 @@ +import { Provider } from '../interface/Provider'; + +export class NCloudProvider implements Provider { + accessKey: string; + secretKey: string; + region: string; + site: string; + name: string; + serviceType: string; + requiredVersion: string; + source: string; + + constructor(json: any) { + this.serviceType = 'provider'; + this.name = 'ncloud'; + this.accessKey = json.accessKey; + this.secretKey = json.secretKey; + this.region = json.region; + this.site = json.site || 'public'; + this.requiredVersion = '>= 0.13'; + this.source = 'NaverCloudPlatform/ncloud'; + } + + getProperties() { + return { + terraform: { + required_providers: { + ncloud: { + source: this.source + } + }, + required_version: this.requiredVersion + }, + provider: { + access_key: "var.access_key", + secret_key: "var.secret_key", + region: "var.region", + site: this.site, + support_vpc: true + } + }; + } + +} diff --git a/apps/terraform/model/NCloudPublicIP.ts b/apps/terraform/model/NCloudPublicIP.ts new file mode 100644 index 00000000..9685fd3e --- /dev/null +++ b/apps/terraform/model/NCloudPublicIP.ts @@ -0,0 +1,20 @@ +import { ResourcePriority } from '../enum/ResourcePriority'; +import { NCloudModel } from '../interface/NCloudModel'; + +export class NCloudPublicIP implements NCloudModel { + name: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_public_ip'; + this.priority = ResourcePriority.PUBLIC_IP; + this.name = json.name; + } + + getProperties() { + return { + server_instance_no: "SERVER_ID_PLACEHOLDER" + }; + } +} diff --git a/apps/terraform/model/NCloudServer.ts b/apps/terraform/model/NCloudServer.ts new file mode 100644 index 00000000..d79d7818 --- /dev/null +++ b/apps/terraform/model/NCloudServer.ts @@ -0,0 +1,42 @@ +import { Server } from '../interface/Server'; +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudServer implements Server, NCloudModel { + id: string; + name: string; + subnetNo: string; + serverImageProductCode: string; + serverProductCode: string; + loginKeyName: string; + networkInterfaceNo: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_server'; + this.priority = ResourcePriority.SERVER; + this.id = json.id; + this.name = json.name; + this.subnetNo = json.subnetNo; + this.serverImageProductCode = json.serverImageProductCode; + this.serverProductCode = json.serverProductCode; + this.loginKeyName = json.loginKeyName; + this.networkInterfaceNo = json.networkInterfaceNo; + } + + getProperties() { + return { + subnet_no: "SUBNET_ID_PLACEHOLDER", + name: this.name, + server_image_product_code: this.serverImageProductCode, + server_product_code: this.serverProductCode, + login_key_name: "LOGIN_KEY_NAME_PLACEHOLDER", + network_interface: { + network_interface_no: "NIC_ID_PLACEHOLDER", + order: 0 + } + }; + } +} + diff --git a/apps/terraform/model/NCloudSubnet.ts b/apps/terraform/model/NCloudSubnet.ts new file mode 100644 index 00000000..643c25b5 --- /dev/null +++ b/apps/terraform/model/NCloudSubnet.ts @@ -0,0 +1,36 @@ +import { ResourcePriority } from '../enum/ResourcePriority'; +import { NCloudModel } from '../interface/NCloudModel'; + +export class NCloudSubnet implements NCloudModel { + id: string; + name: string; + subnet: string; + zone: string; + subnetType: string; + usageType: string; + serviceType: string; + priority: ResourcePriority; + + constructor(json: any) { + this.serviceType = 'ncloud_subnet'; + this.priority = ResourcePriority.SUBNET; + this.name = json.name || 'subnet'; + this.subnet = json.subnet; + this.zone = json.zone; + this.subnetType = json.subnetType; + this.usageType = json.usageType; + } + + getProperties() { + return { + vpc_no: "VPC_ID_PLACEHOLDER", + subnet: this.subnet, + zone: this.zone, + network_acl_no: "VPC_ACL_PLACEHOLDER", + subnet_type: this.subnetType, + name: this.name, + usage_type: this.usageType + }; + } +} + diff --git a/apps/terraform/model/NCloudVPC.ts b/apps/terraform/model/NCloudVPC.ts new file mode 100644 index 00000000..c2b92d63 --- /dev/null +++ b/apps/terraform/model/NCloudVPC.ts @@ -0,0 +1,37 @@ +import { VPC } from '../interface/VPC'; +import { NCloudModel } from '../interface/NCloudModel'; +import { ResourcePriority } from '../enum/ResourcePriority'; + +export class NCloudVPC implements VPC, NCloudModel { + id: string; + name: string; + region: string; + ipv4CidrBlock: string; + defaultNetworkAclNo: string; + defaultAccessControlGroupNo: string; + defaultPublicRouteTableNo: string; + defaultPrivateRouteTableNo: string; + serviceType: string; + priority: ResourcePriority; + + + constructor(json: any) { + this.serviceType = 'ncloud_vpc'; + this.priority = ResourcePriority.VPC; + this.id = json.id; + this.name = json.name; + this.region = json.region; + this.ipv4CidrBlock = json.ipv4CidrBlock; + this.defaultNetworkAclNo = json.defaultNetworkAclNo; + this.defaultAccessControlGroupNo = json.defaultAccessControlGroupNo; + this.defaultPublicRouteTableNo = json.defaultPublicRouteTableNo; + this.defaultPrivateRouteTableNo = json.defaultPrivateRouteTableNo; + } + + getProperties() { + return { + name: this.name, + ipv4_cidr_block: this.ipv4CidrBlock + }; + } +} diff --git a/apps/terraform/outputs.tf b/apps/terraform/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/apps/terraform/sample/sampleData.ts b/apps/terraform/sample/sampleData.ts new file mode 100644 index 00000000..2a1a81a0 --- /dev/null +++ b/apps/terraform/sample/sampleData.ts @@ -0,0 +1,77 @@ +import { CloudCanvasNode } from '../interface/CloudCanvasNode'; + +export const sampleNodes: CloudCanvasNode[] = [ + { + id: "vpc1", + type: "VPC", + name: "my-vpc", + properties: { + cidrBlock: "172.16.0.0/16" + } + }, + { + id: "nacl1", + type: "NetworkACL", + name: "my-nacl", + properties: { + } + }, + { + id: "subnet1", + type: "Subnet", + name: "my-subnet", + properties: { + subnet: "172.16.10.0/24", + zone: "KR-2", + subnetType: "PUBLIC", + usageType: "GEN" + } + }, + { + id: "acg1", + type: "ACG", + name: "my-acg", + properties: { + description: "My ACG" + } + }, + { + id: "acgrule1", + type: "ACGRule", + name: "", + properties: { + protocol: "TCP", + ipBlock: "0.0.0.0/0", + portRange: "80", + description: "HTTP" + } + }, + { + id: "loginkey1", + type: "LoginKey", + name: "my-key", + properties: { + } + }, + { + id: "nic1", + type: "NetworkInterface", + name: "my-nic", + properties: {} + }, + { + id: "server1", + type: "Server", + name: "my-server", + properties: { + serverImageProductCode: "SW.VSVR.OS.LNX64.CNTOS.0708.B050", + serverProductCode: "SVR.VSVR.HICPU.C002.M004.NET.HDD.B050.G002" + } + }, + { + id: "publicip1", + type: "PublicIP", + name: "my-public-ip", + properties: {} + } +]; diff --git a/apps/terraform/terraform.tfvars b/apps/terraform/terraform.tfvars new file mode 100644 index 00000000..e69de29b diff --git a/apps/terraform/type/ResourceManager.ts b/apps/terraform/type/ResourceManager.ts new file mode 100644 index 00000000..b1621c51 --- /dev/null +++ b/apps/terraform/type/ResourceManager.ts @@ -0,0 +1,24 @@ +import { NCloudModel } from '../interface/NCloudModel'; + +export class ResourceManager { + private resources: NCloudModel[]; + private readonly nameMap: Map; + + constructor() { + this.resources = []; + this.nameMap = new Map(); + } + + addResource(resource: NCloudModel): void { + this.resources.push(resource); + this.nameMap.set(resource.serviceType, resource.name); + } + + getResources(): NCloudModel[] { + return [...this.resources].sort((a, b) => a.priority - b.priority); + } + + getNameMap(): Map { + return this.nameMap; + } +} diff --git a/apps/terraform/type/TerraformGenerator.ts b/apps/terraform/type/TerraformGenerator.ts new file mode 100644 index 00000000..ed4ce4fd --- /dev/null +++ b/apps/terraform/type/TerraformGenerator.ts @@ -0,0 +1,45 @@ +import { replaceReferences } from '../util/reference'; +import { generateProviderBlock, generateResourceBlock, generateTerraformBlock } from '../util/generator'; +import { ResourceManager } from './ResourceManager'; +import { NCloudProvider } from '../model/NCloudProvider'; + +export class CodeGenerator { + private resourceManager: ResourceManager; + + constructor(resourceManager: ResourceManager) { + this.resourceManager = resourceManager; + } + + generateCode(provider: NCloudProvider): string { + const providerProperties = provider.getProperties(); + + const blocks = [ + generateTerraformBlock( + providerProperties.terraform.required_providers.ncloud.source, + providerProperties.terraform.required_version + ), + generateProviderBlock( + provider.name, + providerProperties.provider + ), + ...this.generateResourceBlocks() + ]; + + return blocks.join('\n'); + } + + private generateResourceBlocks(): string[] { + return this.resourceManager.getResources().map(resource => { + const properties = replaceReferences( + resource.getProperties(), + this.resourceManager.getNameMap() + ); + + return generateResourceBlock( + resource.serviceType, + resource.name, + properties + ); + }); + } +} \ No newline at end of file diff --git a/apps/terraform/util/file.ts b/apps/terraform/util/file.ts new file mode 100644 index 00000000..2ff83b7e --- /dev/null +++ b/apps/terraform/util/file.ts @@ -0,0 +1,57 @@ +import { FileOption } from '../interface/FileOption'; + +export const writeFile = async ( + filePath: string, + content: string, + options: FileOption = {} +): Promise => { + const fs = require('fs').promises; + await fs.writeFile(filePath, content); + + if (options.log) { + console.log(`\nGenerated ${filePath}:`); + console.log('----------------------------------------'); + console.log(content); + console.log('----------------------------------------'); + } +}; + +export const generateVariablesContent = (): string => ` +variable "access_key" { + type = string +} + +variable "secret_key" { + type = string +} + +variable "region" { + type = string + default = "KR" +}`; + +export const generateTfvarsContent = (): string => ` +access_key = "your_access_key" +secret_key = "your_secret_key" +region = "KR"`; + +export const saveTerraformFiles = async ( + terraformCode: string, + options: FileOption = {} +): Promise => { + try { + await writeFile('main.tf', terraformCode, options); + + const variablesContent = generateVariablesContent(); + await writeFile('variables.tf', variablesContent); + + const tfvarsContent = generateTfvarsContent(); + await writeFile('terraform.tfvars.example', tfvarsContent); + + console.log('\n테라폼 파일 생성 완료'); + console.log('terraform.tfvars 파일을 생성하고 key를 넣어주세요'); + + } catch (error) { + throw new Error(`Failed to save Terraform files: ${error instanceof Error ? error.message : 'Unknown error'}`); + } +}; diff --git a/apps/terraform/util/formatter.ts b/apps/terraform/util/formatter.ts new file mode 100644 index 00000000..43a74927 --- /dev/null +++ b/apps/terraform/util/formatter.ts @@ -0,0 +1,46 @@ +export const isNcloudReference = (value: string): boolean => { + const ncloudRefPattern = /^ncloud_[a-zA-Z_]+\.[a-zA-Z_-]+\.[a-zA-Z_]+$/; + return ncloudRefPattern.test(value); +}; + +export const isVariableReference = (value: string): boolean => { + const varRefPattern = /^var\.[a-zA-Z_]+$/; + return varRefPattern.test(value); +}; + +export const formatValue = (value: any): string => { + if (Array.isArray(value)) { + return `[${value.map(item => formatValue(item)).join(', ')}]`; + } + + if (typeof value === 'string') { + if (isNcloudReference(value) || isVariableReference(value)) { + return value; + } + return `"${value}"`; + } + + return String(value); +}; + +export const formatProperties = ( + properties: { [key: string]: any }, + indentLevel: number = 1 +): string => { + const indent = ' '.repeat(indentLevel); + const maxKeyLength = Math.max(...Object.keys(properties).map(key => key.length)); + + return Object.entries(properties) + .map(([key, value]) => { + const padding = ' '.repeat(maxKeyLength - key.length); + + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + return `${indent}${key} { +${formatProperties(value, indentLevel + 1)} +${indent}}`; + } + + return `${indent}${key}${padding} = ${formatValue(value)}`; + }) + .join('\n'); +}; diff --git a/apps/terraform/util/generator.ts b/apps/terraform/util/generator.ts new file mode 100644 index 00000000..09fdcfc8 --- /dev/null +++ b/apps/terraform/util/generator.ts @@ -0,0 +1,28 @@ +import { formatProperties } from './formatter'; + +export const generateTerraformBlock = (providerSource: string, version: string): string => ` +terraform { + required_providers { + ncloud = { + source = "${providerSource}" + } + } + required_version = "${version}" +}`; + +export const generateProviderBlock = ( + name: string, + properties: { [key: string]: any } +): string => ` +provider "${name}" { +${formatProperties(properties)} +}`; + +export const generateResourceBlock = ( + serviceType: string, + name: string, + properties: { [key: string]: any } +): string => ` +resource "${serviceType}" "${name}" { +${formatProperties(properties)} +}`; \ No newline at end of file diff --git a/apps/terraform/util/reference.ts b/apps/terraform/util/reference.ts new file mode 100644 index 00000000..0599f1eb --- /dev/null +++ b/apps/terraform/util/reference.ts @@ -0,0 +1,41 @@ +type ReferenceMap = Map; + +export const resolveReference = ( + placeholder: string, + resourceNameMap: ReferenceMap +): string => { + const references: { [key: string]: string } = { + 'VPC_ID_PLACEHOLDER': `ncloud_vpc.${resourceNameMap.get('ncloud_vpc')}.id`, + 'VPC_ACL_PLACEHOLDER': `ncloud_vpc.${resourceNameMap.get('ncloud_vpc')}.default_network_acl_no`, + 'SUBNET_ID_PLACEHOLDER': `ncloud_subnet.${resourceNameMap.get('ncloud_subnet')}.id`, + 'ACG_ID_PLACEHOLDER': `ncloud_access_control_group.${resourceNameMap.get('ncloud_access_control_group')}.id`, + 'LOGIN_KEY_NAME_PLACEHOLDER': `ncloud_login_key.${resourceNameMap.get('ncloud_login_key')}.key_name`, + 'NIC_ID_PLACEHOLDER': `ncloud_network_interface.${resourceNameMap.get('ncloud_network_interface')}.id`, + 'SERVER_ID_PLACEHOLDER': `ncloud_server.${resourceNameMap.get('ncloud_server')}.id` + }; + + return references[placeholder] || placeholder; +}; + +export const replaceReferences = ( + properties: { [key: string]: any }, + resourceNameMap: ReferenceMap +): { [key: string]: any } => { + const result = { ...properties }; + + for (const [key, value] of Object.entries(result)) { + if (typeof value === 'string') { + result[key] = resolveReference(value, resourceNameMap); + } else if (Array.isArray(value)) { + result[key] = value.map(item => + typeof item === 'string' + ? resolveReference(item, resourceNameMap) + : replaceReferences({ value: item }, resourceNameMap).value + ); + } else if (typeof value === 'object' && value !== null) { + result[key] = replaceReferences(value, resourceNameMap); + } + } + + return result; +}; diff --git a/apps/terraform/util/resourceParser.ts b/apps/terraform/util/resourceParser.ts new file mode 100644 index 00000000..f4236cee --- /dev/null +++ b/apps/terraform/util/resourceParser.ts @@ -0,0 +1,79 @@ +import { CloudCanvasNode } from '../interface/CloudCanvasNode'; +import { NCloudModel } from '../interface/NCloudModel'; +import { NCloudVPC } from '../model/NCloudVPC'; +import { NCloudNetworkACL } from '../model/NCloudNetworkACL'; +import { NCloudSubnet } from '../model/NCloudSubnet'; +import { NCloudACG } from '../model/NCloudACG'; +import { NCloudACGRule } from '../model/NCloudACGRule'; +import { NCloudLoginKey } from '../model/NCloudLoginKey'; +import { NCloudNetworkInterface } from '../model/NCloudNetworkInterface'; +import { NCloudServer } from '../model/NCloudServer'; +import { NCloudPublicIP } from '../model/NCloudPublicIP'; + + +export function parseToNCloudModel(resource: CloudCanvasNode ): NCloudModel { + const { type, name, properties } = resource; + + switch (type.toLowerCase()) { + case 'vpc': + return new NCloudVPC({ + name: name || 'vpc', + ipv4CidrBlock: properties.cidrBlock + }); + + case 'networkacl': + return new NCloudNetworkACL({ + name: name || 'nacl' + }); + + case 'subnet': + return new NCloudSubnet({ + name: name || 'subnet', + subnet: properties.subnet, + zone: properties.zone, + subnetType: properties.subnetType, + usageType: properties.usageType + }); + + case 'acg': + case 'accesscontrolgroup': + return new NCloudACG({ + name: name || 'acg', + description: properties.description + }); + + case 'acgrule': + case 'accesscontrolgrouprule': + return new NCloudACGRule({ + protocol: properties.protocol, + ipBlock: properties.ipBlock, + portRange: properties.portRange, + description: properties.description + }); + + case 'loginkey': + return new NCloudLoginKey({ + name: name || 'login-key' + }); + + case 'networkinterface': + return new NCloudNetworkInterface({ + name: name || 'nic' + }); + + case 'server': + return new NCloudServer({ + name: name || 'server', + serverImageProductCode: properties.serverImageProductCode, + serverProductCode: properties.serverProductCode + }); + + case 'publicip': + return new NCloudPublicIP({ + name: name || 'public-ip' + }); + + default: + throw new Error(`Unsupported resource type: ${type}`); + } +} \ No newline at end of file