diff --git a/java/common/org.eclipse.theia.cloud.common/.settings/org.eclipse.jdt.ui.prefs b/java/common/org.eclipse.theia.cloud.common/.settings/org.eclipse.jdt.ui.prefs
index 606155f8..766a660b 100644
--- a/java/common/org.eclipse.theia.cloud.common/.settings/org.eclipse.jdt.ui.prefs
+++ b/java/common/org.eclipse.theia.cloud.common/.settings/org.eclipse.jdt.ui.prefs
@@ -3,7 +3,7 @@ editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=org.eclipse.jdt.ui.default.sun_profile
formatter_settings_version=21
org.eclipse.jdt.ui.javadoc=false
-org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2022 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
+org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2023 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
sp_cleanup.add_all=false
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/Session.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/Session.java
index 1efc90b3..b04ed22f 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/Session.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/Session.java
@@ -25,7 +25,7 @@
import io.fabric8.kubernetes.model.annotation.Singular;
import io.fabric8.kubernetes.model.annotation.Version;
-@Version("v4beta")
+@Version("v5beta")
@Group("theia.cloud")
@Singular("session")
@Plural("sessions")
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/SessionSpec.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/SessionSpec.java
index 1ab4d6c0..00e77d0e 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/SessionSpec.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/SessionSpec.java
@@ -28,7 +28,7 @@
@JsonDeserialize()
public class SessionSpec implements UserScopedSpec {
- public static final String API = "theia.cloud/v4beta";
+ public static final String API = "theia.cloud/v5beta";
public static final String KIND = "Session";
public static final String CRD_NAME = "sessions.theia.cloud";
@@ -58,13 +58,16 @@ public class SessionSpec implements UserScopedSpec {
@JsonProperty("envVars")
private Map envVars;
-
+
@JsonProperty("envVarsFromConfigMaps")
private List envVarsFromConfigMaps;
@JsonProperty("envVarsFromSecrets")
private List envVarsFromSecrets;
+ @JsonProperty("initOperations")
+ private List initOperations;
+
public SessionSpec() {
}
@@ -73,32 +76,27 @@ public SessionSpec(String name, String appDefinition, String user) {
}
public SessionSpec(String name, String appDefinition, String user, String workspace) {
- this(name, appDefinition, user, workspace, Map.of(), List.of(), List.of());
+ this(name, appDefinition, user, workspace, Map.of(), List.of(), List.of());
}
- public SessionSpec(
- String name, String appDefinition, String user, String workspace, Map envVars
- ) {
- this(name, appDefinition, user, workspace, envVars, List.of(), List.of());
+
+ public SessionSpec(String name, String appDefinition, String user, String workspace, Map envVars) {
+ this(name, appDefinition, user, workspace, envVars, List.of(), List.of());
}
- public SessionSpec(
- String name, String appDefinition, String user, String workspace,
- Map envVars, List envVarsFromConfigMaps
- ) {
- this(name, appDefinition, user, workspace, envVars, envVarsFromConfigMaps, List.of());
+ public SessionSpec(String name, String appDefinition, String user, String workspace, Map envVars,
+ List envVarsFromConfigMaps) {
+ this(name, appDefinition, user, workspace, envVars, envVarsFromConfigMaps, List.of());
}
- public SessionSpec(
- String name, String appDefinition, String user, String workspace,
- Map envVars, List envVarsFromConfigMaps, List envVarsFromSecrets
- ) {
- this.name = name;
- this.appDefinition = appDefinition;
- this.user = user;
- this.workspace = workspace;
- this.envVars = envVars;
- this.envVarsFromConfigMaps = envVarsFromConfigMaps;
- this.envVarsFromSecrets = envVarsFromSecrets;
+ public SessionSpec(String name, String appDefinition, String user, String workspace, Map envVars,
+ List envVarsFromConfigMaps, List envVarsFromSecrets) {
+ this.name = name;
+ this.appDefinition = appDefinition;
+ this.user = user;
+ this.workspace = workspace;
+ this.envVars = envVars;
+ this.envVarsFromConfigMaps = envVarsFromConfigMaps;
+ this.envVarsFromSecrets = envVarsFromSecrets;
}
public String getName() {
@@ -167,14 +165,19 @@ public String getWorkspace() {
}
public Map getEnvVars() {
- return envVars;
+ return envVars;
}
public List getEnvVarsFromConfigMaps() {
- return envVarsFromConfigMaps;
+ return envVarsFromConfigMaps;
}
+
public List getEnvVarsFromSecrets() {
- return envVarsFromSecrets;
+ return envVarsFromSecrets;
+ }
+
+ public List getinitOperations() {
+ return initOperations;
}
@JsonIgnore
@@ -187,9 +190,17 @@ public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((appDefinition == null) ? 0 : appDefinition.hashCode());
+ result = prime * result + ((envVars == null) ? 0 : envVars.hashCode());
+ result = prime * result + ((envVarsFromConfigMaps == null) ? 0 : envVarsFromConfigMaps.hashCode());
+ result = prime * result + ((envVarsFromSecrets == null) ? 0 : envVarsFromSecrets.hashCode());
+ result = prime * result + ((error == null) ? 0 : error.hashCode());
+ result = prime * result + ((initOperations == null) ? 0 : initOperations.hashCode());
+ result = prime * result + (int) (lastActivity ^ (lastActivity >>> 32));
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((sessionSecret == null) ? 0 : sessionSecret.hashCode());
+ result = prime * result + ((url == null) ? 0 : url.hashCode());
result = prime * result + ((user == null) ? 0 : user.hashCode());
result = prime * result + ((workspace == null) ? 0 : workspace.hashCode());
- result = prime * result + ((sessionSecret == null) ? 0 : sessionSecret.hashCode());
return result;
}
@@ -207,6 +218,48 @@ public boolean equals(Object obj) {
return false;
} else if (!appDefinition.equals(other.appDefinition))
return false;
+ if (envVars == null) {
+ if (other.envVars != null)
+ return false;
+ } else if (!envVars.equals(other.envVars))
+ return false;
+ if (envVarsFromConfigMaps == null) {
+ if (other.envVarsFromConfigMaps != null)
+ return false;
+ } else if (!envVarsFromConfigMaps.equals(other.envVarsFromConfigMaps))
+ return false;
+ if (envVarsFromSecrets == null) {
+ if (other.envVarsFromSecrets != null)
+ return false;
+ } else if (!envVarsFromSecrets.equals(other.envVarsFromSecrets))
+ return false;
+ if (error == null) {
+ if (other.error != null)
+ return false;
+ } else if (!error.equals(other.error))
+ return false;
+ if (initOperations == null) {
+ if (other.initOperations != null)
+ return false;
+ } else if (!initOperations.equals(other.initOperations))
+ return false;
+ if (lastActivity != other.lastActivity)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (sessionSecret == null) {
+ if (other.sessionSecret != null)
+ return false;
+ } else if (!sessionSecret.equals(other.sessionSecret))
+ return false;
+ if (url == null) {
+ if (other.url != null)
+ return false;
+ } else if (!url.equals(other.url))
+ return false;
if (user == null) {
if (other.user != null)
return false;
@@ -217,37 +270,82 @@ public boolean equals(Object obj) {
return false;
} else if (!workspace.equals(other.workspace))
return false;
- if (sessionSecret == null) {
- if (other.sessionSecret != null)
- return false;
- } else if (!sessionSecret.equals(other.sessionSecret))
- return false;
- if (envVars == null) {
- if (other.envVars != null)
- return false;
- } else if (!envVars.equals(other.envVars))
- return false;
- if (envVarsFromConfigMaps == null) {
- if (other.envVarsFromConfigMaps != null)
- return false;
- } else if (!envVarsFromConfigMaps.equals(other.envVarsFromConfigMaps))
- return false;
- if (envVarsFromSecrets == null) {
- if (other.envVarsFromSecrets != null)
- return false;
- } else if (!envVarsFromSecrets.equals(other.envVarsFromSecrets))
- return false;
return true;
}
@Override
public String toString() {
return "SessionSpec [name=" + name + ", appDefinition=" + appDefinition + ", user=" + user + ", url=" + url
- + ", error=" + error + ", workspace=" + workspace + ", lastActivity=" + lastActivity + "]";
+ + ", error=" + error + ", workspace=" + workspace + ", lastActivity=" + lastActivity
+ + ", sessionSecret=" + sessionSecret + ", envVars=" + envVars + ", envVarsFromConfigMaps="
+ + envVarsFromConfigMaps + ", envVarsFromSecrets=" + envVarsFromSecrets + ", initOperations="
+ + initOperations + "]";
}
public static boolean isEphemeral(String workspace) {
return workspace == null || workspace.isBlank();
}
+ public static class InitOperation {
+
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("arguments")
+ private List arguments;
+
+ public InitOperation() {
+ }
+
+ public InitOperation(String id, List arguments) {
+ this.id = id;
+ this.arguments = arguments;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public List getArguments() {
+ return arguments;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((arguments == null) ? 0 : arguments.hashCode());
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ InitOperation other = (InitOperation) obj;
+ if (arguments == null) {
+ if (other.arguments != null)
+ return false;
+ } else if (!arguments.equals(other.arguments))
+ return false;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "InitOperation [id=" + id + ", arguments=" + arguments + "]";
+ }
+
+ }
+
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/.settings/org.eclipse.jdt.ui.prefs b/java/operator/org.eclipse.theia.cloud.operator/.settings/org.eclipse.jdt.ui.prefs
index 606155f8..766a660b 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/.settings/org.eclipse.jdt.ui.prefs
+++ b/java/operator/org.eclipse.theia.cloud.operator/.settings/org.eclipse.jdt.ui.prefs
@@ -3,7 +3,7 @@ editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=org.eclipse.jdt.ui.default.sun_profile
formatter_settings_version=21
org.eclipse.jdt.ui.javadoc=false
-org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2022 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
+org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2023 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
sp_cleanup.add_all=false
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
index cc2063d5..a817a3ad 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
@@ -30,6 +30,7 @@
import org.eclipse.theia.cloud.operator.handler.BandwidthLimiter;
import org.eclipse.theia.cloud.operator.handler.DeploymentTemplateReplacements;
import org.eclipse.theia.cloud.operator.handler.IngressPathProvider;
+import org.eclipse.theia.cloud.operator.handler.InitOperationHandler;
import org.eclipse.theia.cloud.operator.handler.PersistentVolumeCreator;
import org.eclipse.theia.cloud.operator.handler.PersistentVolumeTemplateReplacements;
import org.eclipse.theia.cloud.operator.handler.SessionHandler;
@@ -39,6 +40,7 @@
import org.eclipse.theia.cloud.operator.handler.impl.DefaultDeploymentTemplateReplacements;
import org.eclipse.theia.cloud.operator.handler.impl.DefaultPersistentVolumeCreator;
import org.eclipse.theia.cloud.operator.handler.impl.DefaultPersistentVolumeTemplateReplacements;
+import org.eclipse.theia.cloud.operator.handler.impl.GitInitOperationHandler;
import org.eclipse.theia.cloud.operator.handler.impl.IngressPathProviderImpl;
import org.eclipse.theia.cloud.operator.monitor.MonitorActivityTracker;
import org.eclipse.theia.cloud.operator.monitor.MonitorActivityTrackerImpl;
@@ -71,6 +73,7 @@ protected void configure() {
bind(MonitorMessagingService.class).to(bindMonitorMessagingService()).in(Singleton.class);
configure(MultiBinding.create(TimeoutStrategy.class), this::configureTimeoutStrategies);
+ configure(MultiBinding.create(InitOperationHandler.class), this::configureInitOperationHandlers);
}
protected void configure(final MultiBinding binding, final Consumer> configurator) {
@@ -120,6 +123,10 @@ protected void configureTimeoutStrategies(final MultiBinding bi
binding.add(TimeoutStrategy.FixedTime.class);
}
+ protected void configureInitOperationHandlers(final MultiBinding binding) {
+ binding.add(GitInitOperationHandler.class);
+ }
+
@Provides
@Singleton
protected NamespacedKubernetesClient provideKubernetesClient() {
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/InitOperationHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/InitOperationHandler.java
new file mode 100644
index 00000000..03693afd
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/InitOperationHandler.java
@@ -0,0 +1,35 @@
+/********************************************************************************
+ * Copyright (C) 2023 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.operator.handler;
+
+import java.util.List;
+
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.Session;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+
+public interface InitOperationHandler {
+
+ static final String THEIA_CLOUD_INIT_LABEL = "theiaCloudInit";
+
+ String operationId();
+
+ void addInitContainer(String correlationId, TheiaCloudClient client, Deployment deployment,
+ AppDefinition appDefinition, Session session, List args);
+
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/AddedHandlerUtil.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/AddedHandlerUtil.java
index cd614c81..03eb81dd 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/AddedHandlerUtil.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/AddedHandlerUtil.java
@@ -26,6 +26,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -47,10 +48,17 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.resource.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.Session;
+import org.eclipse.theia.cloud.common.k8s.resource.SessionSpec.InitOperation;
import org.eclipse.theia.cloud.common.k8s.resource.SessionSpecResourceList;
+import org.eclipse.theia.cloud.common.k8s.resource.Workspace;
import org.eclipse.theia.cloud.common.util.LogMessageUtil;
+import org.eclipse.theia.cloud.common.util.WorkspaceUtil;
+import org.eclipse.theia.cloud.operator.handler.InitOperationHandler;
+import org.eclipse.theia.cloud.operator.handler.util.TheiaCloudPersistentVolumeUtil;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapEnvSource;
@@ -58,9 +66,12 @@
import io.fabric8.kubernetes.api.model.EnvFromSource;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.LocalObjectReference;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.ResourceRequirements;
import io.fabric8.kubernetes.api.model.SecretEnvSource;
+import io.fabric8.kubernetes.api.model.Volume;
+import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
@@ -70,6 +81,8 @@ public final class AddedHandlerUtil {
private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
+ public static final String USER_DATA = "user-data";
+
public static final String TEMPLATE_SERVICE_YAML = "/templateService.yaml";
public static final String TEMPLATE_SERVICE_WITHOUT_AOUTH2_PROXY_YAML = "/templateServiceWithoutOAuthProxy.yaml";
public static final String TEMPLATE_CONFIGMAP_EMAILS_YAML = "/templateConfigmapEmails.yaml";
@@ -308,4 +321,61 @@ private static Optional findContainerIdxInDeployment(Deployment deploym
return Optional.empty();
}
+ public static void addInitContainers(String correlationId, TheiaCloudClient client, Deployment deployment,
+ AppDefinition appDefinition, Session session, Set initOperationHandlers) {
+ List initOperations = session.getSpec().getinitOperations();
+ if (initOperations == null) {
+ return;
+ }
+ for (InitOperation initOperation : initOperations) {
+ Optional handler = initOperationHandlers.stream()
+ .filter(h -> h.operationId().equalsIgnoreCase(initOperation.getId())).findAny();
+ if (handler.isEmpty()) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId, MessageFormat
+ .format("No Init Handler found for operation with id {0}.", initOperation.getId())));
+ continue;
+ }
+ handler.get().addInitContainer(correlationId, client, deployment, appDefinition, session,
+ initOperation.getArguments());
+ LOGGER.info(formatLogMessage(correlationId,
+ MessageFormat.format("Added init container with id {0} to deployment.", initOperation.getId())));
+ }
+ }
+
+ public static Volume createVolume(String pvcName) {
+ Volume volume = new Volume();
+ volume.setName(USER_DATA);
+ PersistentVolumeClaimVolumeSource persistentVolumeClaim = new PersistentVolumeClaimVolumeSource();
+ volume.setPersistentVolumeClaim(persistentVolumeClaim);
+ persistentVolumeClaim.setClaimName(pvcName);
+ return volume;
+ }
+
+ public static VolumeMount createVolumeMount(AppDefinitionSpec appDefinition) {
+ VolumeMount volumeMount = new VolumeMount();
+ volumeMount.setName(AddedHandlerUtil.USER_DATA);
+ volumeMount.setMountPath(TheiaCloudPersistentVolumeUtil.getMountPath(appDefinition));
+ return volumeMount;
+ }
+
+ public static Optional getStorageName(TheiaCloudClient client, Session session, String correlationId) {
+ if (session.getSpec().isEphemeral()) {
+ return Optional.empty();
+ }
+ Optional workspace = client.workspaces().get(session.getSpec().getWorkspace());
+ if (!workspace.isPresent()) {
+ LOGGER.info(formatLogMessage(correlationId, "No workspace with name " + session.getSpec().getWorkspace()
+ + " found for session " + session.getSpec().getName(), correlationId));
+ return Optional.empty();
+
+ }
+ String storageName = WorkspaceUtil.getStorageName(workspace.get());
+ if (!client.persistentVolumeClaims().has(storageName)) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "No storage found for started session, will use ephemeral storage instead", correlationId));
+ return Optional.empty();
+ }
+ return Optional.of(storageName);
+ }
+
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/GitInitOperationHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/GitInitOperationHandler.java
new file mode 100644
index 00000000..b4f6cdea
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/GitInitOperationHandler.java
@@ -0,0 +1,211 @@
+/********************************************************************************
+ * Copyright (C) 2023 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.operator.handler.impl;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.Session;
+import org.eclipse.theia.cloud.common.util.LogMessageUtil;
+import org.eclipse.theia.cloud.operator.handler.InitOperationHandler;
+import org.eclipse.theia.cloud.operator.handler.util.TheiaCloudPersistentVolumeUtil;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.EnvVarSource;
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.api.model.SecretKeySelector;
+import io.fabric8.kubernetes.api.model.SecurityContext;
+import io.fabric8.kubernetes.api.model.VolumeMount;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+
+public class GitInitOperationHandler implements InitOperationHandler {
+
+ protected static final String PASSWORD = "password";
+ protected static final String USERNAME = "username";
+ protected static final String GIT_PROMPT1 = "GIT_PROMPT1";
+ protected static final String GIT_PROMPT2 = "GIT_PROMPT2";
+ protected static final String KUBERNETES_IO_BASIC_AUTH = "kubernetes.io/basic-auth";
+ protected static final String HTTPS = "https://";
+ protected static final String HTTP = "http://";
+ protected static final String IMAGE_ENV_KEY = "GIT_INIT_OPERATION_IMAGE";
+ protected static final String DEFAULT_IMAGE = "theiacloud/theia-cloud-git-init:latest";
+ protected static final String ID = "git";
+
+ private static final Logger LOGGER = LogManager.getLogger(GitInitOperationHandler.class);
+
+ @Override
+ public String operationId() {
+ return ID;
+ }
+
+ @Override
+ public void addInitContainer(String correlationId, TheiaCloudClient client, Deployment deployment,
+ AppDefinition appDefinition, Session session, List args) {
+
+ if (args.size() != 3) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId, MessageFormat.format(
+ "Git init expects three arguments (repository path, branch, secret-name). Passed arguments are: {0}",
+ args.stream().collect(Collectors.joining(",")))));
+ return;
+ }
+
+ Optional storageName = AddedHandlerUtil.getStorageName(client, session, correlationId);
+ if (storageName.isEmpty()) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId,
+ "Git init is only supported for non-ephemeral workspaces"));
+ return;
+ }
+
+ List initContainers = deployment.getSpec().getTemplate().getSpec().getInitContainers();
+
+ Container gitInitContainer = new Container();
+ initContainers.add(gitInitContainer);
+
+ gitInitContainer.setName("git-init");
+ gitInitContainer.setImage(getImage());
+ String repository = args.get(0);
+ String branch = args.get(1);
+ List containerArgs = Arrays.asList(repository,
+ TheiaCloudPersistentVolumeUtil.getMountPath(appDefinition.getSpec()), branch);
+ gitInitContainer.setArgs(containerArgs);
+ LOGGER.info(LogMessageUtil.formatLogMessage(correlationId, MessageFormat.format("Git init arguments are: {0}",
+ containerArgs.stream().collect(Collectors.joining(",")))));
+
+ SecurityContext securityContext = new SecurityContext();
+ gitInitContainer.setSecurityContext(securityContext);
+
+ securityContext.setRunAsUser(Long.valueOf(appDefinition.getSpec().getUid()));
+ securityContext.setRunAsGroup(Long.valueOf(appDefinition.getSpec().getUid()));
+
+ VolumeMount volumeMount = AddedHandlerUtil.createVolumeMount(appDefinition.getSpec());
+ gitInitContainer.getVolumeMounts().add(volumeMount);
+
+ String secretName = args.get(2);
+ Secret secret = client.kubernetes().secrets().withName(secretName).get();
+ if (secret == null) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId,
+ MessageFormat.format("No secret with name {0} found.", secretName)));
+ return;
+ }
+
+ String theiaCloudInit = secret.getMetadata().getLabels().get(THEIA_CLOUD_INIT_LABEL);
+ if (theiaCloudInit == null || !ID.equals(theiaCloudInit)) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId, MessageFormat
+ .format("Secret with name {0} is not configured to be used with Git init.", secretName)));
+ return;
+ }
+
+ String theiaCloudUser = secret.getMetadata().getAnnotations().get("theiaCloudUser");
+ if (theiaCloudUser == null || !session.getSpec().getUser().equals(theiaCloudUser)) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId,
+ MessageFormat.format("Secret with name {0} is not configured to be used by user {1}.", secretName,
+ session.getSpec().getUser())));
+ return;
+ }
+
+ if (isHTTP(repository)) {
+ if (!injectHTTPRepoCredentials(correlationId, secret, secretName, repository, gitInitContainer)) {
+ return;
+ }
+ } else {
+ /* get SSH Key and password from secret */
+ // TODO JF
+ }
+
+ }
+
+ protected boolean injectHTTPRepoCredentials(String correlationId, Secret secret, String secretName,
+ String repository, Container gitInitContainer) {
+ /* get username/password from secret */
+ boolean injectUsername = false;
+ boolean injectPassword = false;
+
+ if (!KUBERNETES_IO_BASIC_AUTH.equals(secret.getType())) {
+ LOGGER.warn(LogMessageUtil.formatLogMessage(correlationId, MessageFormat
+ .format("Secret with name {0} is not of type {1}.", secretName, KUBERNETES_IO_BASIC_AUTH)));
+ return false;
+ }
+
+ String[] split = repository.toLowerCase().split(Pattern.quote("://"), 2);
+ if (split.length != 2) {
+ LOGGER.error(LogMessageUtil.formatLogMessage(correlationId, MessageFormat
+ .format("Failed to check whether repository {0} contains any user information. ", repository)));
+ return false;
+ }
+ String repositoryWithoutProtocol = split[1];
+ if (repositoryWithoutProtocol.contains("@")) {
+ if (repositoryWithoutProtocol.split(Pattern.quote("@"))[0].contains(":")) {
+ /* username and password part of URL */
+ } else {
+ /* username part of url */
+ injectPassword = true;
+ }
+ } else {
+ injectUsername = true;
+ injectPassword = true;
+ }
+
+ LOGGER.info(LogMessageUtil.formatLogMessage(correlationId,
+ MessageFormat.format("Inject username: {0}; Inject password: {1}", injectUsername, injectPassword)));
+
+ String nextEnv = GIT_PROMPT1;
+ if (injectUsername) {
+ EnvVar envVar = new EnvVar();
+ gitInitContainer.getEnv().add(envVar);
+ envVar.setName(nextEnv);
+ nextEnv = GIT_PROMPT2;
+
+ EnvVarSource envVarSource = new EnvVarSource();
+ envVar.setValueFrom(envVarSource);
+ envVarSource.setSecretKeyRef(new SecretKeySelector(USERNAME, secretName, false));
+ }
+ if (injectPassword) {
+ EnvVar envVar = new EnvVar();
+ gitInitContainer.getEnv().add(envVar);
+ envVar.setName(nextEnv);
+
+ EnvVarSource envVarSource = new EnvVarSource();
+ envVar.setValueFrom(envVarSource);
+ envVarSource.setSecretKeyRef(new SecretKeySelector(PASSWORD, secretName, false));
+ }
+
+ return true;
+ }
+
+ protected static boolean isHTTP(String repository) {
+ String lowerCasedRepo = repository.toLowerCase(Locale.US);
+ return (lowerCasedRepo.startsWith(HTTP) || lowerCasedRepo.startsWith(HTTPS));
+ }
+
+ protected String getImage() {
+ String image = System.getenv(IMAGE_ENV_KEY);
+ if (image == null || image.isBlank()) {
+ return DEFAULT_IMAGE;
+ }
+ return image;
+ }
+
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/LazySessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/LazySessionHandler.java
index fa0501bc..126f584b 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/LazySessionHandler.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/impl/LazySessionHandler.java
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -33,13 +34,12 @@
import org.eclipse.theia.cloud.common.k8s.resource.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.Session;
import org.eclipse.theia.cloud.common.k8s.resource.SessionSpec;
-import org.eclipse.theia.cloud.common.k8s.resource.Workspace;
import org.eclipse.theia.cloud.common.util.TheiaCloudError;
-import org.eclipse.theia.cloud.common.util.WorkspaceUtil;
import org.eclipse.theia.cloud.operator.TheiaCloudArguments;
import org.eclipse.theia.cloud.operator.handler.BandwidthLimiter;
import org.eclipse.theia.cloud.operator.handler.DeploymentTemplateReplacements;
import org.eclipse.theia.cloud.operator.handler.IngressPathProvider;
+import org.eclipse.theia.cloud.operator.handler.InitOperationHandler;
import org.eclipse.theia.cloud.operator.handler.SessionHandler;
import org.eclipse.theia.cloud.operator.handler.util.K8sUtil;
import org.eclipse.theia.cloud.operator.handler.util.TheiaCloudConfigMapUtil;
@@ -53,7 +53,6 @@
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Container;
-import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.Volume;
@@ -71,7 +70,6 @@
public class LazySessionHandler implements SessionHandler {
private static final Logger LOGGER = LogManager.getLogger(LazySessionHandler.class);
- protected static final String USER_DATA = "user-data";
@Inject
protected IngressPathProvider ingressPathProvider;
@@ -81,7 +79,8 @@ public class LazySessionHandler implements SessionHandler {
protected BandwidthLimiter bandwidthLimiter;
@Inject
protected DeploymentTemplateReplacements deploymentReplacements;
-
+ @Inject
+ protected Set initOperationHandlers;
@Inject
protected TheiaCloudClient client;
@@ -155,7 +154,7 @@ public boolean sessionAdded(Session session, String correlationId) {
return true;
}
- Optional storageName = getStorageName(session, correlationId);
+ Optional storageName = AddedHandlerUtil.getStorageName(client, session, correlationId);
createAndApplyDeployment(correlationId, sessionResourceName, sessionResourceUID, session, appDefinition,
storageName, arguments.isUseKeycloak());
@@ -238,26 +237,6 @@ protected Optional getIngress(AppDefinition appDefinition, String corre
return ingress;
}
- protected Optional getStorageName(Session session, String correlationId) {
- if (session.getSpec().isEphemeral()) {
- return Optional.empty();
- }
- Optional workspace = client.workspaces().get(session.getSpec().getWorkspace());
- if (!workspace.isPresent()) {
- LOGGER.info(formatLogMessage(correlationId, "No workspace with name " + session.getSpec().getWorkspace()
- + " found for session " + session.getSpec().getName(), correlationId));
- return Optional.empty();
-
- }
- String storageName = WorkspaceUtil.getStorageName(workspace.get());
- if (!client.persistentVolumeClaims().has(storageName)) {
- LOGGER.info(formatLogMessage(correlationId,
- "No storage found for started session, will use ephemeral storage instead", correlationId));
- return Optional.empty();
- }
- return Optional.of(storageName);
- }
-
protected Optional createAndApplyService(String correlationId, String sessionResourceName,
String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy) {
Map replacements = TheiaCloudServiceUtil.getServiceReplacements(client.namespace(), session,
@@ -348,25 +327,19 @@ protected void createAndApplyDeployment(String correlationId, String sessionReso
&& !appDefinition.getSpec().getPullSecret().isEmpty()) {
AddedHandlerUtil.addImagePullSecret(deployment, appDefinition.getSpec().getPullSecret());
}
+
+ AddedHandlerUtil.addInitContainers(correlationId, client, deployment, appDefinition, session,
+ initOperationHandlers);
});
}
protected void addVolumeClaim(Deployment deployment, String pvcName, AppDefinitionSpec appDefinition) {
PodSpec podSpec = deployment.getSpec().getTemplate().getSpec();
-
- Volume volume = new Volume();
+ Volume volume = AddedHandlerUtil.createVolume(pvcName);
podSpec.getVolumes().add(volume);
- volume.setName(USER_DATA);
- PersistentVolumeClaimVolumeSource persistentVolumeClaim = new PersistentVolumeClaimVolumeSource();
- volume.setPersistentVolumeClaim(persistentVolumeClaim);
- persistentVolumeClaim.setClaimName(pvcName);
-
- Container theiaContainer = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefinition);
-
- VolumeMount volumeMount = new VolumeMount();
- theiaContainer.getVolumeMounts().add(volumeMount);
- volumeMount.setName(USER_DATA);
- volumeMount.setMountPath(TheiaCloudPersistentVolumeUtil.getMountPath(appDefinition));
+ Container container = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefinition);
+ VolumeMount volumeMount = AddedHandlerUtil.createVolumeMount(appDefinition);
+ container.getVolumeMounts().add(volumeMount);
}
protected synchronized String updateIngress(Optional ingress, Optional serviceToUse,
diff --git a/java/service/org.eclipse.theia.cloud.service/.settings/org.eclipse.jdt.ui.prefs b/java/service/org.eclipse.theia.cloud.service/.settings/org.eclipse.jdt.ui.prefs
index 606155f8..766a660b 100644
--- a/java/service/org.eclipse.theia.cloud.service/.settings/org.eclipse.jdt.ui.prefs
+++ b/java/service/org.eclipse.theia.cloud.service/.settings/org.eclipse.jdt.ui.prefs
@@ -3,7 +3,7 @@ editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=org.eclipse.jdt.ui.default.sun_profile
formatter_settings_version=21
org.eclipse.jdt.ui.javadoc=false
-org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2022 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
+org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n *//********************************************************************************\n * Copyright (C) 2023 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Eclipse Public License v. 2.0 which is available at\n * http\://www.eclipse.org/legal/epl-2.0.\n *\n * This Source Code may also be made available under the following Secondary\n * Licenses when the conditions for such availability set forth in the Eclipse\n * Public License v. 2.0 are satisfied\: GNU General Public License, version 2\n * with the GNU Classpath Exception which is available at\n * https\://www.gnu.org/software/classpath/license.html.\n *\n * SPDX-License-Identifier\: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0\n ********************************************************************************/\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
sp_cleanup.add_all=false
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
diff --git a/python/git-init/README.md b/python/git-init/README.md
index 919ff014..e2c7a21a 100644
--- a/python/git-init/README.md
+++ b/python/git-init/README.md
@@ -25,7 +25,7 @@ docker build -t theiacloud/theia-cloud-git-init:local -f dockerfiles/git-init/Do
ssh-keygen -t ed25519 -C "Test TC Git Init SSH Keypair"
```
-### Test Checkout
+### Test Checkout with container
```bash
# Adjust URLs and Password/PATs below
@@ -55,3 +55,44 @@ docker run --rm theiacloud/theia-cloud-git-init:local "$HTTP_PRIVATE_WITH_USERNA
# SSH
docker run --env GIT_PROMPT1=$SSH_PASSWORD -v ~/tmp/ssh/:/etc/theia-cloud-ssh --rm theiacloud/theia-cloud-git-init:local "$SSH_REPO" "/tmp/my-repo" "$BRANCH"
```
+
+### Create Kubernetes Resources
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: foo-theiacloud-io-basic-auth
+ namespace: theiacloud
+ labels:
+ theiaCloudInit: git
+ annotations:
+ theiaCloudUser: foo@theia-cloud.io
+type: kubernetes.io/basic-auth
+stringData:
+ username: username
+ password: pat
+```
+
+```yaml
+apiVersion: theia.cloud/v5beta
+kind: Session
+metadata:
+ name: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io-session
+ namespace: theiacloud
+spec:
+ appDefinition: theia-cloud-demo
+ envVars: {}
+ envVarsFromConfigMaps: []
+ envVarsFromSecrets: []
+ name: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io-session
+ user: foo@theia-cloud.io
+ workspace: ws-asdfghjkl-theia-cloud-demo-foo-theia-cloud-io
+ sessionSecret: 3e68605f-0c6d-4ae5-9816-738f15d34fc9
+ initOperations:
+ - id: git
+ arguments:
+ - https://gitlab.eclipse.org/username/my.repository.git
+ - maintenance_1_1_x
+ - foo-theiacloud-io-basic-auth
+```
diff --git a/terraform/configurations/.gitignore b/terraform/configurations/.gitignore
new file mode 100644
index 00000000..bf0824e5
--- /dev/null
+++ b/terraform/configurations/.gitignore
@@ -0,0 +1 @@
+*.log
\ No newline at end of file