Skip to content

Commit d81a700

Browse files
authored
Rework the gitconfig configmap provision (#766)
Enhance the provision of the workspace gitconfig, to be able to respect data another from username and email.
1 parent 8f8b103 commit d81a700

File tree

6 files changed

+404
-258
lines changed

6 files changed

+404
-258
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012-2024 Red Hat, Inc.
2+
* Copyright (c) 2012-2025 Red Hat, Inc.
33
* This program and the accompanying materials are made
44
* available under the terms of the Eclipse Public License 2.0
55
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -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.GitconfigConfigurator;
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(GitconfigConfigurator.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,191 @@
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 GitconfigConfigurator implements NamespaceConfigurator {
42+
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
43+
private final Set<GitUserDataFetcher> gitUserDataFetchers;
44+
private static final Logger LOG = LoggerFactory.getLogger(GitconfigConfigurator.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 GitconfigConfigurator(
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 =
120+
usernameAndEmailOptional.map(p -> generateUserSection(p.first, p.second));
121+
StringJoiner joiner = new StringJoiner("\n");
122+
userSectionOptional.ifPresent(joiner::add);
123+
gitconfigOptional.flatMap(this::getOtherStoredSections).ifPresent(joiner::add);
124+
return joiner.length() > 0 ? Optional.of(joiner.toString()) : Optional.empty();
125+
}
126+
127+
Optional<String> getOtherStoredSections(String gitconfig) {
128+
StringJoiner joiner = new StringJoiner("\n");
129+
Matcher matcher = gitconfigSectionPattern.matcher(gitconfig);
130+
while (matcher.find()) {
131+
String sectionName = matcher.group("sectionName");
132+
if (!sectionName.equals("user") && !sectionName.equals("http")) {
133+
joiner.add(matcher.group());
134+
}
135+
}
136+
return joiner.length() > 0 ? Optional.of(joiner.toString()) : Optional.empty();
137+
}
138+
139+
private Optional<Pair<String, String>> getUsernameAndEmailFromGitconfig(String gitconfig) {
140+
if (gitconfig.contains("[user]")) {
141+
Matcher usernameMatcher = usernmaePattern.matcher(gitconfig);
142+
Matcher emailMatcher = emailPattern.matcher(gitconfig);
143+
if (usernameMatcher.find() && emailMatcher.find()) {
144+
String username = usernameMatcher.group("username");
145+
String email = emailMatcher.group("email");
146+
if (!emptyStringPattern.matcher(username).matches()
147+
&& !emptyStringPattern.matcher(email).matches()) {
148+
return Optional.of(new Pair<>(username, email));
149+
}
150+
}
151+
}
152+
return Optional.empty();
153+
}
154+
155+
private Optional<Pair<String, String>> getUsernameAndEmailFromFetcher() {
156+
GitUserData gitUserData;
157+
for (GitUserDataFetcher fetcher : gitUserDataFetchers) {
158+
try {
159+
gitUserData = fetcher.fetchGitUserData();
160+
if (!isNullOrEmpty(gitUserData.getScmUsername())
161+
&& !isNullOrEmpty(gitUserData.getScmUserEmail())) {
162+
return Optional.of(
163+
new Pair<>(gitUserData.getScmUsername(), gitUserData.getScmUserEmail()));
164+
}
165+
} catch (ScmUnauthorizedException
166+
| ScmCommunicationException
167+
| ScmConfigurationPersistenceException
168+
| ScmItemNotFoundException
169+
| ScmBadRequestException e) {
170+
LOG.debug("No GitUserDataFetcher is configured. " + e.getMessage());
171+
}
172+
}
173+
return Optional.empty();
174+
}
175+
176+
private String generateUserSection(String username, String email) {
177+
return String.format("[user]\n\tname = %1$s\n\temail = %2$s", username, email);
178+
}
179+
180+
private Optional<String> getGitconfig(KubernetesClient client, String namespaceName) {
181+
ConfigMap gitconfigConfigmap =
182+
client.configMaps().inNamespace(namespaceName).withName(GITCONFIG_CONFIGMAP_NAME).get();
183+
if (gitconfigConfigmap != null) {
184+
String gitconfig = gitconfigConfigmap.getData().get(CONFIGMAP_DATA_KEY);
185+
if (!isNullOrEmpty(gitconfig)) {
186+
return Optional.of(gitconfig);
187+
}
188+
}
189+
return Optional.empty();
190+
}
191+
}

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)