Skip to content

Commit 95dd1c2

Browse files
committed
Rework the gitconfig configmap configurator
1 parent e531c3c commit 95dd1c2

File tree

6 files changed

+417
-256
lines changed

6 files changed

+417
-256
lines changed

infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
4949
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.RemoveNamespaceOnWorkspaceRemove;
5050
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
51-
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.GitconfigUserDataConfigurator;
51+
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.GitconfigConfigmapConfigurator;
5252
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
5353
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.OAuthTokenSecretsConfigurator;
5454
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.PreferencesConfigMapConfigurator;
@@ -109,7 +109,7 @@ protected void configure() {
109109
namespaceConfigurators.addBinding().to(WorkspaceServiceAccountConfigurator.class);
110110
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
111111
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
112-
namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class);
112+
namespaceConfigurators.addBinding().to(GitconfigConfigmapConfigurator.class);
113113

114114
bind(AuthorizationChecker.class).to(KubernetesAuthorizationCheckerImpl.class);
115115
bind(PermissionsCleaner.class).asEagerSingleton();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* Copyright (c) 2012-2025 Red Hat, Inc.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Red Hat, Inc. - initial API and implementation
11+
*/
12+
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
13+
14+
import static com.google.common.base.Strings.isNullOrEmpty;
15+
16+
import com.google.common.collect.ImmutableMap;
17+
import io.fabric8.kubernetes.api.model.ConfigMap;
18+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
19+
import io.fabric8.kubernetes.client.KubernetesClient;
20+
import java.util.Map;
21+
import java.util.Optional;
22+
import java.util.Set;
23+
import java.util.StringJoiner;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
import javax.inject.Inject;
27+
import org.eclipse.che.api.factory.server.scm.GitUserData;
28+
import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
29+
import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
30+
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
31+
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
32+
import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
33+
import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
34+
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
35+
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
36+
import org.eclipse.che.commons.lang.Pair;
37+
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
38+
import org.slf4j.Logger;
39+
import org.slf4j.LoggerFactory;
40+
41+
public class GitconfigConfigmapConfigurator implements NamespaceConfigurator {
42+
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
43+
private final Set<GitUserDataFetcher> gitUserDataFetchers;
44+
private static final Logger LOG = LoggerFactory.getLogger(GitconfigConfigmapConfigurator.class);
45+
private static final String CONFIGMAP_DATA_KEY = "gitconfig";
46+
// TODO: rename to a more generic name, since it is not only for user data
47+
private static final String GITCONFIG_CONFIGMAP_NAME = "workspace-userdata-gitconfig-configmap";
48+
private static final Map<String, String> GITCONFIG_CONFIGMAP_LABELS =
49+
ImmutableMap.of(
50+
"controller.devfile.io/mount-to-devworkspace",
51+
"true",
52+
"controller.devfile.io/watch-configmap",
53+
"true");
54+
private static final Map<String, String> GITCONFIG_CONFIGMAP_ANNOTATIONS =
55+
ImmutableMap.of(
56+
"controller.devfile.io/mount-as", "subpath", "controller.devfile.io/mount-path", "/etc");
57+
private final Pattern usernmaePattern =
58+
Pattern.compile("\\[user](.|\\s)*name\\s*=\\s*(?<username>.*)");
59+
private final Pattern emailPattern =
60+
Pattern.compile("\\[user](.|\\s)*email\\s*=\\s*(?<email>.*)");
61+
private final Pattern emptyStringPattern = Pattern.compile("[\"']\\s*[\"']");
62+
private final Pattern gitconfigSectionPattern =
63+
Pattern.compile("\\[(?<sectionName>[a-zA-Z0-9]+)](\\n\\s*\\S*\\s*=.*)*");
64+
65+
@Inject
66+
public GitconfigConfigmapConfigurator(
67+
CheServerKubernetesClientFactory cheServerKubernetesClientFactory,
68+
Set<GitUserDataFetcher> gitUserDataFetchers) {
69+
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
70+
this.gitUserDataFetchers = gitUserDataFetchers;
71+
}
72+
73+
@Override
74+
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
75+
throws InfrastructureException {
76+
KubernetesClient client = cheServerKubernetesClientFactory.create();
77+
Optional<String> gitconfigOptional = getGitconfig(client, namespaceName);
78+
Optional<Pair<String, String>> usernameAndEmailFromGitconfigOptional = Optional.empty();
79+
Optional<Pair<String, String>> usernameAndEmailFromFetcherOptional =
80+
getUsernameAndEmailFromFetcher();
81+
if (gitconfigOptional.isPresent()) {
82+
String gitconfig = gitconfigOptional.get();
83+
usernameAndEmailFromGitconfigOptional = getUsernameAndEmailFromGitconfig(gitconfig);
84+
}
85+
if (needUpdateGitconfigConfigmap(
86+
usernameAndEmailFromGitconfigOptional, usernameAndEmailFromFetcherOptional)) {
87+
ConfigMap gitconfigConfigmap = buildGitconfigConfigmap();
88+
Optional<Pair<String, String>> usernameAndEmailOptional =
89+
usernameAndEmailFromGitconfigOptional.isPresent()
90+
? usernameAndEmailFromGitconfigOptional
91+
: usernameAndEmailFromFetcherOptional;
92+
Optional<String> gitconfigSectionsOptional =
93+
generateGitconfigSections(gitconfigOptional, usernameAndEmailOptional);
94+
gitconfigConfigmap.setData(
95+
ImmutableMap.of(CONFIGMAP_DATA_KEY, gitconfigSectionsOptional.orElse("")));
96+
client.configMaps().inNamespace(namespaceName).createOrReplace(gitconfigConfigmap);
97+
}
98+
}
99+
100+
private ConfigMap buildGitconfigConfigmap() {
101+
return new ConfigMapBuilder()
102+
.withNewMetadata()
103+
.withName(GITCONFIG_CONFIGMAP_NAME)
104+
.withLabels(GITCONFIG_CONFIGMAP_LABELS)
105+
.withAnnotations(GITCONFIG_CONFIGMAP_ANNOTATIONS)
106+
.endMetadata()
107+
.build();
108+
}
109+
110+
private boolean needUpdateGitconfigConfigmap(
111+
Optional<Pair<String, String>> usernameAndEmailFromGitconfigOptional,
112+
Optional<Pair<String, String>> usernameAndEmailFromFetcher) {
113+
return usernameAndEmailFromGitconfigOptional.isEmpty()
114+
&& usernameAndEmailFromFetcher.isPresent();
115+
}
116+
117+
private Optional<String> generateGitconfigSections(
118+
Optional<String> gitconfigOptional, Optional<Pair<String, String>> usernameAndEmailOptional) {
119+
Optional<String> userSectionOptional = Optional.empty();
120+
Optional<String> httpSectionOPtional = Optional.empty();
121+
if (usernameAndEmailOptional.isPresent()) {
122+
userSectionOptional =
123+
Optional.of(
124+
generateUserSection(
125+
usernameAndEmailOptional.get().first, usernameAndEmailOptional.get().second));
126+
}
127+
StringJoiner joiner = new StringJoiner("\n");
128+
userSectionOptional.ifPresent(joiner::add);
129+
httpSectionOPtional.ifPresent(joiner::add);
130+
if (gitconfigOptional.isPresent()) {
131+
Optional<String> otherSectionsOptional = getOtherStoredSections(gitconfigOptional.get());
132+
otherSectionsOptional.ifPresent(joiner::add);
133+
}
134+
return joiner.length() > 0 ? Optional.of(joiner.toString()) : Optional.empty();
135+
}
136+
137+
Optional<String> getOtherStoredSections(String gitconfig) {
138+
StringJoiner joiner = new StringJoiner("\n");
139+
Matcher matcher = gitconfigSectionPattern.matcher(gitconfig);
140+
while (matcher.find()) {
141+
String sectionName = matcher.group("sectionName");
142+
if (!sectionName.equals("user") && !sectionName.equals("http")) {
143+
joiner.add(matcher.group());
144+
}
145+
}
146+
return joiner.length() > 0 ? Optional.of(joiner.toString()) : Optional.empty();
147+
}
148+
149+
private Optional<Pair<String, String>> getUsernameAndEmailFromGitconfig(String gitconfig) {
150+
if (gitconfig.contains("[user]")) {
151+
Matcher usernameMatcher = usernmaePattern.matcher(gitconfig);
152+
Matcher emailaMatcher = emailPattern.matcher(gitconfig);
153+
if (usernameMatcher.find() && emailaMatcher.find()) {
154+
String username = usernameMatcher.group("username");
155+
String email = emailaMatcher.group("email");
156+
if (!emptyStringPattern.matcher(username).matches()
157+
&& !emptyStringPattern.matcher(email).matches()) {
158+
return Optional.of(new Pair<>(username, email));
159+
}
160+
}
161+
}
162+
return Optional.empty();
163+
}
164+
165+
private Optional<Pair<String, String>> getUsernameAndEmailFromFetcher() {
166+
GitUserData gitUserData;
167+
for (GitUserDataFetcher fetcher : gitUserDataFetchers) {
168+
try {
169+
gitUserData = fetcher.fetchGitUserData();
170+
if (!isNullOrEmpty(gitUserData.getScmUsername())
171+
&& !isNullOrEmpty(gitUserData.getScmUserEmail())) {
172+
return Optional.of(
173+
new Pair<>(gitUserData.getScmUsername(), gitUserData.getScmUserEmail()));
174+
}
175+
} catch (ScmUnauthorizedException
176+
| ScmCommunicationException
177+
| ScmConfigurationPersistenceException
178+
| ScmItemNotFoundException
179+
| ScmBadRequestException e) {
180+
LOG.debug("No GitUserDataFetcher is configured. " + e.getMessage());
181+
}
182+
}
183+
return Optional.empty();
184+
}
185+
186+
private String generateUserSection(String username, String email) {
187+
return String.format("[user]\n\tname = %1$s\n\temail = %2$s", username, email);
188+
}
189+
190+
private Optional<String> getGitconfig(KubernetesClient client, String namespaceName) {
191+
ConfigMap gitconfigConfigmap =
192+
client.configMaps().inNamespace(namespaceName).withName(GITCONFIG_CONFIGMAP_NAME).get();
193+
if (gitconfigConfigmap != null) {
194+
String gitconfig = gitconfigConfigmap.getData().get(CONFIGMAP_DATA_KEY);
195+
if (!isNullOrEmpty(gitconfig)) {
196+
return Optional.of(gitconfig);
197+
}
198+
}
199+
return Optional.empty();
200+
}
201+
}

infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/GitconfigUserDataConfigurator.java

-116
This file was deleted.

0 commit comments

Comments
 (0)