From 970449f04f7be3a0c6cee70de154098f30ba4729 Mon Sep 17 00:00:00 2001 From: salluvada Date: Sun, 29 Mar 2020 14:41:16 -0400 Subject: [PATCH] #296 Allow to specify custom plugins location in jenkins CR (#309) --- pkg/apis/jenkins/v1alpha2/jenkins_types.go | 2 ++ .../base/resources/scripts_configmap.go | 4 +-- .../jenkins/configuration/base/validate.go | 4 +-- pkg/controller/jenkins/plugins/plugin.go | 21 +++++++++---- pkg/controller/jenkins/plugins/plugin_test.go | 30 +++++++++++++------ test/e2e/jenkins.go | 1 + 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/pkg/apis/jenkins/v1alpha2/jenkins_types.go b/pkg/apis/jenkins/v1alpha2/jenkins_types.go index 385b10bc..1eb665d7 100644 --- a/pkg/apis/jenkins/v1alpha2/jenkins_types.go +++ b/pkg/apis/jenkins/v1alpha2/jenkins_types.go @@ -250,6 +250,8 @@ type Plugin struct { Name string `json:"name"` // Version is the version of Jenkins plugin Version string `json:"version"` + // DownloadURL is the custom url from where plugin has to be downloaded. + DownloadURL string `json:"downloadURL,omitempty"` } // JenkinsMaster defines the Jenkins master pod attributes and plugins, diff --git a/pkg/controller/jenkins/configuration/base/resources/scripts_configmap.go b/pkg/controller/jenkins/configuration/base/resources/scripts_configmap.go index 1f4849ac..ac52e7ba 100644 --- a/pkg/controller/jenkins/configuration/base/resources/scripts_configmap.go +++ b/pkg/controller/jenkins/configuration/base/resources/scripts_configmap.go @@ -268,7 +268,7 @@ chmod +x {{ .JenkinsHomePath }}/scripts/*.sh echo "Installing plugins required by Operator - begin" cat > {{ .JenkinsHomePath }}/base-plugins << EOF {{ range $index, $plugin := .BasePlugins }} -{{ $plugin.Name }}:{{ $plugin.Version }} +{{ $plugin.Name }}:{{ $plugin.Version }}{{if $plugin.DownloadURL}}:{{ $plugin.DownloadURL }}{{end}} {{ end }} EOF @@ -282,7 +282,7 @@ echo "Installing plugins required by Operator - end" echo "Installing plugins required by user - begin" cat > {{ .JenkinsHomePath }}/user-plugins << EOF {{ range $index, $plugin := .UserPlugins }} -{{ $plugin.Name }}:{{ $plugin.Version }} +{{ $plugin.Name }}:{{ $plugin.Version }}{{if $plugin.DownloadURL}}:{{ $plugin.DownloadURL }}{{end}} {{ end }} EOF if [[ -z "${OPENSHIFT_JENKINS_IMAGE_VERSION}" ]]; then diff --git a/pkg/controller/jenkins/configuration/base/validate.go b/pkg/controller/jenkins/configuration/base/validate.go index dfe1410e..59453697 100644 --- a/pkg/controller/jenkins/configuration/base/validate.go +++ b/pkg/controller/jenkins/configuration/base/validate.go @@ -320,7 +320,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(requiredBasePlugins allPlugins := map[plugins.Plugin][]plugins.Plugin{} for _, jenkinsPlugin := range basePlugins { - plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version) + plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version, jenkinsPlugin.DownloadURL) if err != nil { messages = append(messages, err.Error()) } @@ -331,7 +331,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(requiredBasePlugins } for _, jenkinsPlugin := range userPlugins { - plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version) + plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version, jenkinsPlugin.DownloadURL) if err != nil { messages = append(messages, err.Error()) } diff --git a/pkg/controller/jenkins/plugins/plugin.go b/pkg/controller/jenkins/plugins/plugin.go index a835a7d1..aa95d023 100644 --- a/pkg/controller/jenkins/plugins/plugin.go +++ b/pkg/controller/jenkins/plugins/plugin.go @@ -12,6 +12,7 @@ import ( type Plugin struct { Name string `json:"name"` Version string `json:"version"` + DownloadURL string `json:"downloadURL"` rootPluginNameAndVersion string } @@ -24,6 +25,8 @@ var ( 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" @@ -35,7 +38,7 @@ func New(nameWithVersion string) (*Plugin, error) { name := val[0] version := val[1] - if err := validatePlugin(name, version); err != nil { + if err := validatePlugin(name, version, ""); err != nil { return nil, err } @@ -46,24 +49,30 @@ func New(nameWithVersion string) (*Plugin, error) { } // NewPlugin creates plugin from name and version, for example "name-of-plugin:0.0.1" -func NewPlugin(name, version string) (*Plugin, error) { - if err := validatePlugin(name, version); err != nil { +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, + Name: name, + Version: version, + DownloadURL: downloadURL, }, nil } -func validatePlugin(name, version string) error { +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 } diff --git a/pkg/controller/jenkins/plugins/plugin_test.go b/pkg/controller/jenkins/plugins/plugin_test.go index 2ab9ac7f..0c46f597 100644 --- a/pkg/controller/jenkins/plugins/plugin_test.go +++ b/pkg/controller/jenkins/plugins/plugin_test.go @@ -12,42 +12,54 @@ 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") + 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") + 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") + got := validatePlugin(validPluginName, "1.8-RELEASE", "") assert.NoError(t, got) }) t.Run("version 20.810504d7462", func(t *testing.T) { - got := validatePlugin(validPluginName, "20.810504d7462") + 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") + 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") + 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!") + 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) + got := validatePlugin("01234567890-abcdefghijklmnoprstuwxz_ABCDEFGHIJKLMNOPQRSTUVWXYZ", validPluginVersion, "") assert.NoError(t, got) }) t.Run("invalid name !", func(t *testing.T) { - got := validatePlugin("!", validPluginVersion) + 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) { diff --git a/test/e2e/jenkins.go b/test/e2e/jenkins.go index daa69554..f93b8848 100644 --- a/test/e2e/jenkins.go +++ b/test/e2e/jenkins.go @@ -117,6 +117,7 @@ func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha2.S {Name: "audit-trail", Version: "2.4"}, {Name: "simple-theme-plugin", Version: "0.5.1"}, {Name: "github", Version: "1.29.4"}, + {Name: "devoptics", Version: "1.1863", DownloadURL: "https://jenkins-updates.cloudbees.com/download/plugins/devoptics/1.1863/devoptics.hpi"}, }, NodeSelector: map[string]string{"kubernetes.io/os": "linux"}, Volumes: []corev1.Volume{