From c8acadc0c5764ea143ef53cd65130707f5da570a Mon Sep 17 00:00:00 2001 From: HyunSu1768 Date: Tue, 30 Jul 2024 14:53:21 +0900 Subject: [PATCH] =?UTF-8?q?feat=20::=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=8F=99=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/V2ContainerWebAdapter.kt | 10 ++- .../port/in/SyncContainerDomainUseCase.kt | 7 ++ .../service/SyncContainerDomainService.kt | 64 +++++++++++++++++++ .../domain/container/domain/Container.kt | 4 ++ .../client/cloudflare/CloudflareClient.kt | 30 +++++++++ .../dto/request/CreateDnsRecordRequest.kt | 8 +++ .../dto/response/ListDnsRecordsResponse.kt | 59 +++++++++++++++++ .../env/cloudflare/CloudflareProperties.kt | 12 ++++ .../config/KubernetesClientConfig.kt | 6 +- ...uareAwsProperty.kt => XquareProperties.kt} | 5 +- .../security/config/RequestPermitConfig.kt | 1 + src/main/resources/application.yaml | 9 ++- 12 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/xquare/app/xquareinfra/domain/container/application/port/in/SyncContainerDomainUseCase.kt create mode 100644 src/main/kotlin/xquare/app/xquareinfra/domain/container/application/service/SyncContainerDomainService.kt create mode 100644 src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/CloudflareClient.kt create mode 100644 src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/request/CreateDnsRecordRequest.kt create mode 100644 src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/response/ListDnsRecordsResponse.kt create mode 100644 src/main/kotlin/xquare/app/xquareinfra/infrastructure/global/env/cloudflare/CloudflareProperties.kt rename src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/{XquareAwsProperty.kt => XquareProperties.kt} (77%) diff --git a/src/main/kotlin/xquare/app/xquareinfra/domain/container/adapter/V2ContainerWebAdapter.kt b/src/main/kotlin/xquare/app/xquareinfra/domain/container/adapter/V2ContainerWebAdapter.kt index b4ce9ac..58fb103 100644 --- a/src/main/kotlin/xquare/app/xquareinfra/domain/container/adapter/V2ContainerWebAdapter.kt +++ b/src/main/kotlin/xquare/app/xquareinfra/domain/container/adapter/V2ContainerWebAdapter.kt @@ -7,7 +7,6 @@ import xquare.app.xquareinfra.domain.container.adapter.dto.request.CreateNodeWit import xquare.app.xquareinfra.domain.container.adapter.dto.request.SetContainerConfigRequest import xquare.app.xquareinfra.domain.container.adapter.dto.response.GetContainerDeployHistoryResponse import xquare.app.xquareinfra.domain.container.application.port.`in`.* -import xquare.app.xquareinfra.domain.container.application.service.GetContainerDeployHistoryService import xquare.app.xquareinfra.domain.container.domain.ContainerEnvironment import java.util.* @@ -19,7 +18,7 @@ class V2ContainerWebAdapter( private val createNodeWithNginxDockerfileUseCase: CreateNodeWithNginxDockerfileUseCase, private val createNodeDockerfileUseCase: CreateNodeDockerfileUseCase, private val getContainerDeployHistoryUseCase: GetContainerDeployHistoryUseCase, - private val getContainerDeployHistoryService: GetContainerDeployHistoryService + private val syncContainerDomainUseCase: SyncContainerDomainUseCase ) { @PutMapping("/config") fun setContainerConfig( @@ -66,4 +65,11 @@ class V2ContainerWebAdapter( @RequestParam(name = "environment", required = true) containerEnvironment: ContainerEnvironment, ): GetContainerDeployHistoryResponse = getContainerDeployHistoryUseCase.getContainerDeployHistory(deployId, containerEnvironment) + + @PutMapping("/{deployName}/{containerEnvironment}/sync-domain") + fun syncContainerDomain( + @PathVariable("deployName") deployName: String, + @PathVariable("containerEnvironment") containerEnvironment: ContainerEnvironment, + @RequestParam("domain") domain: String + ) = syncContainerDomainUseCase.syncContainerDomain(deployName , containerEnvironment, domain) } \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/port/in/SyncContainerDomainUseCase.kt b/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/port/in/SyncContainerDomainUseCase.kt new file mode 100644 index 0000000..15d367d --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/port/in/SyncContainerDomainUseCase.kt @@ -0,0 +1,7 @@ +package xquare.app.xquareinfra.domain.container.application.port.`in` + +import xquare.app.xquareinfra.domain.container.domain.ContainerEnvironment + +interface SyncContainerDomainUseCase { + fun syncContainerDomain(deployName: String, containerEnvironment: ContainerEnvironment, domain: String) +} \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/service/SyncContainerDomainService.kt b/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/service/SyncContainerDomainService.kt new file mode 100644 index 0000000..2cf43cf --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/domain/container/application/service/SyncContainerDomainService.kt @@ -0,0 +1,64 @@ +package xquare.app.xquareinfra.domain.container.application.service + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import xquare.app.xquareinfra.domain.container.application.port.`in`.SyncContainerDomainUseCase +import xquare.app.xquareinfra.domain.container.application.port.out.FindContainerPort +import xquare.app.xquareinfra.domain.container.domain.ContainerEnvironment +import xquare.app.xquareinfra.domain.deploy.application.port.out.FindDeployPort +import xquare.app.xquareinfra.infrastructure.exception.BusinessLogicException +import xquare.app.xquareinfra.infrastructure.exception.XquareException +import xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.CloudflareClient +import xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.dto.request.CreateDnsRecordRequest +import xquare.app.xquareinfra.infrastructure.global.env.cloudflare.CloudflareProperties +import xquare.app.xquareinfra.infrastructure.kubernetes.env.XquareProperties + +@Transactional +@Service +class SyncContainerDomainService( + private val findDeployPort: FindDeployPort, + private val findContainerPort: FindContainerPort, + private val cloudflareClient: CloudflareClient, + private val cloudflareProperties: CloudflareProperties, + private val xquareProperties: XquareProperties +) : SyncContainerDomainUseCase{ + override fun syncContainerDomain( + deployName: String, + containerEnvironment: ContainerEnvironment, + domain: String + ) { + val deploy = findDeployPort.findByDeployName(deployName) ?: throw BusinessLogicException.DEPLOY_NOT_FOUND + val container = findContainerPort.findByDeployAndEnvironment(deploy, containerEnvironment) ?: throw BusinessLogicException.CONTAINER_NOT_FOUND + + container.updateDomain(domain) + + val listResponse = cloudflareClient.listDnsRecords( + cloudflareProperties.zoneId, + cloudflareProperties.xAuthEmail, + cloudflareProperties.xAuthKey + ) + + if(listResponse.statusCode.isError) { + throw XquareException.INTERNAL_SERVER_ERROR + } + + val records = listResponse.body + if(!records!!.result.any { it.name == domain }) { + val createResponse = cloudflareClient.createDnsRecords( + cloudflareProperties.zoneId, + cloudflareProperties.xAuthEmail, + cloudflareProperties.xAuthKey, + CreateDnsRecordRequest( + content = xquareProperties.gatewayDns, + name = domain, + proxied = false, + type = "CNAME" + ) + ) + + if(createResponse.statusCode.isError) { + throw XquareException.INTERNAL_SERVER_ERROR + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/domain/container/domain/Container.kt b/src/main/kotlin/xquare/app/xquareinfra/domain/container/domain/Container.kt index 7f3f14f..4a7ac1a 100644 --- a/src/main/kotlin/xquare/app/xquareinfra/domain/container/domain/Container.kt +++ b/src/main/kotlin/xquare/app/xquareinfra/domain/container/domain/Container.kt @@ -58,4 +58,8 @@ class Container( fun updateContainerPort(containerPort: Int) { this.containerPort = containerPort } + + fun updateDomain(domain: String) { + this.subDomain = domain + } } \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/CloudflareClient.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/CloudflareClient.kt new file mode 100644 index 0000000..b659697 --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/CloudflareClient.kt @@ -0,0 +1,30 @@ +package xquare.app.xquareinfra.infrastructure.feign.client.cloudflare + +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.dto.request.CreateDnsRecordRequest +import xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.dto.response.ListDnsRecordsResponse +import xquare.app.xquareinfra.infrastructure.feign.config.FeignConfig + +@FeignClient( + name = "cloudflare-client", + url = "\${url.cloudflare}", + configuration = [FeignConfig::class] +) +interface CloudflareClient { + @GetMapping("/zones/{zone_id}/dns_records") + fun listDnsRecords( + @PathVariable("zone_id") zoneId: String, + @RequestHeader("X-Auth-Email") xAuthEmail: String, + @RequestHeader("X-Auth-Key") xAuthKey: String, + ): ResponseEntity + + @PostMapping("/zones/{zone_id}/dns_records") + fun createDnsRecords( + @PathVariable("zone_id") zoneId: String, + @RequestHeader("X-Auth-Email") xAuthEmail: String, + @RequestHeader("X-Auth-Key") xAuthKey: String, + @RequestBody createDnsRecordRequest: CreateDnsRecordRequest + ): ResponseEntity +} \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/request/CreateDnsRecordRequest.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/request/CreateDnsRecordRequest.kt new file mode 100644 index 0000000..4c401ac --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/request/CreateDnsRecordRequest.kt @@ -0,0 +1,8 @@ +package xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.dto.request + +data class CreateDnsRecordRequest( + val content: String, + val name: String, + val proxied: Boolean, + val type: String +) diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/response/ListDnsRecordsResponse.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/response/ListDnsRecordsResponse.kt new file mode 100644 index 0000000..58ae6b0 --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/feign/client/cloudflare/dto/response/ListDnsRecordsResponse.kt @@ -0,0 +1,59 @@ +package xquare.app.xquareinfra.infrastructure.feign.client.cloudflare.dto.response + +import com.fasterxml.jackson.annotation.JsonProperty + +data class ListDnsRecordsResponse( + val result: List, + val success: Boolean, + val errors: List, + val messages: List, + @JsonProperty("result_info") + val resultInfo: ResultInfo, +) + +data class Result( + val id: String, + @JsonProperty("zone_id") + val zoneId: String, + @JsonProperty("zone_name") + val zoneName: String, + val name: String, + val type: String, + val content: String, + val proxiable: Boolean, + val proxied: Boolean, + val ttl: Long, + val locked: Boolean, + val meta: Meta, + val comment: Any?, + val tags: List, + @JsonProperty("created_on") + val createdOn: String, + @JsonProperty("modified_on") + val modifiedOn: String, + val priority: Long?, +) + +data class Meta( + @JsonProperty("auto_added") + val autoAdded: Boolean, + @JsonProperty("managed_by_apps") + val managedByApps: Boolean, + @JsonProperty("managed_by_argo_tunnel") + val managedByArgoTunnel: Boolean, + @JsonProperty("email_routing") + val emailRouting: Boolean?, + @JsonProperty("read_only") + val readOnly: Boolean?, +) + +data class ResultInfo( + val page: Long, + @JsonProperty("per_page") + val perPage: Long, + val count: Long, + @JsonProperty("total_count") + val totalCount: Long, + @JsonProperty("total_pages") + val totalPages: Long, +) diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/global/env/cloudflare/CloudflareProperties.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/global/env/cloudflare/CloudflareProperties.kt new file mode 100644 index 0000000..f0173bb --- /dev/null +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/global/env/cloudflare/CloudflareProperties.kt @@ -0,0 +1,12 @@ +package xquare.app.xquareinfra.infrastructure.global.env.cloudflare + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConstructorBinding +@ConfigurationProperties(value = "cloudflare") +data class CloudflareProperties( + val zoneId: String, + val xAuthKey: String, + val xAuthEmail: String +) \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/config/KubernetesClientConfig.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/config/KubernetesClientConfig.kt index 09a2c27..d6b10de 100644 --- a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/config/KubernetesClientConfig.kt +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/config/KubernetesClientConfig.kt @@ -1,7 +1,7 @@ package xquare.app.xquareinfra.infrastructure.kubernetes.config import xquare.app.xquareinfra.infrastructure.kubernetes.env.KubernetesProperty -import xquare.app.xquareinfra.infrastructure.kubernetes.env.XquareAwsProperty +import xquare.app.xquareinfra.infrastructure.kubernetes.env.XquareProperties import io.kubernetes.client.openapi.Configuration import io.kubernetes.client.openapi.apis.CoreV1Api import io.kubernetes.client.openapi.apis.CustomObjectsApi @@ -17,12 +17,12 @@ import javax.annotation.PostConstruct @org.springframework.context.annotation.Configuration class KubernetesClientConfig( - private val xquareAwsProperty: XquareAwsProperty, + private val xquareProperties: XquareProperties, private val kubernetesProperty: KubernetesProperty ) { @PostConstruct fun initKubernetesConfig() { - configureAWS("default", xquareAwsProperty.accessKey, xquareAwsProperty.secretKey, Region.AP_NORTHEAST_2.toString()) + configureAWS("default", xquareProperties.accessKey, xquareProperties.secretKey, Region.AP_NORTHEAST_2.toString()) val decodedBytes = Base64.getDecoder().decode(kubernetesProperty.kubeConfig) val kubeconfig = String(decodedBytes, Charset.defaultCharset()) val client = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(StringReader(kubeconfig))).build() diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareAwsProperty.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareProperties.kt similarity index 77% rename from src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareAwsProperty.kt rename to src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareProperties.kt index ab01d93..b349130 100644 --- a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareAwsProperty.kt +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/kubernetes/env/XquareProperties.kt @@ -5,7 +5,8 @@ import org.springframework.boot.context.properties.ConstructorBinding @ConfigurationProperties("xquare") @ConstructorBinding -data class XquareAwsProperty( +data class XquareProperties( val accessKey: String, - val secretKey: String + val secretKey: String, + val gatewayDns: String ) \ No newline at end of file diff --git a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/security/config/RequestPermitConfig.kt b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/security/config/RequestPermitConfig.kt index 50bf6ad..f69a79c 100644 --- a/src/main/kotlin/xquare/app/xquareinfra/infrastructure/security/config/RequestPermitConfig.kt +++ b/src/main/kotlin/xquare/app/xquareinfra/infrastructure/security/config/RequestPermitConfig.kt @@ -17,6 +17,7 @@ class RequestPermitConfig : SecurityConfigurerAdapter