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 Container Lifecycle support #16391

Merged
merged 6 commits into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.ContainerPort;
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
import io.fabric8.kubernetes.api.model.ExecAction;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.wsplugins.model.CheContainer;
import org.eclipse.che.api.workspace.server.wsplugins.model.ChePluginEndpoint;
import org.eclipse.che.api.workspace.server.wsplugins.model.EnvVar;
import org.eclipse.che.api.workspace.server.wsplugins.model.Exec;
import org.eclipse.che.api.workspace.server.wsplugins.model.Handler;
import org.eclipse.che.api.workspace.server.wsplugins.model.Lifecycle;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.workspace.infrastructure.kubernetes.Names;
import org.eclipse.che.workspace.infrastructure.kubernetes.util.Containers;
Expand Down Expand Up @@ -64,6 +68,7 @@ public Container resolve() throws InfrastructureException {
.withPorts(getContainerPorts())
.withCommand(cheContainer.getCommand())
.withArgs(cheContainer.getArgs())
.withLifecycle(toK8sLifecycle(cheContainer.getLifecycle()))
.build();

provisionMemoryLimit(container, cheContainer);
Expand All @@ -74,6 +79,38 @@ public Container resolve() throws InfrastructureException {
return container;
}

private io.fabric8.kubernetes.api.model.Lifecycle toK8sLifecycle(Lifecycle lifecycle) {
if (lifecycle == null) {
return null;
}
io.fabric8.kubernetes.api.model.Handler postStart = toK8sHandler(lifecycle.getPostStart());
io.fabric8.kubernetes.api.model.Handler preStop = toK8sHandler(lifecycle.getPreStop());
io.fabric8.kubernetes.api.model.Lifecycle k8sLifecycle =
new io.fabric8.kubernetes.api.model.Lifecycle(postStart, preStop);
return k8sLifecycle;
}

private io.fabric8.kubernetes.api.model.Handler toK8sHandler(Handler handler) {
if (handler == null || handler.getExec() == null) {
return null;
}
ExecAction exec = toExecAction(handler.getExec());
if (exec == null) {
return null;
}
// TODO: add 'httpGetAction' and 'tcpSocketAction' support
io.fabric8.kubernetes.api.model.Handler k8SHandler =
new io.fabric8.kubernetes.api.model.Handler(exec, null, null);
return k8SHandler;
}

private ExecAction toExecAction(Exec exec) {
if (exec == null || exec.getCommand().isEmpty()) {
return null;
}
return new ExecAction(exec.getCommand());
}

private void provisionMemoryLimit(Container container, CheContainer cheContainer)
throws InfrastructureException {
String memoryLimit = cheContainer.getMemoryLimit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public class CheContainer {
@JsonProperty("args")
private List<String> args;

@JsonProperty("lifecycle")
private Lifecycle lifecycle;

public CheContainer image(String image) {
this.image = image;
return this;
Expand Down Expand Up @@ -238,6 +241,19 @@ public List<String> getArgs() {
return args;
}

public void setLifecycle(Lifecycle lifecycle) {
this.lifecycle = lifecycle;
}

public CheContainer lifecycle(Lifecycle lifecycle) {
this.lifecycle = lifecycle;
return this;
}

public Lifecycle getLifecycle() {
return lifecycle;
}

public void setArgs(List<String> args) {
this.args = args;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.wsplugins.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* Exec describes a "run in container" action. Command is the command line to execute inside the
* container, the working directory for the command is root ('/') in the container's filesystem. T
* he command is simply exec'd, it is not run inside a shell, so traditional shell instructions
* ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status
* of 0 is treated as live/healthy and non-zero is unhealthy.
*/
public class Exec {

@JsonProperty("command")
private List<String> command;

public Exec command(List<String> commands) {
this.command = commands;
return this;
}

public List<String> getCommand() {
if (command == null) {
command = new ArrayList<>();
}
return command;
}

public void setCommand(List<String> command) {
this.command = command;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Exec that = (Exec) o;
return Objects.equals(getCommand(), that.getCommand());
}

@Override
public int hashCode() {
return Objects.hash(command);
}

@Override
public String toString() {
return "Exec{" + "command=" + command + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.wsplugins.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;

/** Handler defines a specific action that should be taken on postStart or preStop. */
public class Handler {

@JsonProperty("exec")
private Exec exec;

public Handler exec(Exec exec) {
this.exec = exec;
return this;
}

public Exec getExec() {
return exec;
}

public void setExec(Exec exec) {
this.exec = exec;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Handler that = (Handler) o;
return Objects.equals(getExec(), that.getExec());
}

@Override
public int hashCode() {
return Objects.hash(exec);
}

@Override
public String toString() {
return "Handler{" + "exec=" + exec + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.wsplugins.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;

/**
* Lifecycle describes actions that the management system should take in response to container
* lifecycle events.
*
* <p>For the PostStart and PreStop lifecycle handlers, management of the container blocks until the
* action is complete, unless the container process fails, in which case the handler is aborted.
*
* <p>PostStart is called immediately after a container is created. If the handler fails, the
* container is terminated and restarted according to its restart policy. Other management of the
* container blocks until the hook completes. More info:
* https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
*
* <p>PreStop is called immediately before a container is terminated due to an API request or
* management event such as liveness/startup probe failure, preemption, resource contention, etc.
* The handler is not called if the container crashes or exits. The reason for termination is passed
* to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is
* executed. Regardless of the outcome of the handler, the container will eventually terminate
* within the Pod's termination grace period. Other management of the container blocks until the
* hook completes or until the termination grace period is reached. More info:
* https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
*/
public class Lifecycle {

@JsonProperty("preStop")
private Handler preStop = new Handler();

@JsonProperty("postStart")
private Handler postStart = new Handler();

public Lifecycle preStop(Handler preStop) {
this.preStop = preStop;
return this;
}

public Handler getPreStop() {
return preStop;
}

public void setPreStop(Handler preStop) {
this.preStop = preStop;
}

public Lifecycle postStart(Handler postStart) {
this.postStart = postStart;
return this;
}

public Handler getPostStart() {
return postStart;
}

public void setPostStart(Handler postStart) {
this.postStart = postStart;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Lifecycle that = (Lifecycle) o;
return Objects.equals(preStop, that.preStop) && Objects.equals(postStart, that.postStart);
}

@Override
public int hashCode() {
return Objects.hash(postStart, preStop);
}

@Override
public String toString() {
return "Lifecycle{" + "postStart=" + postStart + ", preStop=" + preStop + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
import java.util.Map;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.wsplugins.model.Exec;
vparfonov marked this conversation as resolved.
Show resolved Hide resolved
import org.eclipse.che.api.workspace.server.wsplugins.model.ExtendedPluginFQN;
import org.eclipse.che.api.workspace.server.wsplugins.model.Handler;
import org.eclipse.che.api.workspace.server.wsplugins.model.Lifecycle;
import org.eclipse.che.api.workspace.server.wsplugins.model.PluginFQN;
import org.eclipse.che.api.workspace.shared.Constants;
import org.mockito.Mock;
Expand Down