diff --git a/go.mod b/go.mod index 284d0d01f..03ab0bac2 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( go.uber.org/zap v1.14.1 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20200226121028-0de0cce0169b - golang.org/x/tools v0.0.0-20200504022951-6b6965ac5dd1 // indirect + golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df k8s.io/api v0.17.4 diff --git a/go.sum b/go.sum index 02229dba0..68b01f7e1 100644 --- a/go.sum +++ b/go.sum @@ -958,6 +958,8 @@ golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200504022951-6b6965ac5dd1 h1:C8rdnd6KieI73Z2Av0sS0t4kW+geIH/M8kNX8Hmvn9E= golang.org/x/tools v0.0.0-20200504022951-6b6965ac5dd1/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 h1:BMFHd4OFnFtWX46Xj4DN6vvT1btiBxyq+s0orYBqcQY= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/controller/jenkins/configuration/base/pod.go b/pkg/controller/jenkins/configuration/base/pod.go index 1476d308e..2be5ab2cd 100644 --- a/pkg/controller/jenkins/configuration/base/pod.go +++ b/pkg/controller/jenkins/configuration/base/pod.go @@ -147,7 +147,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O } // Check if this Pod already exists - currentJenkinsMasterPod, err := r.getJenkinsMasterPod() + currentJenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod() if err != nil && apierrors.IsNotFound(err) { jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.Configuration.Jenkins) *r.Notifications <- event.Event{ diff --git a/pkg/controller/jenkins/configuration/base/reconcile.go b/pkg/controller/jenkins/configuration/base/reconcile.go index c59f812fb..13f8335b9 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -83,7 +83,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki } r.logger.V(log.VDebug).Info("Jenkins master pod is ready") - jenkinsClient, err := r.ensureJenkinsClient() + jenkinsClient, err := r.Configuration.GetJenkinsClient() if err != nil { return reconcile.Result{}, nil, err } @@ -110,32 +110,6 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki return result, jenkinsClient, err } -// GetJenkinsOpts gets JENKINS_OPTS env parameter, parses it's values and returns it as a map` -func GetJenkinsOpts(jenkins v1alpha2.Jenkins) map[string]string { - envs := jenkins.Spec.Master.Containers[0].Env - jenkinsOpts := make(map[string]string) - - for key, value := range envs { - if value.Name == "JENKINS_OPTS" { - jenkinsOptsEnv := envs[key] - jenkinsOptsWithDashes := jenkinsOptsEnv.Value - if len(jenkinsOptsWithDashes) == 0 { - return nil - } - - jenkinsOptsWithEqOperators := strings.Split(jenkinsOptsWithDashes, " ") - - for _, vx := range jenkinsOptsWithEqOperators { - opt := strings.Split(vx, "=") - jenkinsOpts[strings.ReplaceAll(opt[0], "--", "")] = opt[1] - } - - return jenkinsOpts - } - } - return nil -} - func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod(metaObject metav1.ObjectMeta) error { if err := r.createOperatorCredentialsSecret(metaObject); err != nil { return err @@ -216,16 +190,6 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta return stackerr.WithStack(r.UpdateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins))) } -func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod() (*corev1.Pod, error) { - jenkinsMasterPodName := resources.GetJenkinsMasterPodName(*r.Configuration.Jenkins) - currentJenkinsMasterPod := &corev1.Pod{} - err := r.Client.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPodName, Namespace: r.Configuration.Jenkins.Namespace}, currentJenkinsMasterPod) - if err != nil { - return nil, err // don't wrap error - } - return currentJenkinsMasterPod, nil -} - func (r *ReconcileJenkinsBaseConfiguration) calculateUserAndPasswordHash() (string, error) { credentialsSecret := &corev1.Secret{} err := r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, credentialsSecret) @@ -309,7 +273,7 @@ func (r *ReconcileJenkinsBaseConfiguration) compareVolumes(actualPod corev1.Pod) } func (r *ReconcileJenkinsBaseConfiguration) detectJenkinsMasterPodStartingIssues() (stopReconcileLoop bool, err error) { - jenkinsMasterPod, err := r.getJenkinsMasterPod() + jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod() if err != nil { return false, err } @@ -360,7 +324,7 @@ func (r *ReconcileJenkinsBaseConfiguration) filterEvents(source corev1.EventList } func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins() (reconcile.Result, error) { - jenkinsMasterPod, err := r.getJenkinsMasterPod() + jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod() if err != nil { return reconcile.Result{}, err } @@ -400,106 +364,6 @@ func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins() (reconcile.Result, return reconcile.Result{}, nil } -func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient.Jenkins, error) { - switch r.Configuration.Jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy { - case v1alpha2.ServiceAccountAuthorizationStrategy: - return r.ensureJenkinsClientFromServiceAccount() - case v1alpha2.CreateUserAuthorizationStrategy: - return r.ensureJenkinsClientFromSecret() - default: - return nil, stackerr.Errorf("unrecognized '%s' spec.jenkinsAPISettings.authorizationStrategy", r.Configuration.Jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy) - } -} - -func (r *ReconcileJenkinsBaseConfiguration) getJenkinsAPIUrl() (string, error) { - var service corev1.Service - - err := r.Client.Get(context.TODO(), types.NamespacedName{ - Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace, - Name: resources.GetJenkinsHTTPServiceName(r.Configuration.Jenkins), - }, &service) - - if err != nil { - return "", err - } - jenkinsURL := r.jenkinsAPIConnectionSettings.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort) - if prefix, ok := GetJenkinsOpts(*r.Configuration.Jenkins)["prefix"]; ok { - jenkinsURL = jenkinsURL + prefix - } - return jenkinsURL, nil -} - -func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClientFromServiceAccount() (jenkinsclient.Jenkins, error) { - jenkinsAPIUrl, err := r.getJenkinsAPIUrl() - if err != nil { - return nil, err - } - - podName := resources.GetJenkinsMasterPodName(*r.Configuration.Jenkins) - token, _, err := r.Configuration.Exec(podName, resources.JenkinsMasterContainerName, []string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"}) - if err != nil { - return nil, err - } - - return jenkinsclient.NewBearerTokenAuthorization(jenkinsAPIUrl, token.String()) -} - -func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClientFromSecret() (jenkinsclient.Jenkins, error) { - jenkinsURL, err := r.getJenkinsAPIUrl() - if err != nil { - return nil, err - } - r.logger.V(log.VDebug).Info(fmt.Sprintf("Jenkins API URL '%s'", jenkinsURL)) - credentialsSecret := &corev1.Secret{} - err = r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, credentialsSecret) - if err != nil { - return nil, stackerr.WithStack(err) - } - currentJenkinsMasterPod, err := r.getJenkinsMasterPod() - if err != nil { - return nil, err - } - var tokenCreationTime *time.Time - tokenCreationTimeBytes := credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] - if tokenCreationTimeBytes != nil { - tokenCreationTime = &time.Time{} - err = tokenCreationTime.UnmarshalText(tokenCreationTimeBytes) - if err != nil { - tokenCreationTime = nil - } - } - if credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey] == nil || - tokenCreationTimeBytes == nil || tokenCreationTime == nil || - currentJenkinsMasterPod.ObjectMeta.CreationTimestamp.Time.UTC().After(tokenCreationTime.UTC()) { - r.logger.Info("Generating Jenkins API token for operator") - userName := string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]) - jenkinsClient, err := jenkinsclient.NewUserAndPasswordAuthorization( - jenkinsURL, - userName, - string(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey])) - if err != nil { - return nil, err - } - - token, err := jenkinsClient.GenerateToken(userName, "token") - if err != nil { - return nil, err - } - - credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey] = []byte(token.GetToken()) - now, _ := time.Now().UTC().MarshalText() - credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] = now - err = r.UpdateResource(credentialsSecret) - if err != nil { - return nil, stackerr.WithStack(err) - } - } - return jenkinsclient.NewUserAndPasswordAuthorization( - jenkinsURL, - string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]), - string(credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey])) -} - func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClient jenkinsclient.Jenkins) (reconcile.Result, error) { customization := v1alpha2.GroovyScripts{ Customization: v1alpha2.Customization{ @@ -517,14 +381,14 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClien } func (r *ReconcileJenkinsBaseConfiguration) waitUntilCreateJenkinsMasterPod() (currentJenkinsMasterPod *corev1.Pod, err error) { - currentJenkinsMasterPod, err = r.getJenkinsMasterPod() + currentJenkinsMasterPod, err = r.Configuration.GetJenkinsMasterPod() for { if err != nil && !apierrors.IsNotFound(err) { return nil, stackerr.WithStack(err) } else if err == nil { break } - currentJenkinsMasterPod, err = r.getJenkinsMasterPod() + currentJenkinsMasterPod, err = r.Configuration.GetJenkinsMasterPod() time.Sleep(time.Millisecond * 10) } return diff --git a/pkg/controller/jenkins/configuration/base/reconcile_test.go b/pkg/controller/jenkins/configuration/base/reconcile_test.go index 0d03a1516..08955e3af 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile_test.go +++ b/pkg/controller/jenkins/configuration/base/reconcile_test.go @@ -9,7 +9,6 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources" "github.com/jenkinsci/kubernetes-operator/pkg/log" - "github.com/bndr/gojenkins" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -22,140 +21,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -func TestGetJenkinsOpts(t *testing.T) { - t.Run("JENKINS_OPTS is uninitialized", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "", Value: ""}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - assert.Equal(t, 0, len(opts)) - }) - - t.Run("JENKINS_OPTS is empty", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "JENKINS_OPTS", Value: ""}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - assert.Equal(t, 0, len(opts)) - }) - - t.Run("JENKINS_OPTS have --prefix argument ", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "JENKINS_OPTS", Value: "--prefix=/jenkins"}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - - assert.Equal(t, 1, len(opts)) - assert.NotContains(t, opts, "httpPort") - assert.Contains(t, opts, "prefix") - assert.Equal(t, opts["prefix"], "/jenkins") - }) - - t.Run("JENKINS_OPTS have --prefix and --httpPort argument", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "JENKINS_OPTS", Value: "--prefix=/jenkins --httpPort=8080"}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - - assert.Equal(t, 2, len(opts)) - - assert.Contains(t, opts, "prefix") - assert.Equal(t, opts["prefix"], "/jenkins") - - assert.Contains(t, opts, "httpPort") - assert.Equal(t, opts["httpPort"], "8080") - }) - - t.Run("JENKINS_OPTS have --httpPort argument", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "JENKINS_OPTS", Value: "--httpPort=8080"}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - - assert.Equal(t, 1, len(opts)) - assert.NotContains(t, opts, "prefix") - assert.Contains(t, opts, "httpPort") - assert.Equal(t, opts["httpPort"], "8080") - }) - - t.Run("JENKINS_OPTS have --httpPort=--8080 argument", func(t *testing.T) { - jenkins := v1alpha2.Jenkins{ - Spec: v1alpha2.JenkinsSpec{ - Master: v1alpha2.JenkinsMaster{ - Containers: []v1alpha2.Container{ - { - Env: []corev1.EnvVar{ - {Name: "JENKINS_OPTS", Value: "--httpPort=--8080"}, - }, - }, - }, - }, - }, - } - - opts := GetJenkinsOpts(jenkins) - - assert.Equal(t, 1, len(opts)) - assert.NotContains(t, opts, "prefix") - assert.Contains(t, opts, "httpPort") - assert.Equal(t, opts["httpPort"], "--8080") - }) -} func TestCompareContainerVolumeMounts(t *testing.T) { t.Run("happy with service account", func(t *testing.T) { diff --git a/pkg/controller/jenkins/configuration/configuration.go b/pkg/controller/jenkins/configuration/configuration.go index f5413bcac..1cd7cc65f 100644 --- a/pkg/controller/jenkins/configuration/configuration.go +++ b/pkg/controller/jenkins/configuration/configuration.go @@ -3,12 +3,14 @@ package configuration import ( "bytes" "context" + "strings" + "time" "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/notifications/event" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/notifications/reason" - + jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" stackerr "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -25,17 +27,18 @@ import ( // Configuration holds required for Jenkins configuration type Configuration struct { - Client client.Client - ClientSet kubernetes.Clientset - Notifications *chan event.Event - Jenkins *v1alpha2.Jenkins - Scheme *runtime.Scheme - Config *rest.Config + Client client.Client + ClientSet kubernetes.Clientset + Notifications *chan event.Event + Jenkins *v1alpha2.Jenkins + Scheme *runtime.Scheme + Config *rest.Config + JenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings } // RestartJenkinsMasterPod terminate Jenkins master pod and notifies about it func (c *Configuration) RestartJenkinsMasterPod(reason reason.Reason) error { - currentJenkinsMasterPod, err := c.getJenkinsMasterPod() + currentJenkinsMasterPod, err := c.GetJenkinsMasterPod() if err != nil { return err } @@ -54,7 +57,8 @@ func (c *Configuration) RestartJenkinsMasterPod(reason reason.Reason) error { return stackerr.WithStack(c.Client.Delete(context.TODO(), currentJenkinsMasterPod)) } -func (c *Configuration) getJenkinsMasterPod() (*corev1.Pod, error) { +// GetJenkinsMasterPod gets the jenkins master pod +func (c *Configuration) GetJenkinsMasterPod() (*corev1.Pod, error) { jenkinsMasterPodName := resources.GetJenkinsMasterPodName(*c.Jenkins) currentJenkinsMasterPod := &corev1.Pod{} err := c.Client.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPodName, Namespace: c.Jenkins.Namespace}, currentJenkinsMasterPod) @@ -159,3 +163,130 @@ func (c *Configuration) GetJenkinsMasterContainer() *v1alpha2.Container { } return nil } + +// GetJenkinsClient gets jenkins client from a configuration +func (c *Configuration) GetJenkinsClient() (jenkinsclient.Jenkins, error) { + switch c.Jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy { + case v1alpha2.ServiceAccountAuthorizationStrategy: + return c.GetJenkinsClientFromServiceAccount() + case v1alpha2.CreateUserAuthorizationStrategy: + return c.GetJenkinsClientFromSecret() + default: + return nil, stackerr.Errorf("unrecognized '%s' spec.jenkinsAPISettings.authorizationStrategy", c.Jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy) + } +} + +func (c *Configuration) getJenkinsAPIUrl() (string, error) { + var service corev1.Service + + err := c.Client.Get(context.TODO(), types.NamespacedName{ + Namespace: c.Jenkins.ObjectMeta.Namespace, + Name: resources.GetJenkinsHTTPServiceName(c.Jenkins), + }, &service) + + if err != nil { + return "", err + } + jenkinsURL := c.JenkinsAPIConnectionSettings.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort) + if prefix, ok := GetJenkinsOpts(*c.Jenkins)["prefix"]; ok { + jenkinsURL = jenkinsURL + prefix + } + return jenkinsURL, nil +} + +// GetJenkinsClientFromServiceAccount gets jenkins client from a serviceAccount +func (c *Configuration) GetJenkinsClientFromServiceAccount() (jenkinsclient.Jenkins, error) { + jenkinsAPIUrl, err := c.getJenkinsAPIUrl() + if err != nil { + return nil, err + } + + podName := resources.GetJenkinsMasterPodName(*c.Jenkins) + token, _, err := c.Exec(podName, resources.JenkinsMasterContainerName, []string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"}) + if err != nil { + return nil, err + } + + return jenkinsclient.NewBearerTokenAuthorization(jenkinsAPIUrl, token.String()) +} + +// GetJenkinsClientFromSecret gets jenkins client from a secret +func (c *Configuration) GetJenkinsClientFromSecret() (jenkinsclient.Jenkins, error) { + jenkinsURL, err := c.getJenkinsAPIUrl() + if err != nil { + return nil, err + } + credentialsSecret := &corev1.Secret{} + err = c.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(c.Jenkins), Namespace: c.Jenkins.ObjectMeta.Namespace}, credentialsSecret) + if err != nil { + return nil, stackerr.WithStack(err) + } + currentJenkinsMasterPod, err := c.GetJenkinsMasterPod() + if err != nil { + return nil, err + } + var tokenCreationTime *time.Time + tokenCreationTimeBytes := credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] + if tokenCreationTimeBytes != nil { + tokenCreationTime = &time.Time{} + err = tokenCreationTime.UnmarshalText(tokenCreationTimeBytes) + if err != nil { + tokenCreationTime = nil + } + } + if credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey] == nil || + tokenCreationTimeBytes == nil || tokenCreationTime == nil || + currentJenkinsMasterPod.ObjectMeta.CreationTimestamp.Time.UTC().After(tokenCreationTime.UTC()) { + userName := string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]) + jenkinsClient, err := jenkinsclient.NewUserAndPasswordAuthorization( + jenkinsURL, + userName, + string(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey])) + if err != nil { + return nil, err + } + + token, err := jenkinsClient.GenerateToken(userName, "token") + if err != nil { + return nil, err + } + + credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey] = []byte(token.GetToken()) + now, _ := time.Now().UTC().MarshalText() + credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] = now + err = c.UpdateResource(credentialsSecret) + if err != nil { + return nil, stackerr.WithStack(err) + } + } + return jenkinsclient.NewUserAndPasswordAuthorization( + jenkinsURL, + string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]), + string(credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey])) +} + +// GetJenkinsOpts gets JENKINS_OPTS env parameter, parses it's values and returns it as a map` +func GetJenkinsOpts(jenkins v1alpha2.Jenkins) map[string]string { + envs := jenkins.Spec.Master.Containers[0].Env + jenkinsOpts := make(map[string]string) + + for key, value := range envs { + if value.Name == "JENKINS_OPTS" { + jenkinsOptsEnv := envs[key] + jenkinsOptsWithDashes := jenkinsOptsEnv.Value + if len(jenkinsOptsWithDashes) == 0 { + return nil + } + + jenkinsOptsWithEqOperators := strings.Split(jenkinsOptsWithDashes, " ") + + for _, vx := range jenkinsOptsWithEqOperators { + opt := strings.Split(vx, "=") + jenkinsOpts[strings.ReplaceAll(opt[0], "--", "")] = opt[1] + } + + return jenkinsOpts + } + } + return nil +} \ No newline at end of file diff --git a/pkg/controller/jenkins/configuration/configuration_test.go b/pkg/controller/jenkins/configuration/configuration_test.go new file mode 100644 index 000000000..f8ab50738 --- /dev/null +++ b/pkg/controller/jenkins/configuration/configuration_test.go @@ -0,0 +1,144 @@ +package configuration + +import ( + "testing" + + "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" +) + +func TestGetJenkinsOpts(t *testing.T) { + t.Run("JENKINS_OPTS is uninitialized", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "", Value: ""}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + assert.Equal(t, 0, len(opts)) + }) + + t.Run("JENKINS_OPTS is empty", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "JENKINS_OPTS", Value: ""}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + assert.Equal(t, 0, len(opts)) + }) + + t.Run("JENKINS_OPTS have --prefix argument ", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "JENKINS_OPTS", Value: "--prefix=/jenkins"}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + + assert.Equal(t, 1, len(opts)) + assert.NotContains(t, opts, "httpPort") + assert.Contains(t, opts, "prefix") + assert.Equal(t, opts["prefix"], "/jenkins") + }) + + t.Run("JENKINS_OPTS have --prefix and --httpPort argument", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "JENKINS_OPTS", Value: "--prefix=/jenkins --httpPort=8080"}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + + assert.Equal(t, 2, len(opts)) + + assert.Contains(t, opts, "prefix") + assert.Equal(t, opts["prefix"], "/jenkins") + + assert.Contains(t, opts, "httpPort") + assert.Equal(t, opts["httpPort"], "8080") + }) + + t.Run("JENKINS_OPTS have --httpPort argument", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "JENKINS_OPTS", Value: "--httpPort=8080"}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + + assert.Equal(t, 1, len(opts)) + assert.NotContains(t, opts, "prefix") + assert.Contains(t, opts, "httpPort") + assert.Equal(t, opts["httpPort"], "8080") + }) + + t.Run("JENKINS_OPTS have --httpPort=--8080 argument", func(t *testing.T) { + jenkins := v1alpha2.Jenkins{ + Spec: v1alpha2.JenkinsSpec{ + Master: v1alpha2.JenkinsMaster{ + Containers: []v1alpha2.Container{ + { + Env: []corev1.EnvVar{ + {Name: "JENKINS_OPTS", Value: "--httpPort=--8080"}, + }, + }, + }, + }, + }, + } + + opts := GetJenkinsOpts(jenkins) + + assert.Equal(t, 1, len(opts)) + assert.NotContains(t, opts, "prefix") + assert.Contains(t, opts, "httpPort") + assert.Equal(t, opts["httpPort"], "--8080") + }) +} diff --git a/pkg/controller/jenkins/configuration/user/seedjobs/seedjobs.go b/pkg/controller/jenkins/configuration/user/seedjobs/seedjobs.go index 80a874374..33b0bb9bd 100644 --- a/pkg/controller/jenkins/configuration/user/seedjobs/seedjobs.go +++ b/pkg/controller/jenkins/configuration/user/seedjobs/seedjobs.go @@ -381,7 +381,7 @@ func agentDeployment(jenkins *v1alpha2.Jenkins, namespace string, agentName stri if err != nil { return nil, err } - jenkinsHttpServiceFQDN, err := resources.GetJenkinsHTTPServiceFQDN(jenkins) + jenkinsHTTPServiceFQDN, err := resources.GetJenkinsHTTPServiceFQDN(jenkins) if err != nil { return nil, err } @@ -428,7 +428,7 @@ func agentDeployment(jenkins *v1alpha2.Jenkins, namespace string, agentName stri { Name: "JENKINS_URL", Value: fmt.Sprintf("http://%s:%d", - jenkinsHttpServiceFQDN, + jenkinsHTTPServiceFQDN, jenkins.Spec.Service.Port, ), }, diff --git a/pkg/controller/jenkins/jenkins_controller.go b/pkg/controller/jenkins/jenkins_controller.go index 3c5ce3155..528aa8bcd 100644 --- a/pkg/controller/jenkins/jenkins_controller.go +++ b/pkg/controller/jenkins/jenkins_controller.go @@ -227,12 +227,13 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg } config := configuration.Configuration{ - Client: r.client, - ClientSet: r.clientSet, - Notifications: r.notificationEvents, - Jenkins: jenkins, - Scheme: r.scheme, - Config: &r.config, + Client: r.client, + ClientSet: r.clientSet, + Notifications: r.notificationEvents, + Jenkins: jenkins, + Scheme: r.scheme, + Config: &r.config, + JenkinsAPIConnectionSettings: r.jenkinsAPIConnectionSettings, } // Reconcile base configuration