Skip to content

Commit 5c9e4aa

Browse files
Merge pull request #175 from jmjeong1/main
Change token url to localhost spider host in kubeconfig
2 parents 557ab0f + 5956d37 commit 5c9e4aa

File tree

3 files changed

+52
-236
lines changed

3 files changed

+52
-236
lines changed

src/main/java/kr/co/mcmp/softwarecatalog/kubernetes/config/AwsKubeConfigProvider.java

Lines changed: 23 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,27 @@
1111
import java.net.URI;
1212
import java.net.URLEncoder;
1313
import java.nio.charset.StandardCharsets;
14+
import java.util.List;
1415

1516
@Component
1617
public class AwsKubeConfigProvider implements KubeConfigProvider {
1718

18-
@Value("${cbtumblebug.url}")
19-
private String tumblebugUrl;
20-
21-
@Value("${cbtumblebug.port}")
22-
private String tumblebugPort;
23-
24-
@Value("${cbtumblebug.id}")
25-
private String tumblebugId;
26-
27-
@Value("${cbtumblebug.pass}")
28-
private String tumblebugPassword;
19+
@Value("${spider.url}")
20+
private String spiderUrl;
21+
22+
@Value("${spider.port}")
23+
private String spiderPort;
24+
25+
@Override
26+
public Config buildConfig(K8sClusterDto dto) {
27+
String yaml = dto.getAccessInfo().getKubeconfig();
28+
Config cfg = Config.fromKubeconfig(KubeConfigProviderFactory.replaceUrlHostByPort(yaml, spiderPort, List.of("localhost"), spiderUrl));
29+
System.out.println("------------------- kubeconfig: \n" + cfg);
30+
cfg.setTrustCerts(true);
31+
cfg.setConnectionTimeout(30_000);
32+
cfg.setRequestTimeout(30_000);
33+
return cfg;
34+
}
2935

3036
@Override
3137
public boolean supports(String providerName) {
@@ -37,238 +43,19 @@ public String getOriginalKubeconfigYaml(K8sClusterDto dto) {
3743
if (dto == null) {
3844
throw new IllegalArgumentException("K8sClusterDto cannot be null");
3945
}
40-
46+
4147
if (dto.getAccessInfo() == null) {
42-
throw new IllegalStateException("AccessInfo is null for AWS cluster: " + dto.getName());
48+
throw new IllegalStateException("AccessInfo is null for Azure cluster: " + dto.getName());
4349
}
44-
50+
4551
String kubeconfig = dto.getAccessInfo().getKubeconfig();
4652
if (kubeconfig == null || kubeconfig.trim().isEmpty()) {
47-
throw new IllegalStateException("Kubeconfig is null or empty for AWS cluster: " + dto.getName());
53+
throw new IllegalStateException("Kubeconfig is null or empty for Azure cluster: " + dto.getName());
4854
}
49-
50-
// AWS의 경우 localhost를 실제 Tumblebug 주소로 변경
51-
return processAwsKubeconfig(kubeconfig);
52-
}
53-
54-
@Override
55-
public Config buildConfig(K8sClusterDto dto) {
56-
// String yaml = dto.getAccessInfo().getKubeconfig();
57-
// String server = extractServer(yaml);
58-
// String ca = extractCertificateAuthorityData(yaml);
59-
// String clusterName = dto.getLabel().get("sys.cspResourceName");
6055

61-
// String token = EksClient.builder()
62-
// .region(Region.of(System.getenv("AWS_REGION")))
63-
// .credentialsProvider(DefaultCredentialsProvider.create())
64-
// .build()
65-
// .getToken(GetTokenRequest.builder().clusterName(clusterName).build())
66-
// .token();
56+
System.out.println("------------------- kubeconfig: \n" + KubeConfigProviderFactory.replaceUrlHostByPort(kubeconfig, spiderPort, List.of("localhost"), spiderUrl));
6757

68-
// Config cfg = new Config();
69-
// cfg.setMasterUrl(server);
70-
// cfg.setCaCertData(ca);
71-
// // cfg.setOauthToken(token);
72-
// cfg.setTrustCerts(true);
73-
// cfg.setConnectionTimeout(30_000);
74-
// cfg.setRequestTimeout(30_000);
75-
// return cfg;
76-
return null;
77-
}
78-
79-
private String extractServer(String yaml) {
80-
for (String line : yaml.split("\\r?\\n")) {
81-
if (line.trim().startsWith("server:")) {
82-
return line.split("server:")[1].trim();
83-
}
84-
}
85-
throw new IllegalStateException("server URL을 찾을 수 없습니다");
58+
return KubeConfigProviderFactory.replaceUrlHostByPort(kubeconfig, spiderPort, List.of("localhost"), spiderUrl);
8659
}
8760

88-
private String extractCertificateAuthorityData(String yaml) {
89-
StringBuilder sb = new StringBuilder();
90-
boolean inBlock = false;
91-
for (String line : yaml.split("\\r?\\n")) {
92-
if (line.trim().startsWith("certificate-authority-data:")) {
93-
inBlock = true;
94-
sb.append(line.substring(line.indexOf(':') + 1).trim());
95-
} else if (inBlock && (line.startsWith(" ") || line.startsWith("\t"))) {
96-
sb.append(line.trim());
97-
} else if (inBlock) {
98-
break;
99-
}
100-
}
101-
if (sb.isEmpty()) throw new IllegalStateException("certificate-authority-data를 찾을 수 없습니다");
102-
return sb.toString();
103-
}
104-
105-
/**
106-
* AWS kubeconfig에서 localhost를 Tumblebug IP로 변경하고 exec 플러그인을 제거하여 직접 토큰 사용
107-
*/
108-
private String processAwsKubeconfig(String kubeconfig) {
109-
System.out.println("=== AWS kubeconfig 처리 시작 ===");
110-
try {
111-
// 1. localhost만 Tumblebug IP로 변경 (포트는 그대로 유지)
112-
String processedKubeconfig = kubeconfig.replace("localhost:", tumblebugUrl + ":");
113-
System.out.println("1. 주소 변경: localhost -> " + tumblebugUrl);
114-
115-
// 2. kubeconfig에서 cluster ID와 connection name 추출
116-
String clusterId = extractClusterId(processedKubeconfig);
117-
String connectionName = extractConnectionName(processedKubeconfig);
118-
System.out.println("2. 추출된 정보 - clusterId: " + clusterId + ", connectionName: " + connectionName);
119-
120-
// 3. 현재 토큰 가져오기
121-
System.out.println("3. 토큰 요청 시작...");
122-
String token = getCurrentToken(clusterId, connectionName);
123-
System.out.println("4. 토큰 가져오기 성공: " + token.substring(0, Math.min(20, token.length())) + "...");
124-
125-
// 4. kubeconfig에서 exec 플러그인을 제거하고 직접 토큰 설정
126-
processedKubeconfig = replaceExecWithToken(processedKubeconfig, token);
127-
System.out.println("5. kubeconfig 수정 완료");
128-
129-
return processedKubeconfig;
130-
} catch (Exception e) {
131-
System.err.println("토큰 가져오기 실패, 원본 kubeconfig 사용: " + e.getMessage());
132-
e.printStackTrace();
133-
return kubeconfig;
134-
}
135-
}
136-
137-
/**
138-
* kubeconfig에서 cluster ID 추출
139-
*/
140-
private String extractClusterId(String kubeconfig) {
141-
for (String line : kubeconfig.split("\n")) {
142-
if (line.contains("/spider/cluster/") && line.contains("/token")) {
143-
// "http://localhost:1024/spider/cluster/CLUSTER_ID/token?ConnectionName=..."
144-
String[] parts = line.split("/spider/cluster/");
145-
if (parts.length > 1) {
146-
String clusterPart = parts[1].split("/token")[0];
147-
return clusterPart;
148-
}
149-
}
150-
}
151-
throw new RuntimeException("kubeconfig에서 cluster ID를 찾을 수 없습니다");
152-
}
153-
154-
/**
155-
* kubeconfig에서 connection name 추출
156-
*/
157-
private String extractConnectionName(String kubeconfig) {
158-
for (String line : kubeconfig.split("\n")) {
159-
if (line.contains("ConnectionName=")) {
160-
// "http://localhost:1024/spider/cluster/.../token?ConnectionName=CONNECTION_NAME"
161-
String[] parts = line.split("ConnectionName=");
162-
if (parts.length > 1) {
163-
return parts[1].trim();
164-
}
165-
}
166-
}
167-
throw new RuntimeException("kubeconfig에서 ConnectionName을 찾을 수 없습니다");
168-
}
169-
170-
/**
171-
* Tumblebug에서 현재 토큰을 가져옵니다 (Java HTTP 클라이언트 사용)
172-
*/
173-
private String getCurrentToken(String clusterId, String connectionName) throws Exception {
174-
// URL 인코딩 처리 (백슬래시 및 따옴표 문제 해결)
175-
String cleanConnectionName = connectionName.replace("\\", "").replace("\"", "").trim();
176-
String encodedConnectionName = URLEncoder.encode(cleanConnectionName, StandardCharsets.UTF_8);
177-
String tokenUrl = "http://" + tumblebugUrl + ":1024/spider/cluster/" + clusterId + "/token?ConnectionName=" + encodedConnectionName;
178-
179-
System.out.println("토큰 요청 URL: " + tokenUrl);
180-
181-
// Java HTTP 클라이언트로 토큰 가져오기 (인증 헤더 포함)
182-
HttpClient client = HttpClient.newHttpClient();
183-
184-
// Basic Auth 헤더 생성
185-
String auth = tumblebugId + ":" + tumblebugPassword;
186-
String encodedAuth = java.util.Base64.getEncoder().encodeToString(auth.getBytes());
187-
188-
HttpRequest request = HttpRequest.newBuilder()
189-
.uri(URI.create(tokenUrl))
190-
.header("Authorization", "Basic " + encodedAuth)
191-
.GET()
192-
.build();
193-
194-
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
195-
196-
if (response.statusCode() != 200) {
197-
throw new RuntimeException("토큰 요청 실패: HTTP " + response.statusCode() + " - " + response.body());
198-
}
199-
200-
String responseBody = response.body();
201-
System.out.println("Tumblebug 응답: " + responseBody);
202-
203-
// JSON 응답에서 token 추출 (더 정확한 파싱)
204-
if (responseBody.contains("\"token\"")) {
205-
// "token":"실제토큰값" 형태에서 토큰 추출
206-
int tokenStart = responseBody.indexOf("\"token\":\"") + 9;
207-
int tokenEnd = responseBody.indexOf("\"", tokenStart);
208-
if (tokenStart > 8 && tokenEnd > tokenStart) {
209-
String token = responseBody.substring(tokenStart, tokenEnd);
210-
return token;
211-
}
212-
}
213-
214-
throw new RuntimeException("토큰을 찾을 수 없습니다: " + responseBody);
215-
}
216-
217-
/**
218-
* kubeconfig에서 exec 플러그인을 제거하고 직접 토큰을 설정
219-
*/
220-
private String replaceExecWithToken(String kubeconfig, String token) {
221-
System.out.println("=== kubeconfig 수정 시작 ===");
222-
223-
// kubeconfig를 줄별로 분석하여 정확한 YAML 구조 유지
224-
String[] lines = kubeconfig.split("\n");
225-
StringBuilder result = new StringBuilder();
226-
boolean inExecBlock = false;
227-
boolean userBlockFound = false;
228-
boolean tokenAdded = false;
229-
230-
for (int i = 0; i < lines.length; i++) {
231-
String line = lines[i];
232-
233-
if (line.trim().startsWith("users:")) {
234-
userBlockFound = true;
235-
result.append(line).append("\n");
236-
} else if (userBlockFound && line.trim().startsWith("exec:")) {
237-
inExecBlock = true;
238-
System.out.println("exec 블록 발견, 토큰으로 교체 중...");
239-
// exec: 라인을 token: 라인으로 교체
240-
result.append(" token: ").append(token).append("\n");
241-
tokenAdded = true;
242-
// exec 블록의 나머지 라인들 건너뛰기
243-
i++;
244-
while (i < lines.length && (lines[i].startsWith(" ") || lines[i].startsWith(" "))) {
245-
i++;
246-
}
247-
i--; // 다음 반복에서 현재 라인을 처리하도록
248-
} else if (inExecBlock && (line.startsWith(" ") || line.startsWith(" "))) {
249-
// exec 블록 내부 라인들 건너뛰기
250-
continue;
251-
} else if (inExecBlock && !line.startsWith(" ") && !line.startsWith("\t")) {
252-
// exec 블록 종료
253-
inExecBlock = false;
254-
result.append(line).append("\n");
255-
} else {
256-
result.append(line).append("\n");
257-
}
258-
}
259-
260-
// 토큰이 추가되지 않았다면 users 섹션에 직접 추가
261-
if (!tokenAdded && userBlockFound) {
262-
System.out.println("exec 블록을 찾을 수 없음, users 섹션에 토큰 추가");
263-
String modifiedKubeconfig = result.toString();
264-
modifiedKubeconfig = modifiedKubeconfig.replace(
265-
"users:\n- name: aws-dynamic-token\n user:",
266-
"users:\n- name: aws-dynamic-token\n user:\n token: " + token
267-
);
268-
return modifiedKubeconfig;
269-
}
270-
271-
System.out.println("=== kubeconfig 수정 완료 ===");
272-
return result.toString();
273-
}
27461
}

src/main/java/kr/co/mcmp/softwarecatalog/kubernetes/config/KubeConfigProviderFactory.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package kr.co.mcmp.softwarecatalog.kubernetes.config;
22

33
import java.util.List;
4+
import java.util.regex.Matcher;
5+
import java.util.regex.Pattern;
46

57
import org.springframework.stereotype.Component;
68

@@ -18,4 +20,27 @@ public KubeConfigProvider getProvider(String providerName) {
1820
.findFirst()
1921
.orElseThrow(() -> new IllegalArgumentException("Unsupported CSP: " + providerName));
2022
}
23+
24+
public static String replaceUrlHostByPort(String text, String checkPort, List<String> checkHost, String newHost) {
25+
String urlRegex = "(?<protocol>https?)://(?<host>[^:/\\s]+)(:(?<port>\\d+))?(?<filePart>/[^\\s]*)?";
26+
Pattern pattern = Pattern.compile(urlRegex);
27+
Matcher matcher = pattern.matcher(text);
28+
29+
StringBuffer resultBuffer = new StringBuffer();
30+
while (matcher.find()) {
31+
String host = matcher.group("host").toLowerCase();
32+
String port = matcher.group("port");
33+
if (port != null && port.equals(checkPort) && checkHost.contains(host)) {
34+
String protocol = matcher.group("protocol");
35+
String filePart = matcher.group("filePart") != null ? matcher.group("filePart") : "";
36+
matcher.appendReplacement(resultBuffer, Matcher.quoteReplacement(protocol + "://" + newHost + ":" + checkPort + filePart));
37+
} else {
38+
matcher.appendReplacement(resultBuffer, matcher.group(0));
39+
}
40+
}
41+
matcher.appendTail(resultBuffer);
42+
43+
return resultBuffer.toString();
44+
}
45+
2146
}

src/main/resources/application.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ cbtumblebug:
7272
id: ${TUMBLEBUG_ID:default}
7373
pass: ${TUMBLEBUG_PASSWORD:default}
7474

75+
spider:
76+
url: ${SPIDER_URL:210.217.178.130}
77+
port: ${SPIDER_PORT:1024}
78+
7579
nexus:
7680
url: ${NEXUS_URL:nexus-repository}
7781
port: ${NEXUS_PORT:8081}

0 commit comments

Comments
 (0)