Improve plugins management
This commit is contained in:
parent
8178e2315e
commit
e1aba3ed9f
|
|
@ -1,21 +1,5 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
|
@ -49,6 +33,79 @@ func (in *Build) DeepCopy() *Build {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Container) DeepCopyInto(out *Container) {
|
||||
*out = *in
|
||||
if in.Command != nil {
|
||||
in, out := &in.Command, &out.Command
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Args != nil {
|
||||
in, out := &in.Args, &out.Args
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Ports != nil {
|
||||
in, out := &in.Ports, &out.Ports
|
||||
*out = make([]v1.ContainerPort, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.EnvFrom != nil {
|
||||
in, out := &in.EnvFrom, &out.EnvFrom
|
||||
*out = make([]v1.EnvFromSource, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Env != nil {
|
||||
in, out := &in.Env, &out.Env
|
||||
*out = make([]v1.EnvVar, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
if in.VolumeMounts != nil {
|
||||
in, out := &in.VolumeMounts, &out.VolumeMounts
|
||||
*out = make([]v1.VolumeMount, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ReadinessProbe != nil {
|
||||
in, out := &in.ReadinessProbe, &out.ReadinessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Lifecycle != nil {
|
||||
in, out := &in.Lifecycle, &out.Lifecycle
|
||||
*out = new(v1.Lifecycle)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(v1.SecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container.
|
||||
func (in *Container) DeepCopy() *Container {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Container)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Jenkins) DeepCopyInto(out *Jenkins) {
|
||||
*out = *in
|
||||
|
|
@ -113,13 +170,7 @@ func (in *JenkinsList) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
|
||||
*out = *in
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.Container.DeepCopyInto(&out.Container)
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
|
|
@ -127,23 +178,26 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
|
|||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
if in.Env != nil {
|
||||
in, out := &in.Env, &out.Env
|
||||
*out = make([]v1.EnvVar, len(*in))
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Containers != nil {
|
||||
in, out := &in.Containers, &out.Containers
|
||||
*out = make([]Container, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ReadinessProbe != nil {
|
||||
in, out := &in.ReadinessProbe, &out.ReadinessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
if in.Volumes != nil {
|
||||
in, out := &in.Volumes, &out.Volumes
|
||||
*out = make([]v1.Volume, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.OperatorPlugins != nil {
|
||||
in, out := &in.OperatorPlugins, &out.OperatorPlugins
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ type Container struct {
|
|||
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
|
||||
}
|
||||
|
||||
// Plugin defines Jenkins plugin
|
||||
type Plugin struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// JenkinsMaster defines the Jenkins master pod attributes and plugins,
|
||||
// every single change requires Jenkins master pod restart
|
||||
type JenkinsMaster struct {
|
||||
|
|
@ -49,10 +55,10 @@ type JenkinsMaster struct {
|
|||
Containers []Container `json:"containers,omitempty"`
|
||||
Volumes []corev1.Volume `json:"volumes,omitempty"`
|
||||
|
||||
// OperatorPlugins contains plugins required by operator
|
||||
OperatorPlugins map[string][]string `json:"basePlugins,omitempty"`
|
||||
// BasePlugins contains plugins required by operator
|
||||
BasePlugins []Plugin `json:"basePlugins,omitempty"`
|
||||
// Plugins contains plugins required by user
|
||||
Plugins map[string][]string `json:"plugins,omitempty"`
|
||||
Plugins []Plugin `json:"plugins,omitempty"`
|
||||
}
|
||||
|
||||
// Service defines Kubernetes service attributes which Operator will manage
|
||||
|
|
|
|||
|
|
@ -1,21 +1,5 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
|
|
@ -49,6 +33,79 @@ func (in *Build) DeepCopy() *Build {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Container) DeepCopyInto(out *Container) {
|
||||
*out = *in
|
||||
if in.Command != nil {
|
||||
in, out := &in.Command, &out.Command
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Args != nil {
|
||||
in, out := &in.Args, &out.Args
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Ports != nil {
|
||||
in, out := &in.Ports, &out.Ports
|
||||
*out = make([]v1.ContainerPort, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.EnvFrom != nil {
|
||||
in, out := &in.EnvFrom, &out.EnvFrom
|
||||
*out = make([]v1.EnvFromSource, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Env != nil {
|
||||
in, out := &in.Env, &out.Env
|
||||
*out = make([]v1.EnvVar, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
if in.VolumeMounts != nil {
|
||||
in, out := &in.VolumeMounts, &out.VolumeMounts
|
||||
*out = make([]v1.VolumeMount, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ReadinessProbe != nil {
|
||||
in, out := &in.ReadinessProbe, &out.ReadinessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Lifecycle != nil {
|
||||
in, out := &in.Lifecycle, &out.Lifecycle
|
||||
*out = new(v1.Lifecycle)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(v1.SecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container.
|
||||
func (in *Container) DeepCopy() *Container {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Container)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Jenkins) DeepCopyInto(out *Jenkins) {
|
||||
*out = *in
|
||||
|
|
@ -120,60 +177,36 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
|
|||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
if in.Env != nil {
|
||||
in, out := &in.Env, &out.Env
|
||||
*out = make([]v1.EnvVar, len(*in))
|
||||
if in.Containers != nil {
|
||||
in, out := &in.Containers, &out.Containers
|
||||
*out = make([]Container, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ReadinessProbe != nil {
|
||||
in, out := &in.ReadinessProbe, &out.ReadinessProbe
|
||||
*out = new(v1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.OperatorPlugins != nil {
|
||||
in, out := &in.OperatorPlugins, &out.OperatorPlugins
|
||||
*out = make(map[string][]string, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []string
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
if in.Volumes != nil {
|
||||
in, out := &in.Volumes, &out.Volumes
|
||||
*out = make([]v1.Volume, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.BasePlugins != nil {
|
||||
in, out := &in.BasePlugins, &out.BasePlugins
|
||||
*out = make([]Plugin, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Plugins != nil {
|
||||
in, out := &in.Plugins, &out.Plugins
|
||||
*out = make(map[string][]string, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []string
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
*out = make([]Plugin, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -247,6 +280,22 @@ func (in *JenkinsStatus) DeepCopy() *JenkinsStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Plugin) DeepCopyInto(out *Plugin) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Plugin.
|
||||
func (in *Plugin) DeepCopy() *Plugin {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Plugin)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SeedJob) DeepCopyInto(out *SeedJob) {
|
||||
*out = *in
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki
|
|||
}
|
||||
r.logger.V(log.VDebug).Info("Jenkins API client set")
|
||||
|
||||
ok, err := r.verifyPlugins(jenkinsClient, plugins.BasePluginsMap)
|
||||
ok, err := r.verifyPlugins(jenkinsClient)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, nil, err
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ func (r *ReconcileJenkinsBaseConfiguration) waitForVolumes() (reconcile.Result,
|
|||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsclient.Jenkins, basePlugins map[string][]plugins.Plugin) (bool, error) {
|
||||
func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsclient.Jenkins) (bool, error) {
|
||||
allPluginsInJenkins, err := jenkinsClient.GetPlugins(fetchAllPlugins)
|
||||
if err != nil {
|
||||
return false, stackerr.WithStack(err)
|
||||
|
|
@ -201,46 +201,16 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsc
|
|||
}
|
||||
r.logger.V(log.VDebug).Info(fmt.Sprintf("Installed plugins '%+v'", installedPlugins))
|
||||
|
||||
userPlugins := map[string][]plugins.Plugin{}
|
||||
for rootPlugin, dependentPluginNames := range r.jenkins.Spec.Master.Plugins {
|
||||
var dependentPlugins []plugins.Plugin
|
||||
for _, pluginNameWithVersion := range dependentPluginNames {
|
||||
plugin, err := plugins.New(pluginNameWithVersion)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
dependentPlugins = append(dependentPlugins, *plugin)
|
||||
}
|
||||
userPlugins[rootPlugin] = dependentPlugins
|
||||
}
|
||||
|
||||
status := true
|
||||
allRequiredPlugins := []map[string][]plugins.Plugin{basePlugins, userPlugins}
|
||||
allRequiredPlugins := [][]v1alpha2.Plugin{r.jenkins.Spec.Master.BasePlugins, r.jenkins.Spec.Master.Plugins}
|
||||
for _, requiredPlugins := range allRequiredPlugins {
|
||||
for rootPluginName, p := range requiredPlugins {
|
||||
rootPlugin, _ := plugins.New(rootPluginName)
|
||||
if found, ok := isPluginInstalled(allPluginsInJenkins, *rootPlugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Missing plugin '%s', actual '%+v'", rootPlugin, found))
|
||||
for _, plugin := range requiredPlugins {
|
||||
if found, ok := isPluginInstalled(allPluginsInJenkins, plugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Missing plugin '%s', actual '%+v'", plugin, found))
|
||||
status = false
|
||||
}
|
||||
for _, requiredPlugin := range p {
|
||||
if found, ok := isPluginInstalled(allPluginsInJenkins, requiredPlugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Missing plugin '%s', actual '%+v'", requiredPlugin, found))
|
||||
status = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for rootPluginName, p := range userPlugins {
|
||||
rootPlugin, _ := plugins.New(rootPluginName)
|
||||
if found, ok := isPluginVersionCompatible(allPluginsInJenkins, *rootPlugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Incompatible plugin '%s' version, actual '%+v'", rootPlugin, found.Version))
|
||||
status = false
|
||||
}
|
||||
for _, requiredPlugin := range p {
|
||||
if found, ok := isPluginInstalled(allPluginsInJenkins, requiredPlugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Incompatible plugin '%s' version, actual '%+v'", requiredPlugin, found.Version))
|
||||
if found, ok := isPluginVersionCompatible(allPluginsInJenkins, plugin); !ok {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Incompatible plugin '%s' version, actual '%+v'", plugin, found.Version))
|
||||
status = false
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +219,7 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsc
|
|||
return status, nil
|
||||
}
|
||||
|
||||
func isPluginVersionCompatible(plugins *gojenkins.Plugins, plugin plugins.Plugin) (gojenkins.Plugin, bool) {
|
||||
func isPluginVersionCompatible(plugins *gojenkins.Plugins, plugin v1alpha2.Plugin) (gojenkins.Plugin, bool) {
|
||||
p := plugins.Contains(plugin.Name)
|
||||
if p == nil {
|
||||
return gojenkins.Plugin{}, false
|
||||
|
|
@ -262,7 +232,7 @@ func isValidPlugin(plugin gojenkins.Plugin) bool {
|
|||
return plugin.Active && plugin.Enabled && !plugin.Deleted
|
||||
}
|
||||
|
||||
func isPluginInstalled(plugins *gojenkins.Plugins, requiredPlugin plugins.Plugin) (gojenkins.Plugin, bool) {
|
||||
func isPluginInstalled(plugins *gojenkins.Plugins, requiredPlugin v1alpha2.Plugin) (gojenkins.Plugin, bool) {
|
||||
p := plugins.Contains(requiredPlugin.Name)
|
||||
if p == nil {
|
||||
return gojenkins.Plugin{}, false
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
"github.com/bndr/gojenkins"
|
||||
|
|
@ -167,19 +166,67 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
pluginsInJenkins := &gojenkins.Plugins{
|
||||
Raw: &gojenkins.PluginResponse{},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("happy, not empty base and user plugins", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
BasePlugins: []v1alpha2.Plugin{{Name: "plugin-name1", Version: "0.0.1"}},
|
||||
Plugins: []v1alpha2.Plugin{{Name: "plugin-name2", Version: "0.0.1"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
r := ReconcileJenkinsBaseConfiguration{
|
||||
logger: log.Log,
|
||||
jenkins: jenkins,
|
||||
}
|
||||
pluginsInJenkins := &gojenkins.Plugins{
|
||||
Raw: &gojenkins.PluginResponse{
|
||||
Plugins: []gojenkins.Plugin{
|
||||
{
|
||||
ShortName: "plugin-name1",
|
||||
Active: true,
|
||||
Deleted: false,
|
||||
Enabled: true,
|
||||
Version: "0.0.1",
|
||||
},
|
||||
{
|
||||
ShortName: "plugin-name2",
|
||||
Active: true,
|
||||
Deleted: false,
|
||||
Enabled: true,
|
||||
Version: "0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("happy, not empty base and empty user plugins", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{}
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
BasePlugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.1"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
r := ReconcileJenkinsBaseConfiguration{
|
||||
logger: log.Log,
|
||||
jenkins: jenkins,
|
||||
|
|
@ -197,15 +244,12 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{
|
||||
"plugin-name:0.0.1": {},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, got)
|
||||
|
|
@ -214,7 +258,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
Plugins: map[string][]string{"plugin-name:0.0.1": {}},
|
||||
Plugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.1"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -235,19 +279,24 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("happy, plugin version doesn't matter for base plugins", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{}
|
||||
t.Run("happy, plugin version matter for base plugins", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
BasePlugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.1"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
r := ReconcileJenkinsBaseConfiguration{
|
||||
logger: log.Log,
|
||||
jenkins: jenkins,
|
||||
|
|
@ -265,24 +314,21 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{
|
||||
"plugin-name:0.0.1": {},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, got)
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("plugin version matter for user plugins", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
Plugins: map[string][]string{"plugin-name:0.0.2": {}},
|
||||
Plugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.2"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -303,46 +349,21 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("missing base plugin", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{}
|
||||
r := ReconcileJenkinsBaseConfiguration{
|
||||
logger: log.Log,
|
||||
jenkins: jenkins,
|
||||
}
|
||||
pluginsInJenkins := &gojenkins.Plugins{
|
||||
Raw: &gojenkins.PluginResponse{
|
||||
Plugins: []gojenkins.Plugin{},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{
|
||||
"plugin-name:0.0.2": {},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("missing user plugin", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
Plugins: map[string][]string{"plugin-name:0.0.2": {}},
|
||||
BasePlugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.2"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -355,13 +376,39 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
|
|||
Plugins: []gojenkins.Plugin{},
|
||||
},
|
||||
}
|
||||
basePlugins := map[string][]plugins.Plugin{}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient, basePlugins)
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("missing user plugin", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{
|
||||
Spec: v1alpha2.JenkinsSpec{
|
||||
Master: v1alpha2.JenkinsMaster{
|
||||
Plugins: []v1alpha2.Plugin{{Name: "plugin-name", Version: "0.0.2"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
r := ReconcileJenkinsBaseConfiguration{
|
||||
logger: log.Log,
|
||||
jenkins: jenkins,
|
||||
}
|
||||
pluginsInJenkins := &gojenkins.Plugins{
|
||||
Raw: &gojenkins.PluginResponse{
|
||||
Plugins: []gojenkins.Plugin{},
|
||||
},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jenkinsClient := client.NewMockJenkins(ctrl)
|
||||
jenkinsClient.EXPECT().GetPlugins(fetchAllPlugins).Return(pluginsInJenkins, nil)
|
||||
|
||||
got, err := r.verifyPlugins(jenkinsClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, got)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ def remove = { list ->
|
|||
remove(jenkins.getExtensionList(RootAction.class))
|
||||
remove(jenkins.actions)
|
||||
|
||||
jenkins.getDescriptor("jenkins.CLI").get().setEnabled(false)
|
||||
if (jenkins.getDescriptor("jenkins.CLI") != null) {
|
||||
jenkins.getDescriptor("jenkins.CLI").get().setEnabled(false)
|
||||
}
|
||||
|
||||
jenkins.save()
|
||||
`
|
||||
|
|
|
|||
|
|
@ -255,17 +255,11 @@ chmod +x {{ .JenkinsHomePath }}/scripts/*.sh
|
|||
{{- $installPluginsCommand := .InstallPluginsCommand }}
|
||||
|
||||
echo "Installing plugins required by Operator - begin"
|
||||
{{- range $rootPluginName, $plugins := .OperatorPlugins }}
|
||||
echo "Installing required plugins for '{{ $rootPluginName }}'"
|
||||
{{ $jenkinsHomePath }}/scripts/{{ $installPluginsCommand }} {{ $rootPluginName }} {{ range $index, $plugin := $plugins }}{{ . }} {{ end }}
|
||||
{{- end }}
|
||||
{{ $installPluginsCommand }} {{ range $index, $plugin := .BasePlugins }}{{ $plugin.Name }}:{{ $plugin.Version }} {{ end }}
|
||||
echo "Installing plugins required by Operator - end"
|
||||
|
||||
echo "Installing plugins required by user - begin"
|
||||
{{- range $rootPluginName, $plugins := .UserPlugins }}
|
||||
echo "Installing required plugins for '{{ $rootPluginName }}'"
|
||||
{{ $jenkinsHomePath }}/scripts/{{ $installPluginsCommand }} {{ $rootPluginName }} {{ range $index, $plugin := $plugins }}{{ . }} {{ end }}
|
||||
{{- end }}
|
||||
{{ $installPluginsCommand }} {{ range $index, $plugin := .UserPlugins }}{{ $plugin.Name }}:{{ $plugin.Version }} {{ end }}
|
||||
echo "Installing plugins required by user - end"
|
||||
|
||||
/sbin/tini -s -- /usr/local/bin/jenkins.sh
|
||||
|
|
@ -284,12 +278,12 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) {
|
|||
InitConfigurationPath string
|
||||
InstallPluginsCommand string
|
||||
JenkinsScriptsVolumePath string
|
||||
OperatorPlugins map[string][]string
|
||||
UserPlugins map[string][]string
|
||||
BasePlugins []v1alpha2.Plugin
|
||||
UserPlugins []v1alpha2.Plugin
|
||||
}{
|
||||
JenkinsHomePath: jenkinsHomePath,
|
||||
InitConfigurationPath: jenkinsInitConfigurationVolumePath,
|
||||
OperatorPlugins: jenkins.Spec.Master.OperatorPlugins,
|
||||
BasePlugins: jenkins.Spec.Master.BasePlugins,
|
||||
UserPlugins: jenkins.Spec.Master.Plugins,
|
||||
InstallPluginsCommand: installPluginsCommand,
|
||||
JenkinsScriptsVolumePath: jenkinsScriptsVolumePath,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins)
|
|||
}
|
||||
}
|
||||
|
||||
if !r.validatePlugins(jenkins.Spec.Master.OperatorPlugins, jenkins.Spec.Master.Plugins) {
|
||||
if !r.validatePlugins(plugins.BasePlugins(), jenkins.Spec.Master.BasePlugins, jenkins.Spec.Master.Plugins) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
@ -215,36 +215,60 @@ func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool
|
|||
return valid
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(pluginsWithVersionSlice ...map[string][]string) bool {
|
||||
func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(requiredBasePlugins []plugins.Plugin, basePlugins, userPlugins []v1alpha2.Plugin) bool {
|
||||
valid := true
|
||||
allPlugins := map[plugins.Plugin][]plugins.Plugin{}
|
||||
|
||||
for _, pluginsWithVersions := range pluginsWithVersionSlice {
|
||||
for rootPluginName, dependentPluginNames := range pluginsWithVersions {
|
||||
rootPlugin, err := plugins.New(rootPluginName)
|
||||
if err != nil {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Invalid root plugin name '%s'", rootPluginName))
|
||||
valid = false
|
||||
}
|
||||
for _, jenkinsPlugin := range basePlugins {
|
||||
plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version)
|
||||
if err != nil {
|
||||
r.logger.V(log.VWarn).Info(err.Error())
|
||||
valid = false
|
||||
}
|
||||
|
||||
var dependentPlugins []plugins.Plugin
|
||||
for _, pluginName := range dependentPluginNames {
|
||||
if p, err := plugins.New(pluginName); err != nil {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Invalid dependent plugin name '%s' in root plugin '%s'", pluginName, rootPluginName))
|
||||
valid = false
|
||||
} else {
|
||||
dependentPlugins = append(dependentPlugins, *p)
|
||||
}
|
||||
}
|
||||
|
||||
if rootPlugin != nil {
|
||||
allPlugins[*rootPlugin] = dependentPlugins
|
||||
}
|
||||
if plugin != nil {
|
||||
allPlugins[*plugin] = []plugins.Plugin{}
|
||||
}
|
||||
}
|
||||
|
||||
if valid {
|
||||
return plugins.VerifyDependencies(allPlugins)
|
||||
for _, jenkinsPlugin := range userPlugins {
|
||||
plugin, err := plugins.NewPlugin(jenkinsPlugin.Name, jenkinsPlugin.Version)
|
||||
if err != nil {
|
||||
r.logger.V(log.VWarn).Info(err.Error())
|
||||
valid = false
|
||||
}
|
||||
|
||||
if plugin != nil {
|
||||
allPlugins[*plugin] = []plugins.Plugin{}
|
||||
}
|
||||
}
|
||||
|
||||
if !plugins.VerifyDependencies(allPlugins) {
|
||||
valid = false
|
||||
}
|
||||
|
||||
if !r.verifyBasePlugins(requiredBasePlugins, basePlugins) {
|
||||
valid = false
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) verifyBasePlugins(requiredBasePlugins []plugins.Plugin, basePlugins []v1alpha2.Plugin) bool {
|
||||
valid := true
|
||||
|
||||
for _, requiredBasePlugin := range requiredBasePlugins {
|
||||
found := false
|
||||
for _, basePlugin := range basePlugins {
|
||||
if requiredBasePlugin.Name == basePlugin.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
valid = false
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Missing plugin '%s' in spec.master.basePlugins", requiredBasePlugin.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
|
|
@ -16,62 +18,117 @@ import (
|
|||
)
|
||||
|
||||
func TestValidatePlugins(t *testing.T) {
|
||||
baseReconcileLoop := New(nil, nil, logf.ZapLogger(false),
|
||||
log.SetupLogger(true)
|
||||
baseReconcileLoop := New(nil, nil, log.Log,
|
||||
nil, false, false)
|
||||
t.Run("happy", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"valid-plugin-name:1.0": {
|
||||
"valid-plugin-name:1.0",
|
||||
},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, true, got)
|
||||
})
|
||||
t.Run("fail, no version in plugin name", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"invalid-plugin-name": {
|
||||
"invalid-plugin-name",
|
||||
},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, false, got)
|
||||
})
|
||||
t.Run("fail, no version in root plugin name", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"invalid-plugin-name": {
|
||||
"invalid-plugin-name:1.0",
|
||||
},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, false, got)
|
||||
})
|
||||
t.Run("fail, no version in plugin name", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"invalid-plugin-name:1.0": {
|
||||
"invalid-plugin-name",
|
||||
},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, false, got)
|
||||
})
|
||||
t.Run("happy", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"valid-plugin-name:1.0": {
|
||||
"valid-plugin-name:1.0",
|
||||
"valid-plugin-name2:1.0",
|
||||
},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, true, got)
|
||||
})
|
||||
t.Run("hapy", func(t *testing.T) {
|
||||
plugins := map[string][]string{
|
||||
"valid-plugin-name:1.0": {},
|
||||
}
|
||||
got := baseReconcileLoop.validatePlugins(plugins)
|
||||
assert.Equal(t, true, got)
|
||||
})
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
var basePlugins []v1alpha2.Plugin
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("valid user plugin", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
var basePlugins []v1alpha2.Plugin
|
||||
userPlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("invalid user plugin name", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
var basePlugins []v1alpha2.Plugin
|
||||
userPlugins := []v1alpha2.Plugin{{Name: "INVALID", Version: "0.0.1"}}
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("invalid user plugin version", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
var basePlugins []v1alpha2.Plugin
|
||||
userPlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "invalid"}}
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("valid base plugin", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("invalid base plugin name", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "INVALID", Version: "0.0.1"}}
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("invalid base plugin version", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "invalid"}}
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("valid user and base plugin version", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
userPlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("invalid user and base plugin version", func(t *testing.T) {
|
||||
var requiredBasePlugins []plugins.Plugin
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
userPlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.2"}}
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
t.Run("required base plugin set with the same version", func(t *testing.T) {
|
||||
requiredBasePlugins := []plugins.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("required base plugin set with different version", func(t *testing.T) {
|
||||
requiredBasePlugins := []plugins.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
basePlugins := []v1alpha2.Plugin{{Name: "simple-plugin", Version: "0.0.2"}}
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.True(t, got)
|
||||
})
|
||||
t.Run("missign required base plugin", func(t *testing.T) {
|
||||
requiredBasePlugins := []plugins.Plugin{{Name: "simple-plugin", Version: "0.0.1"}}
|
||||
var basePlugins []v1alpha2.Plugin
|
||||
var userPlugins []v1alpha2.Plugin
|
||||
|
||||
got := baseReconcileLoop.validatePlugins(requiredBasePlugins, basePlugins, userPlugins)
|
||||
|
||||
assert.False(t, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -262,15 +262,15 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
|
|||
FailureThreshold: int32(12),
|
||||
}
|
||||
}
|
||||
if len(jenkins.Spec.Master.OperatorPlugins) == 0 {
|
||||
if len(jenkins.Spec.Master.BasePlugins) == 0 {
|
||||
logger.Info("Setting default operator plugins")
|
||||
changed = true
|
||||
jenkins.Spec.Master.OperatorPlugins = plugins.BasePlugins()
|
||||
jenkins.Spec.Master.BasePlugins = basePlugins()
|
||||
}
|
||||
if len(jenkins.Status.OperatorVersion) > 0 && version.Version != jenkins.Status.OperatorVersion {
|
||||
logger.Info("Setting default operator plugins after Operator version change")
|
||||
changed = true
|
||||
jenkins.Spec.Master.OperatorPlugins = plugins.BasePlugins()
|
||||
jenkins.Spec.Master.BasePlugins = basePlugins()
|
||||
}
|
||||
if len(jenkins.Status.OperatorVersion) == 0 {
|
||||
logger.Info("Setting operator version")
|
||||
|
|
@ -279,7 +279,7 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
|
|||
}
|
||||
if len(jenkins.Spec.Master.Plugins) == 0 {
|
||||
changed = true
|
||||
jenkins.Spec.Master.Plugins = map[string][]string{"simple-theme-plugin:0.5.1": {}}
|
||||
jenkins.Spec.Master.Plugins = []v1alpha2.Plugin{{Name: "simple-theme-plugin", Version: "0.5.1"}}
|
||||
}
|
||||
if isResourceRequirementsNotSet(jenkins.Spec.Master.Resources) {
|
||||
logger.Info("Setting default Jenkins master container resource requirements")
|
||||
|
|
@ -365,3 +365,10 @@ func isResourceRequirementsNotSet(requirements corev1.ResourceRequirements) bool
|
|||
|
||||
return !limitCPUSet || !limitMemorySet || !requestCPUSet || !requestMemporySet
|
||||
}
|
||||
|
||||
func basePlugins() (result []v1alpha2.Plugin) {
|
||||
for _, value := range plugins.BasePlugins() {
|
||||
result = append(result, v1alpha2.Plugin{Name: value.Name, Version: value.Version})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,180 +1,29 @@
|
|||
package plugins
|
||||
|
||||
const (
|
||||
aceEditorPlugin = "ace-editor:1.1"
|
||||
apacheComponentsClientPlugin = "apache-httpcomponents-client-4-api:4.5.5-3.0"
|
||||
authenticationTokensPlugin = "authentication-tokens:1.3"
|
||||
branchAPIPlugin = "branch-api:2.5.2"
|
||||
cloudBeesFolderPlugin = "cloudbees-folder:6.8"
|
||||
configurationAsCodePlugin = "configuration-as-code:1.17"
|
||||
configurationAsCodeSupportPlugin = "configuration-as-code-support:1.17"
|
||||
credentialsBindingPlugin = "credentials-binding:1.18"
|
||||
credentialsPlugin = "credentials:2.1.19"
|
||||
displayURLAPIPlugin = "display-url-api:2.3.1"
|
||||
dockerCommonsPlugin = "docker-commons:1.15"
|
||||
dockerWorkflowPlugin = "docker-workflow:1.18"
|
||||
durableTaskPlugin = "durable-task:1.29"
|
||||
gitClientPlugin = "git-client:2.7.7"
|
||||
configurationAsCodePlugin = "configuration-as-code:1.19"
|
||||
configurationAsCodeSupportPlugin = "configuration-as-code-support:1.19"
|
||||
gitPlugin = "git:3.10.0"
|
||||
gitServerPlugin = "git-server:1.7"
|
||||
handlebarsPlugin = "handlebars:1.1.1"
|
||||
jackson2ADIPlugin = "jackson2-api:2.9.9"
|
||||
jobDslPlugin = "job-dsl:1.74"
|
||||
jqueryDetachedPlugin = "jquery-detached:1.2.1"
|
||||
jschPlugin = "jsch:0.1.55"
|
||||
junitPlugin = "junit:1.28"
|
||||
kubernetesCredentialsPlugin = "kubernetes-credentials:0.4.0"
|
||||
kubernetesCredentialsProviderPlugin = "kubernetes-credentials-provider:0.12.1"
|
||||
kubernetesPlugin = "kubernetes:1.15.5"
|
||||
lockableResourcesPlugin = "lockable-resources:2.5"
|
||||
mailerPlugin = "mailer:1.23"
|
||||
matrixProjectPlugin = "matrix-project:1.14"
|
||||
momentjsPlugin = "momentjs:1.1.1"
|
||||
pipelineBuildStepPlugin = "pipeline-build-step:2.9"
|
||||
pipelineGraphAnalysisPlugin = "pipeline-graph-analysis:1.10"
|
||||
pipelineInputStepPlugin = "pipeline-input-step:2.10"
|
||||
pipelineMilestoneStepPlugin = "pipeline-milestone-step:1.3.1"
|
||||
pipelineModelAPIPlugin = "pipeline-model-api:1.3.8"
|
||||
pipelineModelDeclarativeAgentPlugin = "pipeline-model-declarative-agent:1.1.1"
|
||||
pipelineModelDefinitionPlugin = "pipeline-model-definition:1.3.8"
|
||||
pipelineModelExtensionsPlugin = "pipeline-model-extensions:1.3.8"
|
||||
pipelineRestAPIPlugin = "pipeline-rest-api:2.11"
|
||||
pipelineStageStepPlugin = "pipeline-stage-step:2.3"
|
||||
pipelineStageTagsMetadataPlugin = "pipeline-stage-tags-metadata:1.3.8"
|
||||
pipelineStageViewPlugin = "pipeline-stage-view:2.11"
|
||||
plainCredentialsPlugin = "plain-credentials:1.5"
|
||||
scmAPIPlugin = "scm-api:2.4.1"
|
||||
scriptSecurityPlugin = "script-security:1.59"
|
||||
sshCredentialsPlugin = "ssh-credentials:1.16"
|
||||
structsPlugin = "structs:1.19"
|
||||
variantPlugin = "variant:1.2"
|
||||
kubernetesPlugin = "kubernetes:1.15.7"
|
||||
workflowAggregatorPlugin = "workflow-aggregator:2.6"
|
||||
workflowAPIPlugin = "workflow-api:2.34"
|
||||
workflowBasicStepsPlugin = "workflow-basic-steps:2.16"
|
||||
workflowCpsGlobalLibPlugin = "workflow-cps-global-lib:2.13"
|
||||
workflowCpsPlugin = "workflow-cps:2.69"
|
||||
workflowDurableTaskStepPlugin = "workflow-durable-task-step:2.30"
|
||||
workflowJobPlugin = "workflow-job:2.32"
|
||||
workflowMultibranchPlugin = "workflow-multibranch:2.21"
|
||||
workflowSCMStepPlugin = "workflow-scm-step:2.7"
|
||||
workflowStepAPIPlugin = "workflow-step-api:2.19"
|
||||
workflowSupportPlugin = "workflow-support:3.3"
|
||||
)
|
||||
|
||||
// BasePluginsMap contains plugins to install by operator
|
||||
var BasePluginsMap = map[string][]Plugin{
|
||||
Must(New(kubernetesPlugin)).String(): {
|
||||
Must(New(apacheComponentsClientPlugin)),
|
||||
Must(New(cloudBeesFolderPlugin)),
|
||||
Must(New(credentialsPlugin)),
|
||||
Must(New(durableTaskPlugin)),
|
||||
Must(New(jackson2ADIPlugin)),
|
||||
Must(New(kubernetesCredentialsPlugin)),
|
||||
Must(New(plainCredentialsPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
Must(New(variantPlugin)),
|
||||
Must(New(workflowStepAPIPlugin)),
|
||||
},
|
||||
Must(New(workflowJobPlugin)).String(): {
|
||||
Must(New(scmAPIPlugin)),
|
||||
Must(New(scriptSecurityPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
Must(New(workflowAPIPlugin)),
|
||||
Must(New(workflowStepAPIPlugin)),
|
||||
Must(New(workflowSupportPlugin)),
|
||||
},
|
||||
Must(New(workflowAggregatorPlugin)).String(): {
|
||||
Must(New(aceEditorPlugin)),
|
||||
Must(New(apacheComponentsClientPlugin)),
|
||||
Must(New(authenticationTokensPlugin)),
|
||||
Must(New(branchAPIPlugin)),
|
||||
Must(New(cloudBeesFolderPlugin)),
|
||||
Must(New(credentialsBindingPlugin)),
|
||||
Must(New(credentialsPlugin)),
|
||||
Must(New(displayURLAPIPlugin)),
|
||||
Must(New(dockerCommonsPlugin)),
|
||||
Must(New(dockerWorkflowPlugin)),
|
||||
Must(New(durableTaskPlugin)),
|
||||
Must(New(gitClientPlugin)),
|
||||
Must(New(gitServerPlugin)),
|
||||
Must(New(handlebarsPlugin)),
|
||||
Must(New(jackson2ADIPlugin)),
|
||||
Must(New(jqueryDetachedPlugin)),
|
||||
Must(New(jschPlugin)),
|
||||
Must(New(junitPlugin)),
|
||||
Must(New(lockableResourcesPlugin)),
|
||||
Must(New(mailerPlugin)),
|
||||
Must(New(matrixProjectPlugin)),
|
||||
Must(New(momentjsPlugin)),
|
||||
Must(New(pipelineBuildStepPlugin)),
|
||||
Must(New(pipelineGraphAnalysisPlugin)),
|
||||
Must(New(pipelineInputStepPlugin)),
|
||||
Must(New(pipelineMilestoneStepPlugin)),
|
||||
Must(New(pipelineModelAPIPlugin)),
|
||||
Must(New(pipelineModelDeclarativeAgentPlugin)),
|
||||
Must(New(pipelineModelDefinitionPlugin)),
|
||||
Must(New(pipelineModelExtensionsPlugin)),
|
||||
Must(New(pipelineRestAPIPlugin)),
|
||||
Must(New(pipelineStageStepPlugin)),
|
||||
Must(New(pipelineStageTagsMetadataPlugin)),
|
||||
Must(New(pipelineStageViewPlugin)),
|
||||
Must(New(plainCredentialsPlugin)),
|
||||
Must(New(scmAPIPlugin)),
|
||||
Must(New(scriptSecurityPlugin)),
|
||||
Must(New(sshCredentialsPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
Must(New(workflowAPIPlugin)),
|
||||
Must(New(workflowBasicStepsPlugin)),
|
||||
Must(New(workflowCpsGlobalLibPlugin)),
|
||||
Must(New(workflowCpsPlugin)),
|
||||
Must(New(workflowDurableTaskStepPlugin)),
|
||||
Must(New(workflowJobPlugin)),
|
||||
Must(New(workflowMultibranchPlugin)),
|
||||
Must(New(workflowSCMStepPlugin)),
|
||||
Must(New(workflowStepAPIPlugin)),
|
||||
Must(New(workflowSupportPlugin)),
|
||||
},
|
||||
Must(New(gitPlugin)).String(): {
|
||||
Must(New(apacheComponentsClientPlugin)),
|
||||
Must(New(credentialsPlugin)),
|
||||
Must(New(displayURLAPIPlugin)),
|
||||
Must(New(gitClientPlugin)),
|
||||
Must(New(jschPlugin)),
|
||||
Must(New(junitPlugin)),
|
||||
Must(New(mailerPlugin)),
|
||||
Must(New(matrixProjectPlugin)),
|
||||
Must(New(scmAPIPlugin)),
|
||||
Must(New(scriptSecurityPlugin)),
|
||||
Must(New(sshCredentialsPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
Must(New(workflowAPIPlugin)),
|
||||
Must(New(workflowSCMStepPlugin)),
|
||||
Must(New(workflowStepAPIPlugin)),
|
||||
},
|
||||
Must(New(jobDslPlugin)).String(): {
|
||||
Must(New(scriptSecurityPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
},
|
||||
Must(New(configurationAsCodePlugin)).String(): {
|
||||
Must(New(configurationAsCodeSupportPlugin)),
|
||||
},
|
||||
Must(New(kubernetesCredentialsProviderPlugin)).String(): {
|
||||
Must(New(credentialsPlugin)),
|
||||
Must(New(structsPlugin)),
|
||||
Must(New(variantPlugin)),
|
||||
},
|
||||
// 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(configurationAsCodeSupportPlugin)),
|
||||
Must(New(kubernetesCredentialsProviderPlugin)),
|
||||
}
|
||||
|
||||
// BasePlugins returns map of plugins to install by operator
|
||||
func BasePlugins() (plugins map[string][]string) {
|
||||
plugins = map[string][]string{}
|
||||
|
||||
for rootPluginName, dependentPlugins := range BasePluginsMap {
|
||||
plugins[rootPluginName] = []string{}
|
||||
for _, pluginName := range dependentPlugins {
|
||||
plugins[rootPluginName] = append(plugins[rootPluginName], pluginName.String())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
// BasePlugins returns list of plugins to install by operator
|
||||
func BasePlugins() []Plugin {
|
||||
return basePluginsList
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package plugins
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
|
@ -20,18 +21,52 @@ func (p Plugin) String() string {
|
|||
return fmt.Sprintf("%s:%s", p.Name, p.Version)
|
||||
}
|
||||
|
||||
var (
|
||||
namePattern = regexp.MustCompile(`^[0-9a-z-]+$`)
|
||||
versionPattern = regexp.MustCompile(`^[0-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: val[0],
|
||||
Version: val[1],
|
||||
Name: name,
|
||||
Version: version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Plugin{
|
||||
Name: name,
|
||||
Version: version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validatePlugin(name, version 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())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must returns plugin from pointer and throws panic when error is set
|
||||
func Must(plugin *Plugin, err error) Plugin {
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -228,25 +228,16 @@ func verifyPlugins(t *testing.T, jenkinsClient jenkinsclient.Jenkins, jenkins *v
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
requiredPlugins := []map[string][]string{plugins.BasePlugins(), jenkins.Spec.Master.Plugins}
|
||||
for _, p := range requiredPlugins {
|
||||
for rootPluginName, dependentPlugins := range p {
|
||||
rootPlugin, err := plugins.New(rootPluginName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if found, ok := isPluginValid(installedPlugins, *rootPlugin); !ok {
|
||||
t.Fatalf("Invalid plugin '%s', actual '%+v'", rootPlugin, found)
|
||||
}
|
||||
for _, pluginName := range dependentPlugins {
|
||||
plugin, err := plugins.New(pluginName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if found, ok := isPluginValid(installedPlugins, *plugin); !ok {
|
||||
t.Fatalf("Invalid plugin '%s', actual '%+v'", rootPlugin, found)
|
||||
}
|
||||
}
|
||||
for _, basePlugin := range plugins.BasePlugins() {
|
||||
if found, ok := isPluginValid(installedPlugins, basePlugin); !ok {
|
||||
t.Fatalf("Invalid plugin '%s', actual '%+v'", basePlugin, found)
|
||||
}
|
||||
}
|
||||
|
||||
for _, userPlugin := range jenkins.Spec.Master.Plugins {
|
||||
plugin := plugins.Plugin{Name: userPlugin.Name, Version: userPlugin.Version}
|
||||
if found, ok := isPluginValid(installedPlugins, plugin); !ok {
|
||||
t.Fatalf("Invalid plugin '%s', actual '%+v'", plugin, found)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,9 +113,9 @@ func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha2.S
|
|||
Image: "envoyproxy/envoy-alpine",
|
||||
},
|
||||
},
|
||||
Plugins: map[string][]string{
|
||||
"audit-trail:2.4": {},
|
||||
"simple-theme-plugin:0.5.1": {},
|
||||
Plugins: []v1alpha2.Plugin{
|
||||
{Name: "audit-trail:", Version: "2.4"},
|
||||
{Name: "simple-theme-plugin", Version: "0.5.1"},
|
||||
},
|
||||
NodeSelector: map[string]string{"kubernetes.io/hostname": "minikube"},
|
||||
Volumes: volumes,
|
||||
|
|
|
|||
Loading…
Reference in New Issue