Skip to content
This repository has been archived by the owner on Jan 20, 2023. It is now read-only.

Start remote binary with override entry point #75

Merged
merged 10 commits into from
Oct 9, 2019
33 changes: 20 additions & 13 deletions brokers/unified/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ import (
"gopkg.in/yaml.v2"
)

const ChePluginType = "che plugin"
const EditorPluginType = "che editor"
const TheiaPluginType = "theia plugin"
const VscodePluginType = "vs code extension"

// RegistryURLFormat specifies the format string for registry urls
// when downloading metas
const RegistryURLFormat = "%s/%s/meta.yaml"
Expand Down Expand Up @@ -100,6 +95,17 @@ func (b *Broker) Start(pluginFQNs []model.PluginFQN, defaultRegistry string) err
return err
}

plugins, err := b.Storage.Plugins()
if err != nil {
b.PubFailed(err.Error())
b.PubLog(err.Error())
}

if err := InjectRemoteRuntime(plugins); err != nil {
b.PubFailed(err.Error())
b.PubLog(err.Error())
}

result, err := b.serializeTooling()
if err != nil {
b.PubFailed(err.Error())
Expand Down Expand Up @@ -232,18 +238,18 @@ func ValidateMeta(meta model.PluginMeta) error {
}

switch strings.ToLower(meta.Type) {
case ChePluginType:
case model.ChePluginType:
fallthrough
case EditorPluginType:
case model.EditorPluginType:
if len(meta.Spec.Extensions) != 0 {
return fmt.Errorf("Plugin '%s' is invalid. Field 'spec.extensions' is not allowed in plugin of type '%s'", meta.ID, meta.Type)
}
if len(meta.Spec.Containers) == 0 {
return fmt.Errorf("Plugin '%s' is invalid. Field 'spec.containers' must not be empty", meta.ID)
}
case TheiaPluginType:
case model.TheiaPluginType:
fallthrough
case VscodePluginType:
case model.VscodePluginType:
if len(meta.Spec.Extensions) == 0 {
return fmt.Errorf("Plugin '%s' is invalid. Field 'spec.extensions' must not be empty", meta.ID)
}
Expand Down Expand Up @@ -271,13 +277,13 @@ func sortMetas(metas []model.PluginMeta) (che []model.PluginMeta, vscode []model
cheBrokerMetas := make([]model.PluginMeta, 0)
for _, meta := range metas {
switch strings.ToLower(meta.Type) {
case ChePluginType:
case model.ChePluginType:
fallthrough
case EditorPluginType:
case model.EditorPluginType:
cheBrokerMetas = append(cheBrokerMetas, meta)
case TheiaPluginType:
case model.TheiaPluginType:
fallthrough
case VscodePluginType:
case model.VscodePluginType:
vscodeMetas = append(vscodeMetas, meta)
case "":
return nil, nil, fmt.Errorf("Type field is missing in meta information of plugin '%s'", meta.ID)
Expand Down Expand Up @@ -339,5 +345,6 @@ func ConvertMetaToPlugin(meta model.PluginMeta) model.ChePlugin {
InitContainers: meta.Spec.InitContainers,
Endpoints: meta.Spec.Endpoints,
WorkspaceEnv: meta.Spec.WorkspaceEnv,
Type: meta.Type,
}
}
29 changes: 18 additions & 11 deletions brokers/unified/broker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func TestBroker_processPlugins(t *testing.T) {
},
},
want: want{
commonPlugins: []model.ChePlugin{createChePlugin("id2"), createChePlugin("id6")},
commonPlugins: []model.ChePlugin{createChePlugin("id2", TestChePluginType), createChePlugin("id6", TestChePluginType)},
vscodeMetas: []model.PluginMeta{createVSCodeMeta("id1"), createVSCodeMeta("id4"), createTheiaMeta("id3"), createTheiaMeta("id5")},
},
},
Expand All @@ -283,7 +283,7 @@ func TestBroker_processPlugins(t *testing.T) {
metas: []model.PluginMeta{createChePluginMeta("id1"), createCheEditorMeta("id2")},
},
want: want{
commonPlugins: []model.ChePlugin{createChePlugin("id1"), createChePlugin("id2")},
commonPlugins: []model.ChePlugin{createChePlugin("id1", TestChePluginType), createChePlugin("id2", TestEditorPluginType)},
},
},
{
Expand All @@ -297,7 +297,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name1",
Version: "v0.13",
ID: "id1",
Type: ChePluginType,
Type: model.ChePluginType,
Title: "test title",
DisplayName: "test display name",
Description: "test description",
Expand Down Expand Up @@ -339,7 +339,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name2",
Version: "v0",
ID: "id2",
Type: EditorPluginType,
Type: model.EditorPluginType,
Title: "test title",
DisplayName: "test display name",
Description: "test description",
Expand Down Expand Up @@ -374,6 +374,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name1",
Version: "v0.13",
ID: "id1",
Type: model.ChePluginType,
Endpoints: []model.Endpoint{
{
Name: "end1",
Expand Down Expand Up @@ -408,6 +409,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name2",
Version: "v0",
ID: "id2",
Type: model.EditorPluginType,
Endpoints: []model.Endpoint{
{
Name: "end2",
Expand Down Expand Up @@ -441,7 +443,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name1",
Version: "v0.13",
ID: "id1",
Type: ChePluginType,
Type: model.ChePluginType,
Title: "test title",
DisplayName: "test display name",
Description: "test description",
Expand Down Expand Up @@ -486,6 +488,7 @@ func TestBroker_processPlugins(t *testing.T) {
Name: "name1",
Version: "v0.13",
ID: "id1",
Type: model.ChePluginType,
Endpoints: []model.Endpoint{
{
Name: "end1",
Expand Down Expand Up @@ -685,23 +688,26 @@ func TestBroker_processPlugins(t *testing.T) {
},
commonPlugins: []model.ChePlugin{
{
ID: "id11",
ID: "id11",
Type: "che plugin",
Containers: []model.Container{
{
Image: defaultImage,
},
},
},
{
ID: "id12",
ID: "id12",
Type: "Che Plugin",
Containers: []model.Container{
{
Image: defaultImage,
},
},
},
{
ID: "id13",
ID: "id13",
Type: "cHE plugIN",
Containers: []model.Container{
{
Image: defaultImage,
Expand Down Expand Up @@ -752,7 +758,7 @@ func TestBroker_processPlugins(t *testing.T) {
args: args{
metas: []model.PluginMeta{
{
Type: ChePluginType,
Type: model.ChePluginType,
ID: "test id",
Version: "test version",
Publisher: "test publisher",
Expand Down Expand Up @@ -1541,9 +1547,10 @@ func createMetaWithExtension(ID string, extensions ...string) model.PluginMeta {
}
}

func createChePlugin(ID string) model.ChePlugin {
func createChePlugin(ID string, pluginType string) model.ChePlugin {
return model.ChePlugin{
ID: ID,
ID: ID,
Type: pluginType,
Containers: []model.Container{
{
Image: defaultImage,
Expand Down
147 changes: 147 additions & 0 deletions brokers/unified/remote_runtime_injector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//
// Copyright (c) 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
//

package unified

import (
"errors"
"strings"

"github.com/eclipse/che-plugin-broker/model"
)

const (
CheTheiaEditorName = "che-theia"

InjectorContainerName = "remote-runtime-injector"

RemoteEndpointExecutableEnvVar = "PLUGIN_REMOTE_ENDPOINT_EXECUTABLE"
VolumeNameEnvVar = "REMOTE_ENDPOINT_VOLUME_NAME"
)

type RemotePluginInjection struct {
Volume model.Volume
Env model.EnvVar
Command []string
Args []string
}

func InjectRemoteRuntime(plugins []model.ChePlugin) error {
editorPlugin := findCheTheiaEditor(plugins)
if editorPlugin == nil {
return nil
}

injection, err := getRuntimeInjection(editorPlugin)
if err != nil {
return err
}

for _, plugin := range plugins {
inject(&plugin, injection)
}

return nil
}

func findCheTheiaEditor(plugins []model.ChePlugin) *model.ChePlugin {
for _, plugin := range plugins {
if strings.ToLower(plugin.Type) == model.EditorPluginType &&
strings.ToLower(plugin.Name) == CheTheiaEditorName &&
len(plugin.InitContainers) > 0 {
return &plugin
}
}
// it's ok, maybe used some another editor instead of che-theia
return nil
}

func getRuntimeInjection(editorPlugin *model.ChePlugin) (*RemotePluginInjection, error) {
containerInjector, err := findContainerInjector(editorPlugin.InitContainers)
if err != nil {
// it's ok, older che-theia could be without runtime injection
return nil, nil
}

runtimeBinaryPathEnv, err := findEnv(RemoteEndpointExecutableEnvVar, containerInjector.Env)
if err != nil {
return nil, err
}

volumeName, err := findEnv(VolumeNameEnvVar, containerInjector.Env)
if err != nil {
return nil, err
}

volume, err := findVolume(volumeName.Value, containerInjector.Volumes)
if err != nil {
return nil, err
}

return &RemotePluginInjection{
Volume: *volume,
Env: *runtimeBinaryPathEnv,
Command: []string{runtimeBinaryPathEnv.Value},
}, nil
}

func findContainerInjector(containers []model.Container) (*model.Container, error) {
for _, container := range containers {
if container.Name == InjectorContainerName {
return &container, nil
}
}
return nil, errors.New("Unable to find injector container")
}

func findEnv(envName string, envVars []model.EnvVar) (*model.EnvVar, error) {
var result *model.EnvVar
for _, envVar := range envVars {
if envVar.Name == envName {
result = &envVar
break
}
}
if result == nil {
return nil, errors.New("Unable to find required env with name " + envName)
}
if result.Value == "" {
return nil, errors.New("Required env with name " + envName + " was found, but value is empty")
}

return result, nil
}

func findVolume(volumeName string, volumes []model.Volume) (*model.Volume, error) {
for _, volume := range volumes {
if volume.Name == volumeName {
return &volume, nil
}
}
return nil, errors.New("Unable to find volume by name " + volumeName)
}

func inject(plugin *model.ChePlugin, injection *RemotePluginInjection) {
pluginType := strings.ToLower(plugin.Type)

if pluginType != model.TheiaPluginType && pluginType != model.VscodePluginType {
return
}
// sidecar container has one and only one container.
container := &plugin.Containers[0]

container.Env = append(container.Env, injection.Env)
container.Volumes = append(container.Volumes, injection.Volume)
if len(container.Command) == 0 && len(container.Args) == 0 {
container.Command = injection.Command
}
}
Loading