Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an ability to try Workspace.Next features #9774

Merged
merged 10 commits into from
Jun 5, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,10 @@ che.singleport.wildcard_domain.port=NULL

# Enable single port custom DNS without inserting the IP
che.singleport.wildcard_domain.ipless=false

### Experimental properties
# Next properties are subject to changes and removal, so do not rely on them in a stable Che assembly

# Workspace.Next feature API endpoint. Should be a valid HTTP URL that includes protocol, port etc.
# In case Workspace.Next is not needed value 'NULL' should be used
che.workspace.feature.api=NULL
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@
import com.google.inject.spi.TypeConverter;
import java.util.regex.Pattern;

/** @author andrew00x */
/**
* Converts injected string value to an array of strings if such an array is requested by injection.
*
* <p>Entries of the array should be separated by a comma sign. Spaces around entries are trimmed.
* Supports injection from property files, environment variables and Java system properties.
*
* @author andrew00x
*/
public class StringArrayConverter extends AbstractModule implements TypeConverter {
private static final Pattern PATTERN = Pattern.compile(" *, *");

Expand Down
4 changes: 4 additions & 0 deletions deploy/kubernetes/kubectl/Deploy Che.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ and clean redeploy of Che.
- Create namespace `che`: `kubectl create namespace che`
- Deploy Che: `kubectl --namespace=che apply -f che-kubernetes.yaml`
- Check Che pod status until it become `Running`: `kubectl get --namespace=che pods`

### Workspace Next:
There is a file `wsnext/feature-api.yaml` which contains kubesrv service needed to test Workspace Next on kubernetes infrastructure.
To use it call `kubectl apply --namespace=che -f wsnext/feature-api.yaml` and Che will be able to use Workspace Next flow.
8 changes: 7 additions & 1 deletion deploy/kubernetes/kubectl/che-kubernetes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ items:
CHE_WORKSPACE_AUTO_START: "false"
CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON: '{"nginx.ingress.kubernetes.io/rewrite-target": "/","nginx.ingress.kubernetes.io/ssl-redirect": "false","nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600","nginx.ingress.kubernetes.io/proxy-read-timeout": "3600"}'
CHE_LOGS_APPENDERS_IMPL: "plaintext"
CHE_INFRA_KUBERNETES_INGRESS_DOMAIN: "192.168.99.101.nip.io"
CHE_INFRA_KUBERNETES_INGRESS_DOMAIN: "192.168.99.100.nip.io"
CHE_INFRA_KUBERNETES_SERVER__STRATEGY: "default-host"
CHE_WORKSPACE_FEATURE_API: "http://feature-api-service:3000"
- apiVersion: extensions/v1beta1
kind: Ingress
metadata:
Expand Down Expand Up @@ -270,6 +271,11 @@ items:
configMapKeyRef:
key: CHE_INFRA_KUBERNETES_SERVER__STRATEGY
name: che
- name: CHE_WORKSPACE_FEATURE_API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it makes sense to add the same configuration into OS deployment

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't check workspace next on OS infra, so don't want to add it for the time

valueFrom:
configMapKeyRef:
key: CHE_WORKSPACE_FEATURE_API
name: che
image: eclipse/che-server:nightly
imagePullPolicy: Always
livenessProbe:
Expand Down
85 changes: 85 additions & 0 deletions deploy/kubernetes/kubectl/wsnext/feature-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have few words in Deploy Che.md about deploying feature-api

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: feature-api
name: feature-api-service
spec:
ports:
- name: feature-api
port: 3000
protocol: TCP
targetPort: 3000
selector:
app: feature-api
- apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: feature-api
name: feature-api
data:
CHE_REGISTRY_UPDATE_INTERVAL: "60"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it value in seconds?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

CHE_REGISTRY_GITHUB_URL: "https://github.com/garagatyi/che-registry.git"
- apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: feature-api-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
spec:
rules:
- host: feature.192.168.99.100.nip.io
http:
paths:
- backend:
serviceName: feature-api-service
servicePort: 3000
- apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: feature-api
name: feature-api
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app: feature-api
strategy:
type: Recreate
template:
metadata:
labels:
app: feature-api
spec:
containers:
- env:
- name: CHE_REGISTRY_UPDATE_INTERVAL
valueFrom:
configMapKeyRef:
key: CHE_REGISTRY_UPDATE_INTERVAL
name: feature-api
- name: CHE_REGISTRY_GITHUB_URL
valueFrom:
configMapKeyRef:
key: CHE_REGISTRY_GITHUB_URL
name: feature-api
image: garagatyi/kubesrv:latest
imagePullPolicy: Always
name: feature-api
ports:
- containerPort: 3000
name: feature-api
resources:
limits:
memory: 600Mi
requests:
memory: 256Mi
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.utils.HttpClientUtils;
import io.fabric8.kubernetes.client.utils.Utils;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
Expand All @@ -32,11 +31,8 @@
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Sergii Leshchenko
Expand All @@ -45,8 +41,6 @@
@Singleton
public class KubernetesClientFactory {

private static final Logger LOG = LoggerFactory.getLogger(KubernetesClientFactory.class);

/** {@link OkHttpClient} instance shared by all Kubernetes clients. */
private OkHttpClient httpClient;

Expand Down Expand Up @@ -92,7 +86,7 @@ public KubernetesClientFactory(
* @throws InfrastructureException if any error occurs on client instance creation.
*/
public KubernetesClient create(String workspaceId) throws InfrastructureException {
Config configForWorkspace = buildConfig(defaultConfig, workspaceId);
Config configForWorkspace = buildConfig(getDefaultConfig(), workspaceId);

return create(configForWorkspace);
}
Expand All @@ -106,7 +100,28 @@ public KubernetesClient create(String workspaceId) throws InfrastructureExceptio
* @throws InfrastructureException if any error occurs on client instance creation.
*/
public KubernetesClient create() throws InfrastructureException {
return create(buildConfig(defaultConfig, null));
return create(buildConfig(getDefaultConfig(), null));
}

/**
* Shuts down the {@link KubernetesClient} by closing its connection pool. Typically should be
* called on application tear down.
*/
public void shutdownClient() {
ConnectionPool connectionPool = httpClient.connectionPool();
Dispatcher dispatcher = httpClient.dispatcher();
ExecutorService executorService =
httpClient.dispatcher() != null ? httpClient.dispatcher().executorService() : null;

if (dispatcher != null) {
dispatcher.cancelAll();
}

if (connectionPool != null) {
connectionPool.evictAll();
}

Utils.shutdownExecutorService(executorService);
}

/** Retrieves the {@link OkHttpClient} instance shared by all Kubernetes clients. */
Expand Down Expand Up @@ -149,77 +164,50 @@ protected Config buildDefaultConfig(
configBuilder.withTrustCerts(doTrustCerts);
}

Config config = configBuilder.build();
return config;
return configBuilder.build();
}

/**
* Builds the Kubernetes {@link Config} object based on a default {@link Config} object and an
* optional workspace Id.
* Builds the Kubernetes {@link Config} object based on a provided {@link Config} object and an
* optional workspace ID.
*/
protected Config buildConfig(Config defaultConfig, @Nullable String workspaceId)
protected Config buildConfig(Config config, @Nullable String workspaceId)
throws InfrastructureException {
return defaultConfig;
return config;
}

protected Interceptor buildKubernetesInterceptor(Config config) {
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (isNotNullOrEmpty(config.getUsername()) && isNotNullOrEmpty(config.getPassword())) {
Request authReq =
chain
.request()
.newBuilder()
.addHeader(
"Authorization",
Credentials.basic(config.getUsername(), config.getPassword()))
.build();
return chain.proceed(authReq);
} else if (isNotNullOrEmpty(config.getOauthToken())) {
Request authReq =
chain
.request()
.newBuilder()
.addHeader("Authorization", "Bearer " + config.getOauthToken())
.build();
return chain.proceed(authReq);
}
return chain.proceed(request);
return chain -> {
Request request = chain.request();
if (isNotNullOrEmpty(config.getUsername()) && isNotNullOrEmpty(config.getPassword())) {
Request authReq =
chain
.request()
.newBuilder()
.addHeader(
"Authorization", Credentials.basic(config.getUsername(), config.getPassword()))
.build();
return chain.proceed(authReq);
} else if (isNotNullOrEmpty(config.getOauthToken())) {
Request authReq =
chain
.request()
.newBuilder()
.addHeader("Authorization", "Bearer " + config.getOauthToken())
.build();
return chain.proceed(authReq);
}
return chain.proceed(request);
};
}

/**
* Shuts down the {@link KubernetesClient} by closing it's connection pool. Typically should be
* called on application tear down.
*/
public void shutdownClient() {
ConnectionPool connectionPool = httpClient.connectionPool();
Dispatcher dispatcher = httpClient.dispatcher();
ExecutorService executorService =
httpClient.dispatcher() != null ? httpClient.dispatcher().executorService() : null;

if (dispatcher != null) {
dispatcher.cancelAll();
}

if (connectionPool != null) {
connectionPool.evictAll();
}

Utils.shutdownExecutorService(executorService);
}

/**
* Creates instance of {@link KubernetesClient} that uses an {@link OkHttpClient} instance derived
* from the shared {@code httpClient} instance in which interceptors are overriden to authenticate
* with the credentials (user/password or Oauth token) contained in the {@code config} parameter.
*
* @throws InfrastructureException if any error occurs on client instance creation.
* from the shared {@code httpClient} instance in which interceptors are overridden to
* authenticate with the credentials (user/password or Oauth token) contained in the {@code
* config} parameter.
*/
private KubernetesClient create(Config config) throws InfrastructureException {
private KubernetesClient create(Config config) {
OkHttpClient clientHttpClient =
httpClient.newBuilder().authenticator(Authenticator.NONE).build();
OkHttpClient.Builder builder = clientHttpClient.newBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiExternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.EnvVarProvider;
import org.eclipse.che.api.workspace.server.wsnext.WorkspaceNextApplier;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironmentFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.bootstrapper.KubernetesBootstrapperFactory;
Expand All @@ -53,6 +54,7 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.server.IngressAnnotationsProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.MultiHostIngressExternalServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.SingleHostIngressExternalServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.KubernetesWorkspaceNextApplier;

/** @author Sergii Leshchenko */
public class KubernetesInfraModule extends AbstractModule {
Expand Down Expand Up @@ -116,5 +118,9 @@ protected void configure() {

bind(KubernetesRuntimeStateCache.class).to(JpaKubernetesRuntimeStateCache.class);
bind(KubernetesMachineCache.class).to(JpaKubernetesMachineCache.class);

MapBinder<String, WorkspaceNextApplier> wsNext =
MapBinder.newMapBinder(binder(), String.class, WorkspaceNextApplier.class);
wsNext.addBinding(KubernetesEnvironment.TYPE).to(KubernetesWorkspaceNextApplier.class);
}
}
Loading