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

Commit

Permalink
Check plugins' extensions for conflicts during metadata broker
Browse files Browse the repository at this point in the history
To warn the user of issues with multiple plugins depending on the same
extension, add a step to metadata brokering to detect extensions used by
mutliple plugins and print a warning.

Current approach is very limited, as it depends on extension URLs
matching, so some cases are misse

Signed-off-by: Angel Misevski <amisevsk@redhat.com>
  • Loading branch information
amisevsk committed Feb 14, 2020
1 parent 42d418a commit ee45bb5
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 1 deletion.
8 changes: 7 additions & 1 deletion brokers/metadata/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
// when downloading metas
const RegistryURLFormat = "%s/%s/meta.yaml"

// Broker is used to process Che plugins
// Broker is used to process Che plugins
type Broker struct {
common.Broker
Expand Down Expand Up @@ -69,6 +68,13 @@ func (b *Broker) Start(pluginFQNs []model.PluginFQN, defaultRegistry string) err
}
b.PrintPlan(pluginMetas)

if collisions := utils.GetExtensionCollisions(pluginMetas); len(collisions) > 0 {
collisionLog := []string{"WARNING: multiple instances of the same extension will be included in this workspace:"}
collisionLog = append(collisionLog, utils.ConvertCollisionsToLog(collisions)...)
collisionLog = append(collisionLog, "These plugins may not work as expected. If errors occur please try disabling all but one of the conflicting plugins.")
b.PrintInfoBuffer(collisionLog)
}

// Process plugins into ChePlugins
plugins, err := b.ProcessPlugins(pluginMetas)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions brokers/testdata/config-plugin-ids.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
{
"id": "ms-kubernetes-tools/vscode-kubernetes-tools/0.1.17"
},
{
"id": "redhat/vscode-openshift-connector/0.1.2"
},
{
"id": "eclipse/che-machine-exec-plugin/0.0.1"
},
Expand Down
56 changes: 56 additions & 0 deletions utils/dependencies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// Copyright (c) 2020 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 utils

import (
"fmt"

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

// GetExtensionCollisions checks a list of plugin metas for extensions shared by
// more than one plugin. Return value is a list of
func GetExtensionCollisions(metas []model.PluginMeta) map[string][]string {
extensions := map[string][]string{}
for _, meta := range metas {
for _, ext := range meta.Spec.Extensions {
extensions[ext] = append(extensions[ext], meta.ID)
}
}
collisions := make(map[string][]string)
for ext, ids := range extensions {
if len(ids) > 1 {
collisions[ext] = ids
}
}
return collisions
}

// ConvertCollisionsToLog converts the output of GetExtensionCollisions to a human-readable
// string. Output is a slice of strings, to be joined by newlines
func ConvertCollisionsToLog(collisions map[string][]string) []string {
var output []string
for ext, plugins := range collisions {
output = append(output, fmt.Sprintf(" Plugins"))
for _, plugin := range plugins {
output = append(output, fmt.Sprintf(" - %s", plugin))
}
if len(plugins) > 2 {
output = append(output, fmt.Sprintf(" all depend on and embed extension"))
} else {
output = append(output, fmt.Sprintf(" both depend on and embed extension"))
}
output = append(output, fmt.Sprintf(" %s", ext))
}
return output
}
86 changes: 86 additions & 0 deletions utils/dependencies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// Copyright (c) 2020 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 utils

import (
"regexp"
"strings"
"testing"

"github.com/stretchr/testify/assert"

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

func TestGetExtensionCollisions(t *testing.T) {
metas := []model.PluginMeta{
generateMetaWithExtensions("test/conflict_one", "aaa", "bbb"),
generateMetaWithExtensions("test/no_conflict", "ddd"),
generateMetaWithExtensions("test/conflict_two", "ccc", "bbb"),
}

output := GetExtensionCollisions(metas)
assert.NotEmpty(t, output)
assert.ElementsMatch(t, output["bbb"], []string{"test/conflict_one", "test/conflict_two"})
assert.NotContains(t, output, "aaa")
assert.NotContains(t, output, "ccc")
assert.NotContains(t, output, "ddd")
}

func TestGetExtensionCollisionsMulti(t *testing.T) {
metas := []model.PluginMeta{
generateMetaWithExtensions("one", "aaa", "bbb"),
generateMetaWithExtensions("two", "xxx", "bbb"),
generateMetaWithExtensions("three", "aaa", "yyy"),
}

output := GetExtensionCollisions(metas)
assert.NotEmpty(t, output)
assert.ElementsMatch(t, output["aaa"], []string{"one", "three"})
assert.ElementsMatch(t, output["bbb"], []string{"one", "two"})
assert.NotContains(t, output, "xxx")
assert.NotContains(t, output, "yyy")
}

func TestConvertCollisionsToLog(t *testing.T) {
collisions := map[string][]string{
"ext1": []string{"plugin_a", "plugin_b"},
"ext2": []string{"plugin_c", "plugin_d", "plugin_e"},
}
output := ConvertCollisionsToLog(collisions)
for _, test := range []string{
".*ext1.*", ".*ext2.*", ".*plugin_a.*", ".*plugin_b.*", ".*plugin_c.*", ".*plugin_d.*", ".*plugin_e.*",
} {
assertMatchSliceRegexp(t, output, test)
}
}

func generateMetaWithExtensions(id string, exts ...string) model.PluginMeta {
return model.PluginMeta{
ID: id,
Spec: model.PluginMetaSpec{
Extensions: exts,
},
}
}

func assertMatchSliceRegexp(t *testing.T, slice []string, pattern string) {
re := regexp.MustCompile(pattern)
match := false
for _, str := range slice {
if re.MatchString(str) {
match = true
}
}
assert.Truef(t, match, "Expected '%s' to be logged but got:\n%s", pattern, strings.Join(slice, "\n"))
}

0 comments on commit ee45bb5

Please sign in to comment.