mirror of
https://github.com/jenkinsci/kubernetes-operator.git
synced 2026-09-30 05:01:10 +02:00
reorganise directories for better readbility
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package plugins
|
||||
|
||||
const (
|
||||
configurationAsCodePlugin = "configuration-as-code:1.38"
|
||||
gitPlugin = "git:4.2.2"
|
||||
jobDslPlugin = "job-dsl:1.77"
|
||||
kubernetesCredentialsProviderPlugin = "kubernetes-credentials-provider:0.13"
|
||||
kubernetesPlugin = "kubernetes:1.25.2"
|
||||
workflowAggregatorPlugin = "workflow-aggregator:2.6"
|
||||
workflowJobPlugin = "workflow-job:2.38"
|
||||
)
|
||||
|
||||
// basePluginsList contains plugins to install by operator.
|
||||
var basePluginsList = []Plugin{
|
||||
Must(New(kubernetesPlugin)),
|
||||
Must(New(workflowJobPlugin)),
|
||||
Must(New(workflowAggregatorPlugin)),
|
||||
Must(New(gitPlugin)),
|
||||
Must(New(jobDslPlugin)),
|
||||
Must(New(configurationAsCodePlugin)),
|
||||
Must(New(kubernetesCredentialsProviderPlugin)),
|
||||
}
|
||||
|
||||
// BasePlugins returns list of plugins to install by operator.
|
||||
func BasePlugins() []Plugin {
|
||||
return basePluginsList
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Plugin represents jenkins plugin.
|
||||
type Plugin struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
DownloadURL string `json:"downloadURL"`
|
||||
rootPluginNameAndVersion string
|
||||
}
|
||||
|
||||
func (p Plugin) String() string {
|
||||
return fmt.Sprintf("%s:%s", p.Name, p.Version)
|
||||
}
|
||||
|
||||
var (
|
||||
// NamePattern is the plugin name regex pattern
|
||||
NamePattern = regexp.MustCompile(`^[0-9a-zA-Z-_]+$`)
|
||||
// VersionPattern is the plugin version regex pattern
|
||||
VersionPattern = regexp.MustCompile(`^[0-9a-zA-Z+\\.-]+$`)
|
||||
// DownloadURLPattern is the plugin download url regex pattern
|
||||
DownloadURLPattern = regexp.MustCompile(`https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`)
|
||||
)
|
||||
|
||||
// New creates plugin from string, for example "name-of-plugin:0.0.1".
|
||||
func New(nameWithVersion string) (*Plugin, error) {
|
||||
val := strings.SplitN(nameWithVersion, ":", 2)
|
||||
if val == nil || len(val) != 2 {
|
||||
return nil, errors.Errorf("invalid plugin format '%s'", nameWithVersion)
|
||||
}
|
||||
name := val[0]
|
||||
version := val[1]
|
||||
|
||||
if err := validatePlugin(name, version, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Plugin{
|
||||
Name: name,
|
||||
Version: version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewPlugin creates plugin from name and version, for example "name-of-plugin:0.0.1".
|
||||
func NewPlugin(name, version, downloadURL string) (*Plugin, error) {
|
||||
if err := validatePlugin(name, version, downloadURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Plugin{
|
||||
Name: name,
|
||||
Version: version,
|
||||
DownloadURL: downloadURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validatePlugin(name, version, downloadURL string) error {
|
||||
if ok := NamePattern.MatchString(name); !ok {
|
||||
return errors.Errorf("invalid plugin name '%s:%s', must follow pattern '%s'", name, version, NamePattern.String())
|
||||
}
|
||||
if ok := VersionPattern.MatchString(version); !ok {
|
||||
return errors.Errorf("invalid plugin version '%s:%s', must follow pattern '%s'", name, version, VersionPattern.String())
|
||||
}
|
||||
if len(downloadURL) > 0 {
|
||||
if ok := DownloadURLPattern.MatchString(downloadURL); !ok {
|
||||
return errors.Errorf("invalid download URL '%s' for plugin name %s:%s, must follow pattern '%s'", downloadURL, name, version, DownloadURLPattern.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must returns plugin from pointer and throws panic when error is set.
|
||||
func Must(plugin *Plugin, err error) Plugin {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return *plugin
|
||||
}
|
||||
|
||||
// VerifyDependencies checks if all plugins have compatible versions.
|
||||
func VerifyDependencies(values ...map[Plugin][]Plugin) []string {
|
||||
var messages []string
|
||||
// key - plugin name, value array of versions
|
||||
allPlugins := make(map[string][]Plugin)
|
||||
|
||||
for _, value := range values {
|
||||
for rootPlugin, plugins := range value {
|
||||
allPlugins[rootPlugin.Name] = append(allPlugins[rootPlugin.Name], Plugin{
|
||||
Name: rootPlugin.Name,
|
||||
Version: rootPlugin.Version,
|
||||
rootPluginNameAndVersion: rootPlugin.String()})
|
||||
for _, plugin := range plugins {
|
||||
allPlugins[plugin.Name] = append(allPlugins[plugin.Name], Plugin{
|
||||
Name: plugin.Name,
|
||||
Version: plugin.Version,
|
||||
rootPluginNameAndVersion: rootPlugin.String()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pluginName, versions := range allPlugins {
|
||||
if len(versions) == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, firstVersion := range versions {
|
||||
for _, secondVersion := range versions {
|
||||
if firstVersion.Version != secondVersion.Version {
|
||||
messages = append(messages, fmt.Sprintf("Plugin '%s' requires version '%s' but plugin '%s' requires '%s' for plugin '%s'",
|
||||
firstVersion.rootPluginNameAndVersion,
|
||||
firstVersion.Version,
|
||||
secondVersion.rootPluginNameAndVersion,
|
||||
secondVersion.Version,
|
||||
pluginName,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidatePlugin(t *testing.T) {
|
||||
validPluginName := "valid"
|
||||
validPluginVersion := "0.1.2"
|
||||
t.Run("version 1.6+build.162", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "1.6+build.162", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("version 1.8+build.201601050116", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "1.8+build.201601050116", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("version 1.8-RELEASE", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "1.8-RELEASE", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("version 20.810504d7462", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "20.810504d7462", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
|
||||
t.Run("version 3.0-rc1", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "3.0-rc1", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("version 3.1.20180605-140134.c2e96c4", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "3.1.20180605-140134.c2e96c4", "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("invalid version !", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, "0.5.1!", "")
|
||||
assert.Error(t, got)
|
||||
})
|
||||
t.Run("name 01234567890-abcdefghijklmnoprstuwxz_ABCDEFGHIJKLMNOPQRSTUVWXYZ", func(t *testing.T) {
|
||||
got := validatePlugin("01234567890-abcdefghijklmnoprstuwxz_ABCDEFGHIJKLMNOPQRSTUVWXYZ", validPluginVersion, "")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("invalid name !", func(t *testing.T) {
|
||||
got := validatePlugin("!", validPluginVersion, "")
|
||||
assert.Error(t, got)
|
||||
})
|
||||
t.Run("invalid download URL", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, validPluginVersion, "http://www.jenkins/plugin.hpi")
|
||||
assert.Error(t, got)
|
||||
})
|
||||
t.Run("valid http download URL", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, validPluginVersion, "http://www.jenkins.com/plugin.hpi")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
t.Run("valid https download URL", func(t *testing.T) {
|
||||
got := validatePlugin(validPluginName, validPluginVersion, "https://www.jenkins.com/plugin.hpi")
|
||||
assert.NoError(t, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVerifyDependencies(t *testing.T) {
|
||||
log.SetupLogger(false)
|
||||
|
||||
t.Run("happy, single root plugin with one dependent plugin", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
t.Run("happy, two root plugins with one depended plugin with the same version", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
Must(New("second-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
t.Run("happy, two plugin names with names with underscores", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first_root_plugin:1.0.0")): {
|
||||
Must(New("first_plugin:0.0.1")),
|
||||
},
|
||||
Must(New("second_root_plugin:1.0.0")): {
|
||||
Must(New("first_plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
t.Run("happy, two plugin names with uppercase names", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("First-Root-Plugin:1.0.0")): {
|
||||
Must(New("First_Plugin:0.0.1")),
|
||||
},
|
||||
Must(New("Second_Root_Plugin:1.0.0")): {
|
||||
Must(New("First_Plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
t.Run("fail, two root plugins have different versions", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
Must(New("first-root-plugin:2.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:1.0.0' requires version '1.0.0' but plugin 'first-root-plugin:2.0.0' requires '2.0.0' for plugin 'first-root-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:2.0.0' requires version '2.0.0' but plugin 'first-root-plugin:1.0.0' requires '1.0.0' for plugin 'first-root-plugin'")
|
||||
})
|
||||
t.Run("happy, no version collision with two separate plugins lists", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
extraPlugins := map[Plugin][]Plugin{
|
||||
Must(New("second-root-plugin:2.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins, extraPlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
t.Run("fail, dependent plugins have different versions", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
Must(New("first-root-plugin:2.0.0")): {
|
||||
Must(New("first-plugin:0.0.2")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:1.0.0' requires version '1.0.0' but plugin 'first-root-plugin:2.0.0' requires '2.0.0' for plugin 'first-root-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:2.0.0' requires version '2.0.0' but plugin 'first-root-plugin:1.0.0' requires '1.0.0' for plugin 'first-root-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:1.0.0' requires version '0.0.1' but plugin 'first-root-plugin:2.0.0' requires '0.0.2' for plugin 'first-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:2.0.0' requires version '0.0.2' but plugin 'first-root-plugin:1.0.0' requires '0.0.1' for plugin 'first-plugin'")
|
||||
})
|
||||
t.Run("fail, root and dependent plugins have different versions", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0")): {
|
||||
Must(New("first-plugin:0.0.1")),
|
||||
},
|
||||
}
|
||||
extraPlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:2.0.0")): {
|
||||
Must(New("first-plugin:0.0.2")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins, extraPlugins)
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:1.0.0' requires version '1.0.0' but plugin 'first-root-plugin:2.0.0' requires '2.0.0' for plugin 'first-root-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:2.0.0' requires version '2.0.0' but plugin 'first-root-plugin:1.0.0' requires '1.0.0' for plugin 'first-root-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:1.0.0' requires version '0.0.1' but plugin 'first-root-plugin:2.0.0' requires '0.0.2' for plugin 'first-plugin'")
|
||||
assert.Contains(t, got, "Plugin 'first-root-plugin:2.0.0' requires version '0.0.2' but plugin 'first-root-plugin:1.0.0' requires '0.0.1' for plugin 'first-plugin'")
|
||||
})
|
||||
t.Run("happy with dash in version", func(t *testing.T) {
|
||||
basePlugins := map[Plugin][]Plugin{
|
||||
Must(New("first-root-plugin:1.0.0-1")): {
|
||||
Must(New("first-plugin:0.0.1-1")),
|
||||
},
|
||||
}
|
||||
got := VerifyDependencies(basePlugins)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user