Skip to content

Commit

Permalink
#200: Initial crash loop prevention for custom resources
Browse files Browse the repository at this point in the history
- Extend the resource clients for app definitions, sessions, and
workspaces to be able to handle the status subresource.
- Add basic status for our CRDs to track handling state of the operator
- Prevent crashloops by never handling resources that are in error or
handling state. The latter indicates an unexcepted crash during handling
(e.g. NPE).
- NOTE: The previous step was only dony for LAZY handlers. EAGER
handlers did not change
- Exemplary sub steps for volume claim an attachment for workspace
resources
- Increase resource versions for all CRs
- Move API, KIND, and CRD_NAME constants from Spec to resource classes
(e.g. from SessionSpec to Session) because they belong to the resource
itself. This became apparent when adding the Status to the resources

Note: Status handling can probably be refactored to be a bit cleaner and
make use of better code-reuse.

Part of #200
  • Loading branch information
lucas-koehler committed Aug 2, 2023
1 parent e0397b6 commit 594c7ef
Show file tree
Hide file tree
Showing 28 changed files with 454 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource and others.
* Copyright (C) 2022-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
Expand All @@ -18,7 +18,8 @@
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.AppDefinitionSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.AppDefinitionStatus;

public interface AppDefinitionResourceClient
extends CustomResourceClient<AppDefinitionSpec, AppDefinition, AppDefinitionSpecResourceList> {
public interface AppDefinitionResourceClient extends
CustomResourceClient<AppDefinitionSpec, AppDefinitionStatus, AppDefinition, AppDefinitionSpecResourceList> {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource and others.
* Copyright (C) 2022-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
Expand All @@ -18,32 +18,49 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.eclipse.theia.cloud.common.k8s.resource.UserScopedSpec;

import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.client.CustomResource;

public interface CustomResourceClient<S, T extends CustomResource<S, Void>, L extends KubernetesResourceList<T>>
public interface CustomResourceClient<SPEC, STATUS, T extends CustomResource<SPEC, STATUS>, L extends KubernetesResourceList<T>>
extends ResourceClient<T, L> {

T create(String correlationId, S spec);
T create(String correlationId, SPEC spec);

default Optional<S> spec(String name) {
default Optional<SPEC> spec(String name) {
return get(name).map(T::getSpec);
}

default Optional<STATUS> status(String name) {
return get(name).map(T::getStatus);
}

default List<T> list(String user) {
return list().stream().filter(item -> Objects.equals(UserScopedSpec.getUser(item.getSpec()), user))
.collect(Collectors.toList());
}

default List<S> specs() {
default List<SPEC> specs() {
return list().stream().map(item -> item.getSpec()).collect(Collectors.toList());
}

default List<S> specs(String user) {
default List<SPEC> specs(String user) {
return list(user).stream().map(item -> item.getSpec()).collect(Collectors.toList());
}

default boolean updateStatus(String correlationId, T resource, Consumer<STATUS> editOperation) {
trace(correlationId, "Update Status of " + resource);
final String name = resource.getMetadata().getName();
return (editStatus(correlationId, name, res -> {
STATUS status = Optional.ofNullable(res.getStatus()).orElse(createDefaultStatus());
res.setStatus(status);
editOperation.accept(status);
}) != null);
}

STATUS createDefaultStatus();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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.AppDefinitionSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.AppDefinitionStatus;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
Expand All @@ -42,4 +43,9 @@ public AppDefinition create(String correlationId, AppDefinitionSpec spec) {
return operation().create(appDefinition);
}

@Override
public AppDefinitionStatus createDefaultStatus() {
return new AppDefinitionStatus();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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.SessionSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.SessionStatus;
import org.eclipse.theia.cloud.common.util.TheiaCloudError;

import io.fabric8.kubernetes.api.model.ObjectMeta;
Expand Down Expand Up @@ -102,4 +103,9 @@ public boolean reportActivity(String correlationId, String name) {
}) != null;
}

@Override
public SessionStatus createDefaultStatus() {
return new SessionStatus();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource and others.
* Copyright (C) 2022-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
Expand All @@ -20,6 +20,7 @@
import org.eclipse.theia.cloud.common.k8s.resource.Workspace;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceSpec;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceStatus;
import org.eclipse.theia.cloud.common.util.TheiaCloudError;

import io.fabric8.kubernetes.api.model.ObjectMeta;
Expand Down Expand Up @@ -88,4 +89,9 @@ protected boolean isWorkspaceComplete(String correlationId, WorkspaceSpec create
}
return false;
}

@Override
public WorkspaceStatus createDefaultStatus() {
return new WorkspaceStatus();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ default T edit(String correlationId, String name, Consumer<T> consumer) {
return resource.edit(JavaUtil.toUnary(consumer));
}

default T editStatus(String correlationId, String name, Consumer<T> consumer) {
trace(correlationId, "Edit status of" + name);
Resource<T> resource = resource(name);
if (resource.get() == null) {
return null;
}
return resource.editStatus(JavaUtil.toUnary(consumer));
}

Optional<T> loadAndCreate(String correlationId, String yaml, Consumer<T> customization);

default Optional<T> loadAndCreate(String correlationId, String yaml) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource and others.
* Copyright (C) 2022-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
Expand All @@ -20,8 +20,10 @@
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.SessionSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.SessionStatus;

public interface SessionResourceClient extends CustomResourceClient<SessionSpec, Session, SessionSpecResourceList> {
public interface SessionResourceClient
extends CustomResourceClient<SessionSpec, SessionStatus, Session, SessionSpecResourceList> {
Session launch(String correlationId, SessionSpec spec, long timeout, TimeUnit unit);

default Session launch(String correlationId, SessionSpec spec, int timeout) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource and others.
* Copyright (C) 2022-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
Expand All @@ -20,14 +20,14 @@
import org.eclipse.theia.cloud.common.k8s.resource.Workspace;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceSpec;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.WorkspaceStatus;

public interface WorkspaceResourceClient
extends CustomResourceClient<WorkspaceSpec, Workspace, WorkspaceSpecResourceList> {
extends CustomResourceClient<WorkspaceSpec, WorkspaceStatus, Workspace, WorkspaceSpecResourceList> {

Workspace launch(String correlationId, WorkspaceSpec spec, long timeout, TimeUnit unit);

default Workspace launch(String correlationId, WorkspaceSpec spec) {
return launch(correlationId, spec, 1, TimeUnit.MINUTES);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@
import io.fabric8.kubernetes.model.annotation.Singular;
import io.fabric8.kubernetes.model.annotation.Version;

@Version("v6beta")
@Version("v8beta")
@Group("theia.cloud")
@Singular("appdefinition")
@Plural("appdefinitions")
public class AppDefinition extends CustomResource<AppDefinitionSpec, Void> implements Namespaced {
public class AppDefinition extends CustomResource<AppDefinitionSpec, AppDefinitionStatus> implements Namespaced {

private static final long serialVersionUID = 8749670583218521755L;
public static final String API = "theia.cloud/v8beta";
public static final String KIND = "AppDefinition";
public static final String CRD_NAME = "appdefinitions.theia.cloud";

@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
@JsonDeserialize()
public class AppDefinitionSpec {

public static final String API = "theia.cloud/v6beta";
public static final String KIND = "AppDefinition";
public static final String CRD_NAME = "appdefinitions.theia.cloud";

@JsonProperty("name")
private String name;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/********************************************************************************
* 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.common.k8s.resource;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize
public class AppDefinitionStatus extends ResourceStatus {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/********************************************************************************
* Copyright (C) 2022 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.common.k8s.resource;

/**
* Constant values to describe resource handling.
*/
public interface OperatorStatus {

String NEW = "NEW";

String ERROR = "ERROR";

String HANDLING = "HANDLING";

String HANDLED = "HANDLED";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/********************************************************************************
* 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.common.k8s.resource;

import com.fasterxml.jackson.annotation.JsonProperty;

public abstract class ResourceStatus {

@JsonProperty()
private String operatorStatus;

@JsonProperty()
private String operatorMessage;

public String getOperatorStatus() {
return operatorStatus;
}

public void setOperatorStatus(String operatorStatus) {
this.operatorStatus = operatorStatus;
}

public String getOperatorMessage() {
return operatorMessage;
}

public void setOperatorMessage(String operatorMessage) {
this.operatorMessage = operatorMessage;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2022 EclipseSource, Lockular, Ericsson, STMicroelectronics and
* Copyright (C) 2022-2023 EclipseSource, Lockular, Ericsson, STMicroelectronics and
* others.
*
* This program and the accompanying materials are made available under the
Expand All @@ -25,13 +25,16 @@
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")
public class Session extends CustomResource<SessionSpec, Void> implements Namespaced {
public class Session extends CustomResource<SessionSpec, SessionStatus> implements Namespaced {

private static final long serialVersionUID = 4518092300237069237L;
public static final String API = "theia.cloud/v5beta";
public static final String KIND = "Session";
public static final String CRD_NAME = "sessions.theia.cloud";

@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
@JsonDeserialize()
public class SessionSpec implements UserScopedSpec {

public static final String API = "theia.cloud/v4beta";
public static final String KIND = "Session";
public static final String CRD_NAME = "sessions.theia.cloud";

@JsonProperty("name")
private String name;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/********************************************************************************
* 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.common.k8s.resource;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize
public class SessionStatus extends ResourceStatus {

}
Loading

0 comments on commit 594c7ef

Please sign in to comment.