Add preferences to plugin component of devfile (#13341)

Signed-off-by: Sergii Leshchenko <sleshche@redhat.com>
7.20.x
Sergii Leshchenko 2019-05-24 08:59:29 +03:00 committed by GitHub
parent 9eb3ea0340
commit c94c3acc03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 211 additions and 24 deletions

View File

@ -28,6 +28,12 @@ public interface Component {
/** Returns the plugin/editor FQN. Is mandatory only for cheEditor/chePlugin components types. */
String getId();
/**
* Returns the preferences of the plugin. Example value of preference: {@code java.home:
* /home/user/jdk11}
*/
Map<String, String> getPreferences();
/**
* Returns absolute or devfile-relative location of Kubernetes list yaml file. It is mandatory and
* applicable only for 'kubernetes' and 'openshift' components types.

View File

@ -1,7 +1,7 @@
### Introduction
Previously, two kind of recipes were available to bootstrap a cloud developer workspace and to make it portable: [Chefile](https://www.eclipse.org/che/docs/chefile.html)
Previously, two kind of recipes were available to bootstrap a cloud developer workspace and to make it portable: [Chefile](https://www.eclipse.org/che/docs/chefile.html)
and [Factories](https://www.eclipse.org/che/docs/factories-getting-started.html#try-a-factory).
As a continuation of this, the brand new `devfile` format was introduced, which combines simplicity and support for high variety of different components available to develop a container based application.
@ -38,37 +38,37 @@ components:
type: chePlugin
id: eclipse/che-machine-exec-plugin/0.0.1
```
For the detailed explanation of all devfile components assignment and possible values, please see the following resources:
- [Specification repository](https://github.com/redhat-developer/devfile)
- [detailed json-schema documentation](https://redhat-developer.github.io/devfile/devfile).
### Getting Started
The simplest way to use devfile is to have it deployed into GitHub source repository and then create factory from this repo.
This is as simple as create `devfile.yaml` file in the root of your GH repo, and then execute the factory:
This is as simple as create `devfile.yaml` file in the root of your GH repo, and then execute the factory:
```
https://<your-che-host>/f?url=https://github.com/mygroup/myrepo
```
Also, it is possible to execute devfile by constructing the factory with the URL to it's raw content, for example,
Also, it is possible to execute devfile by constructing the factory with the URL to it's raw content, for example,
```
https://<your-che-host>/f?url=https://pastebin.com/raw/ux6iCGaW
```
or sending a devfile to a dedicated REST API using curl/swagger, which will create new workspace and return it's configuration:
```
or sending a devfile to a dedicated REST API using curl/swagger, which will create new workspace and return it's configuration:
```
curl -X POST -H "Authorization: <TOKEN>" -H "Content-Type: application/yaml" -d <devlile_content> https://<your-che-host>/api/devfile
```
```
If you're a user of `chectl` tool, it is also possible to execute workspace from devfile, using `workspace:start` command
If you're a user of `chectl` tool, it is also possible to execute workspace from devfile, using `workspace:start` command
parameter as follows:
```
chectl workspace:start --devfile=devfile.yaml
````
````
Please note that currently this way only works for the local (same machine) devfiles - URL can't be used here atm.
### Project details
A single devfile can specify several projects. For each project, one has to specify the type of the
source repository, its location and optionally also the directory to which the project should be
source repository, its location and optionally also the directory to which the project should be
cloned to.
As an example, consider this devfile:
@ -89,17 +89,17 @@ projects:
```
In the example above, we see a devfile with 2 projects, `frontend` and `backend`, each located in
its own repository on github. `backend` has a specific requirement to be cloned into the
its own repository on github. `backend` has a specific requirement to be cloned into the
`src/github.com/acmecorp/backend` directory under the source root (implicitly defined by the Che
runtime) while frontend will be cloned into `frontend` directory under the source root.
### Supported component types
There are currently four types of components supported. There is two simpler types, such as `cheEditor` and `chePlugin` and
There are currently four types of components supported. There is two simpler types, such as `cheEditor` and `chePlugin` and
two more complex - `kubernetes` (or `openshift`) and `dockerimage`.
Please note that all components inside single devfile must have unique names.
Detailed component types explanation below:
#### cheEditor
#### cheEditor
Describes the editor which used in workspace by defining its id.
Devfile can only contain one component with `cheEditor` type.
@ -117,7 +117,7 @@ By default, `Che Theia` is configured as default editor along with `Che Machine
You're able to put `editorFree:true` attribute into Devfile attributes in case you do not need any editor in your workspace.
#### chePlugin
Describes the plugin which used in workspace by defining it's id.
Describes the plugin which used in workspace by defining it's id.
It is allowed to have several `chePlugin` components.
```
@ -128,9 +128,9 @@ It is allowed to have several `chePlugin` components.
id: eclipse/che-machine-exec-plugin/0.0.1
```
Both types above using composite id, which is colon-separated id and version of plugin from Che Plugin registry.
List of available Che plugins and more information about registry can be found on https://github.com/eclipse/che-plugin-registry
For each of types above it is also possible to specify container(s) memory limit as follows:
Both types above using id, which is slash-separated publisher, name and version of plugin from Che Plugin registry.
List of available Che plugins and more information about registry can be found on https://github.com/eclipse/che-plugin-registry.
For each of types above it is also possible to specify container(s) memory limit as follows:
```
...
components:
@ -139,8 +139,18 @@ For each of types above it is also possible to specify container(s) memory limit
id: eclipse/che-machine-exec-plugin/0.0.1
memoryLimit: 256M
```
This limit will be apllied to each container of given plugin.
This limit will be applied to each container of given plugin.
A plugin may need to be precisely tuned and in such case plugin preferences should be used.
Example shows how jvm may be configured with plugin's preferences.
```
...
-
id: redhat/java/0.38.0
type: chePlugin
preferences:
java.jdt.ls.vmargs: '-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication'
```
#### kubernetes/openshift
More complex component type, which allows to apply configuration from kubernetes/openshift lists. Content of the component may be provided either via `reference` attribute which points to the file with component content.
@ -212,12 +222,12 @@ Other types of constraints (and their combinations) are possible:
* `parentName` - the name of the parent object that (indirectly) contains the containers to override
* `parentSelector` - the set of labels the parent object needs to have
Combination of these constraints can be used to precisely locate the containers inside the
Combination of these constraints can be used to precisely locate the containers inside the
referenced Kubernetes list.
#### dockerimage
Component type which allows to define docker image based configuration of container in workspace.
Devfile can only contain one component with `dockerimage` type.
Devfile can only contain one component with `dockerimage` type.
```
...

View File

@ -18,9 +18,11 @@ import static org.eclipse.che.api.core.model.workspace.config.Command.PLUGIN_ATT
import static org.eclipse.che.api.devfile.server.Constants.COMPONENT_ALIAS_COMMAND_ATTRIBUTE;
import static org.eclipse.che.api.devfile.server.Constants.PLUGINS_COMPONENTS_ALIASES_WORKSPACE_ATTRIBUTE;
import static org.eclipse.che.api.devfile.server.Constants.PLUGIN_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.shared.Constants.PLUGIN_PREFERENCE_ATTR_TEMPLATE;
import static org.eclipse.che.api.workspace.shared.Constants.SIDECAR_MEMORY_LIMIT_ATTR_TEMPLATE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import java.util.Map.Entry;
import javax.inject.Inject;
import org.eclipse.che.api.core.model.workspace.devfile.Component;
import org.eclipse.che.api.devfile.server.FileContentProvider;
@ -100,6 +102,15 @@ public class PluginComponentToWorkspaceApplier implements ComponentToWorkspaceAp
.put(format(SIDECAR_MEMORY_LIMIT_ATTR_TEMPLATE, fqn.getPublisherAndName()), memoryLimit);
}
for (Entry<String, String> preference : pluginComponent.getPreferences().entrySet()) {
workspaceConfig
.getAttributes()
.put(
format(
PLUGIN_PREFERENCE_ATTR_TEMPLATE, fqn.getPublisherAndName(), preference.getKey()),
preference.getValue());
}
for (CommandImpl command : workspaceConfig.getCommands()) {
String commandComponent = command.getAttributes().get(COMPONENT_ALIAS_COMMAND_ATTRIBUTE);

View File

@ -162,10 +162,10 @@
}
},
"then": {
"additionalProperties": true,
"required": [
"id"
],
"additionalProperties": false,
"properties": {
"type": {},
"alias": {},
@ -191,6 +191,56 @@
}
}
},
{
"if": {
"properties": {
"type": {
"enum": [
"cheEditor"
]
}
}
},
"then": {
"additionalProperties": false,
"properties": {
"type": {},
"alias": {},
"id": {},
"memoryLimit": {}
}
}
},
{
"if": {
"properties": {
"type": {
"enum": [
"chePlugin"
]
}
}
},
"then": {
"additionalProperties": false,
"properties": {
"type": {},
"alias": {},
"id": {},
"memoryLimit": {},
"preferences": {
"type": "object",
"description": "Additional plugin preferences",
"examples": [
"{\"java.home\": \"/home/user/jdk11\", \"java.jdt.ls.vmargs\": \"-Xmx1G\"}"
],
"additionalProperties": {
"type": "string"
}
}
}
}
},
{
"if": {
"properties": {

View File

@ -16,6 +16,7 @@ import static org.eclipse.che.api.core.model.workspace.config.Command.PLUGIN_ATT
import static org.eclipse.che.api.devfile.server.Constants.COMPONENT_ALIAS_COMMAND_ATTRIBUTE;
import static org.eclipse.che.api.devfile.server.Constants.PLUGINS_COMPONENTS_ALIASES_WORKSPACE_ATTRIBUTE;
import static org.eclipse.che.api.devfile.server.Constants.PLUGIN_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.shared.Constants.PLUGIN_PREFERENCE_ATTR_TEMPLATE;
import static org.eclipse.che.api.workspace.shared.Constants.SIDECAR_MEMORY_LIMIT_ATTR_TEMPLATE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import static org.testng.Assert.assertEquals;
@ -51,6 +52,7 @@ public class PluginComponentToWorkspaceApplierTest {
superPluginComponent.setId(superPluginId);
superPluginComponent.setType(PLUGIN_COMPONENT_TYPE);
superPluginComponent.setMemoryLimit("1234M");
superPluginComponent.getPreferences().put("java-home", "/home/user/jdk11");
ComponentImpl customPluginComponent = new ComponentImpl();
customPluginComponent.setAlias("custom");
@ -79,6 +81,12 @@ public class PluginComponentToWorkspaceApplierTest {
.getAttributes()
.get(format(SIDECAR_MEMORY_LIMIT_ATTR_TEMPLATE, "eclipse/super-plugin")),
"1234M");
assertEquals(
workspaceConfig
.getAttributes()
.get(format(PLUGIN_PREFERENCE_ATTR_TEMPLATE, "eclipse/super-plugin", "java-home")),
"/home/user/jdk11");
}
@Test

View File

@ -42,6 +42,9 @@ public class DevfileSchemaValidatorTest {
public Object[][] validDevfiles() {
return new Object[][] {
{"editor_plugin_component/devfile_editor_plugins.yaml"},
{"editor_plugin_component/devfile_editor_component_with_custom_registry.yaml"},
{"editor_plugin_component/devfile_editor_plugins_components_with_memory_limit.yaml"},
{"editor_plugin_component/devfile_plugin_components_with_preferences.yaml"},
{"kubernetes_openshift_component/devfile_kubernetes_component_reference.yaml"},
{"kubernetes_openshift_component/devfile_kubernetes_component_absolute_reference.yaml"},
{"component/devfile_without_any_component.yaml"},
@ -59,8 +62,6 @@ public class DevfileSchemaValidatorTest {
},
{"dockerimage_component/devfile_dockerimage_component.yaml"},
{"dockerimage_component/devfile_dockerimage_component_without_entry_point.yaml"},
{"editor_plugin_component/devfile_editor_component_with_custom_registry.yaml"},
{"editor_plugin_component/devfile_editor_plugins_components_with_memory_limit.yaml"}
};
}

View File

@ -0,0 +1,20 @@
#
# 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
#
specVersion: 0.0.1
name: terminal-sample
components:
- type: chePlugin
id: check/terminal-sample/0.0.1
preferences:
java.home: '/home/user/jdk11'
java.jdt.ls.vmargs: '-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication'

View File

@ -125,6 +125,13 @@ public final class Constants {
*/
public static final String SIDECAR_MEMORY_LIMIT_ATTR_TEMPLATE = "sidecar.%s.memory_limit";
/**
* Template for workspace config attribute key that stores plugin component preference. The first
* %s should be replaced with `pluginPublisher/pluginName` and the second one should be replaced
* with preference name. Example value: `plugin.redhat/java.preference.java.home`.
*/
public static final String PLUGIN_PREFERENCE_ATTR_TEMPLATE = "plugin.%s.preference.%s";
/**
* Describes workspace runtimes which perform start/stop of this workspace. Should be set/read
* from {@link Workspace#getAttributes}

View File

@ -43,6 +43,15 @@ public interface ComponentDto extends Component {
ComponentDto withId(String id);
// plugin
@Override
Map<String, String> getPreferences();
void setPreferences(Map<String, String> preferences);
ComponentDto withPreferences(Map<String, String> preferences);
// k8s/OS
@Override

View File

@ -142,6 +142,8 @@ public final class DtoConverter {
.withAlias(component.getAlias())
// chePlugin/cheEditor
.withId(component.getId())
// chePlugin
.withPreferences(component.getPreferences())
// dockerimage
.withImage(component.getImage())
.withMemoryLimit(component.getMemoryLimit())

View File

@ -49,6 +49,14 @@ public class ComponentImpl implements Component {
@Column(name = "component_id", nullable = false)
private String componentId;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name = "devfile_component_preferences",
joinColumns = @JoinColumn(name = "devfile_component_id"))
@MapKeyColumn(name = "preference_key")
@Column(name = "preference")
private Map<String, String> preferences;
@Column(name = "alias")
private String alias;
@ -115,6 +123,14 @@ public class ComponentImpl implements Component {
this.componentId = id;
}
public ComponentImpl(String type, String id, Map<String, String> preferences) {
this.type = type;
this.componentId = id;
if (preferences != null) {
this.preferences = new HashMap<>(preferences);
}
}
public ComponentImpl(
String type,
String id,
@ -171,6 +187,7 @@ public class ComponentImpl implements Component {
String type,
String alias,
String id,
Map<String, String> preferences,
String reference,
String referenceContent,
Map<String, String> selector,
@ -186,6 +203,9 @@ public class ComponentImpl implements Component {
this.alias = alias;
this.type = type;
this.componentId = id;
if (preferences != null) {
this.preferences = new HashMap<>(preferences);
}
this.reference = reference;
this.referenceContent = referenceContent;
if (selector != null) {
@ -217,6 +237,7 @@ public class ComponentImpl implements Component {
component.getType(),
component.getAlias(),
component.getId(),
component.getPreferences(),
component.getReference(),
component.getReferenceContent(),
component.getSelector(),
@ -258,6 +279,18 @@ public class ComponentImpl implements Component {
this.componentId = id;
}
@Override
public Map<String, String> getPreferences() {
if (preferences == null) {
preferences = new HashMap<>();
}
return preferences;
}
public void setPreferences(Map<String, String> preferences) {
this.preferences = preferences;
}
@Override
public String getReference() {
return reference;
@ -405,6 +438,7 @@ public class ComponentImpl implements Component {
&& Objects.equals(referenceContent, component.referenceContent)
&& Objects.equals(image, component.image)
&& Objects.equals(memoryLimit, component.memoryLimit)
&& Objects.equals(getPreferences(), component.getPreferences())
&& Objects.equals(getSelector(), component.getSelector())
&& Objects.equals(getEntrypoints(), component.getEntrypoints())
&& Objects.equals(getCommand(), component.getCommand())
@ -426,6 +460,7 @@ public class ComponentImpl implements Component {
referenceContent,
image,
memoryLimit,
getPreferences(),
getSelector(),
getEntrypoints(),
getMountSources(),
@ -448,6 +483,8 @@ public class ComponentImpl implements Component {
+ ", type='"
+ type
+ '\''
+ ", preferences="
+ preferences
+ ", reference='"
+ reference
+ '\''

View File

@ -588,6 +588,7 @@ public class WorkspaceDaoTest {
"kubernetes",
"component3",
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/opt/jdk11"),
"/dev.yaml",
null,
ImmutableMap.of("app.kubernetes.io/component", "webapp"),
@ -903,6 +904,7 @@ public class WorkspaceDaoTest {
"kubernetes",
"component1",
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/home/user/jdk11"),
"/dev.yaml",
"refcontent1",
ImmutableMap.of("app.kubernetes.io/component", "db"),
@ -922,6 +924,7 @@ public class WorkspaceDaoTest {
"kubernetes",
"component2",
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/home/user/jdk11"),
"/dev.yaml",
"refcontent2",
ImmutableMap.of("app.kubernetes.io/component", "webapp"),

View File

@ -0,0 +1,22 @@
--
-- Copyright (c) 2012-2019 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
--
-- component preferences
CREATE TABLE devfile_component_preferences (
devfile_component_id BIGINT,
preference_key VARCHAR(255),
preference VARCHAR(255)
);
-- constraints & indexes
ALTER TABLE devfile_component_preferences ADD CONSTRAINT fk_prefs_component_id FOREIGN KEY (devfile_component_id) REFERENCES devfile_component (id);
CREATE UNIQUE INDEX index_devfile_component_prefs ON devfile_component_preferences (devfile_component_id, preference_key);

View File

@ -116,6 +116,7 @@ public final class TestObjectsFactory {
"kubernetes",
name,
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/home/user/jdk11"),
"/dev.yaml",
"refContent",
ImmutableMap.of("app.kubernetes.io/component", "webapp"),