-
Notifications
You must be signed in to change notification settings - Fork 3
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
NCloud Terraform 변환 코드 구현 #133
Merged
Gdm0714
merged 24 commits into
boostcampwm-2024:development
from
Gdm0714:feature/convertTerraform
Nov 22, 2024
Merged
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
eb1286c
✨ Feat: terraform을 이용하여 NCloud server 생성하는 코드 구현
Gdm0714 0cde358
✨ Feat: 각 리소스 인터페이스 정의
Gdm0714 815b67a
✨ Feat: nCloud 모델 구현
Gdm0714 a3ef4a6
✨ Feat: nCloud Resource 모델 구현
Gdm0714 77a62bb
✨ Feat: networkACL, PublicIP, LoginKey 모델 구현
Gdm0714 5e4a16f
✨ Feat: 모델 priority 속성 추가하여 실행 순서 제어
Gdm0714 3647eb7
✨ Feat: netwrokACL interface추가 및 NCloud 모델에 priority 추가
Gdm0714 1f073cf
✨ Feat: resource Priority enum 타입 추가
Gdm0714 25afd78
✨ Feat: 테라폼 코드로 바꿔주는 terraform Convertor 구현
Gdm0714 9fc7a0c
✨ Feat: terraform convertor 테스트 가능한 main 구현
Gdm0714 b279228
🤖 Refactor: 테라폼 서버 생성 관련 파일 삭제 및 variables.tf 재생성
Gdm0714 072b46c
💄 Style: terraform convertor 코드 포맷 변경
Gdm0714 b9e0a15
🤖 Refactor: 불필요한 import 삭제
Gdm0714 74cf37b
✨ Feat: jSON 데이터를 받아 파싱하기 위한 interface 정의
Gdm0714 0f6748a
🤖 Refactor: 필요없는 import 제거
Gdm0714 17435ac
✨ Feat: resource json Data 받아서 모델로 파싱해주는 함수 구현
Gdm0714 a51c087
✨ Feat: resource json Data를 받아서 모델을 이용해 파싱
Gdm0714 4cd75bf
✨ Feat: nCloud subnet 참조값 처리 구현
Gdm0714 bac4156
✨ Feat: 나머지 리소스들에 대한 참조값 처리 구현
Gdm0714 f9525d2
🐞 Fix: json 파싱에서 properties안에 있던 name 분리
Gdm0714 3112fe4
🐞 Fix: 배열 리소스 참조 처리 개선
Gdm0714 df18552
🤖 Refactor: terraformConvertor 각 역할이 명확하게 파일 분리
Gdm0714 7b38f1c
🤖 Refactor: 실행 테스트 파일 각 기능이 명확하게 파일 분리
Gdm0714 60f46ea
🤖 Refactor: terrformConvertor 각 기능이 명확하게 파일 분리
Gdm0714 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { NCloudModel } from '../interface/NCloudModel'; | ||
import { NCloudProvider } from '../model/NCloudProvider'; | ||
import { CloudCanvasNode } from '../interface/CloudCanvasNode'; | ||
import { parseToNCloudModel } from '../util/resourceParser'; | ||
|
||
export class TerraformConvertor { | ||
private resources: NCloudModel[]; | ||
private provider: NCloudProvider; | ||
private resourceNameMap: Map<string, string>; | ||
|
||
constructor(provider: NCloudProvider) { | ||
this.provider = provider; | ||
this.resources = []; | ||
this.resourceNameMap = new Map(); | ||
} | ||
|
||
addResourceFromJson(jsonData: { nodes?: CloudCanvasNode[] }) { | ||
jsonData.nodes?.forEach((node => { | ||
try { | ||
const resource = parseToNCloudModel(node); | ||
this.addResource(resource); | ||
} catch (error) { | ||
console.warn(`Skipping unsupported node type: ${node.type}`); | ||
} | ||
})); | ||
} | ||
|
||
addResource(resource: NCloudModel) { | ||
this.resources.push(resource); | ||
this.resourceNameMap.set(resource.serviceType, resource.name); | ||
} | ||
|
||
|
||
|
||
private replaceReferences(properties: { [key: string]: any }): { [key: string]: any } { | ||
const result = { ...properties }; | ||
|
||
for (const [key, value] of Object.entries(result)) { | ||
if (typeof value === 'string') { | ||
switch(value) { | ||
case 'VPC_ID_PLACEHOLDER': | ||
const vpcName = this.resourceNameMap.get('ncloud_vpc'); | ||
result[key] = `ncloud_vpc.${vpcName}.id`; | ||
break; | ||
case 'VPC_ACL_PLACEHOLDER': | ||
const vpcAclName = this.resourceNameMap.get('ncloud_vpc'); | ||
result[key] = `ncloud_vpc.${vpcAclName}.default_network_acl_no`; | ||
break; | ||
case 'SUBNET_ID_PLACEHOLDER': | ||
const subnetName = this.resourceNameMap.get('ncloud_subnet'); | ||
result[key] = `ncloud_subnet.${subnetName}.id`; | ||
break; | ||
case 'ACG_ID_PLACEHOLDER': | ||
const acgName = this.resourceNameMap.get('ncloud_access_control_group'); | ||
result[key] = `ncloud_access_control_group.${acgName}.id`; | ||
break; | ||
case 'LOGIN_KEY_NAME_PLACEHOLDER': | ||
const loginKeyName = this.resourceNameMap.get('ncloud_login_key'); | ||
result[key] = `ncloud_login_key.${loginKeyName}.key_name`; | ||
break; | ||
case 'NIC_ID_PLACEHOLDER': | ||
const nicName = this.resourceNameMap.get('ncloud_network_interface'); | ||
result[key] = `ncloud_network_interface.${nicName}.id`; | ||
break; | ||
case 'SERVER_ID_PLACEHOLDER': | ||
const serverName = this.resourceNameMap.get('ncloud_server'); | ||
result[key] = `ncloud_server.${serverName}.id`; | ||
break; | ||
} | ||
} else if (Array.isArray(value)) { | ||
result[key] = value.map(item => { | ||
if (typeof item === 'string') { | ||
const replacedValue = this.replaceReferences({ temp: item }).temp; | ||
return replacedValue; | ||
} | ||
return this.replaceReferences(item); | ||
}); | ||
} else if (typeof value === 'object' && value !== null) { | ||
result[key] = this.replaceReferences(value); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private formatValue(value: any): string { | ||
if (Array.isArray(value)) { | ||
return `[${value.map(item => this.formatValue(item)).join(', ')}]`; | ||
} | ||
|
||
if (typeof value === 'string') { | ||
const ncloudRefPattern = /^ncloud_[a-zA-Z_]+\.[a-zA-Z_-]+\.[a-zA-Z_]+$/; | ||
const varRefPattern = /^var\.[a-zA-Z_]+$/; | ||
|
||
if (ncloudRefPattern.test(value) || varRefPattern.test(value)) { | ||
return value; | ||
} | ||
|
||
return `"${value}"`; | ||
} | ||
return value; | ||
} | ||
|
||
private formatProperties(properties: { [key: string]: any }): string { | ||
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 ` ${key} { | ||
${this.formatProperties(value)} | ||
}`; | ||
} | ||
|
||
return ` ${key}${padding} = ${this.formatValue(value)}`; | ||
}) | ||
.join('\n'); | ||
} | ||
generate(): string { | ||
const providerProperties = this.provider.getProperties(); | ||
|
||
const terraformBlock = ` | ||
terraform { | ||
required_providers { | ||
ncloud = { | ||
source = "${providerProperties.terraform.required_providers.ncloud.source}" | ||
} | ||
} | ||
required_version = "${providerProperties.terraform.required_version}" | ||
}`; | ||
|
||
const providerBlock = ` | ||
provider "${this.provider.name}" { | ||
${this.formatProperties(providerProperties.provider)} | ||
}`; | ||
|
||
const resourceBlocks = this.resources | ||
.sort((a, b) => a.priority - b.priority) | ||
.map(resource => { | ||
const properties = this.replaceReferences(resource.getProperties()); | ||
return ` | ||
resource "${resource.serviceType}" "${resource.name}" { | ||
${this.formatProperties(properties)} | ||
}`; | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 문자열 템플릿 부분을 utils로 분리하는건 어떨까요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 포크된 레포에는 이미 반영된 상태이고 머지 이후에 다시 pr올릴려고 했는데 그냥 올리겠습니다 |
||
|
||
return [terraformBlock, providerBlock, ...resourceBlocks].join('\n'); | ||
} | ||
|
||
async saveToFile(filePath: string): Promise<void> { | ||
const fs = require('fs').promises; | ||
const terraformCode = this.generate(); | ||
console.log(terraformCode); | ||
await fs.writeFile(filePath, terraformCode); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface ACG { | ||
id: string; | ||
name: string; | ||
vpcNo: string; | ||
description: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface ACGRule { | ||
protocol: string; | ||
ipBlock: string; | ||
portRange: string; | ||
description: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface CloudCanvasNode { | ||
id: string; | ||
type: string; | ||
name: string; | ||
properties: { [key: string]: any }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { ResourcePriority } from '../enum/ResourcePriority'; | ||
|
||
export interface NCloudModel { | ||
name: string; | ||
serviceType: string; | ||
priority: ResourcePriority; | ||
getProperties(): { [key: string]: any }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface NetworkACL { | ||
id: string; | ||
name: string; | ||
vpcNo: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface NetworkInterface { | ||
id: string; | ||
name: string; | ||
subnetNo: string; | ||
accessControlGroups: string[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface Provider { | ||
accessKey: string; | ||
secretKey: string; | ||
region: string; | ||
site: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface PublicIp { | ||
id: string; | ||
publicIp: string; | ||
serverInstanceNo: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export interface Server { | ||
id: string; | ||
name: string; | ||
subnetNo: string; | ||
serverImageProductCode: string; | ||
serverProductCode: string; | ||
loginKeyName: string; | ||
networkInterfaceNo: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export interface Subnet { | ||
id: string; | ||
name: string; | ||
vpcNo: string; | ||
subnet: string; | ||
zone: string; | ||
networkAclNo: string; | ||
subnetType: string; | ||
usageType: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export interface VPC { | ||
id: string; | ||
name: string; | ||
region: string; | ||
ipv4CidrBlock: string; | ||
defaultNetworkAclNo: string; | ||
defaultAccessControlGroupNo: string; | ||
defaultPublicRouteTableNo: string; | ||
defaultPrivateRouteTableNo: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { CloudCanvasNode } from './interface/CloudCanvasNode'; | ||
import { NCloudProvider } from './model/NCloudProvider'; | ||
import { TerraformConvertor } from './convertor/TerraformConvertor'; | ||
|
||
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: {} | ||
} | ||
]; | ||
|
||
async function main() { | ||
try { | ||
const provider = new NCloudProvider({ | ||
accessKey: "var.access_key", | ||
secretKey: "var.secret_key", | ||
region: "var.region", | ||
site: "public" | ||
}); | ||
|
||
const convertor = new TerraformConvertor(provider); | ||
|
||
convertor.addResourceFromJson({ nodes: sampleNodes }); | ||
await convertor.saveToFile('main.tf'); | ||
console.log('\nGenerated Terraform code:'); | ||
console.log('----------------------------------------'); | ||
console.log(convertor.generate()); | ||
console.log('----------------------------------------'); | ||
|
||
} catch (error) { | ||
console.error('Error generating Terraform configuration:', error); | ||
} | ||
} | ||
|
||
main().catch(console.error); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시
jsonData
를 객체로 설정하신 이유가 있나요? 바로CloudCanvasNode[]
배열을 받는건 어떤가요?