#190 Allow set Jenkins API authorization strategy

This commit is contained in:
Tomasz Sęk 2020-01-11 22:04:00 +01:00
parent 6de21202e8
commit 8e1d66de0f
No known key found for this signature in database
GPG Key ID: DC356D23F6A644D0
13 changed files with 224 additions and 124 deletions

View File

@ -61,6 +61,24 @@ type JenkinsSpec struct {
// ServiceAccount defines Jenkins master service account attributes // ServiceAccount defines Jenkins master service account attributes
// +optional // +optional
ServiceAccount ServiceAccount `json:"serviceAccount,omitempty"` ServiceAccount ServiceAccount `json:"serviceAccount,omitempty"`
// JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
JenkinsAPISettings JenkinsAPISettings `json:"jenkinsAPISettings"`
}
// AuthorizationStrategy defines authorization strategy of the operator for the Jenkins API
type AuthorizationStrategy string
const (
// CreateUserAuthorizationStrategy operator sets HudsonPrivateSecurityRealm and FullControlOnceLoggedInAuthorizationStrategy than creates user using init.d groovy script
CreateUserAuthorizationStrategy AuthorizationStrategy = "createUser"
// ServiceAccountAuthorizationStrategy operator gets token associated with Jenkins service account and uses it as bearer token
ServiceAccountAuthorizationStrategy AuthorizationStrategy = "serviceAccount"
)
// JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
type JenkinsAPISettings struct {
AuthorizationStrategy AuthorizationStrategy `json:"authorizationStrategy"`
} }
// ServiceAccount defines Kubernetes service account attributes // ServiceAccount defines Kubernetes service account attributes

View File

@ -68,6 +68,23 @@ type JenkinsAPIConnectionSettings struct {
UseNodePort bool UseNodePort bool
} }
type setBearerToken struct {
rt http.RoundTripper
token string
}
func (t *setBearerToken) transport() http.RoundTripper {
if t.rt != nil {
return t.rt
}
return http.DefaultTransport
}
func (t *setBearerToken) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", t.token))
return t.transport().RoundTrip(r)
}
// CreateOrUpdateJob creates or updates a job from config // CreateOrUpdateJob creates or updates a job from config
func (jenkins *jenkins) CreateOrUpdateJob(config, jobName string) (job *gojenkins.Job, created bool, err error) { func (jenkins *jenkins) CreateOrUpdateJob(config, jobName string) (job *gojenkins.Job, created bool, err error) {
// create or update // create or update
@ -114,19 +131,37 @@ func (j JenkinsAPIConnectionSettings) Validate() error {
return nil return nil
} }
// New creates Jenkins API client // NewUserAndPasswordAuthorization creates Jenkins API client with user and password authorization
func New(url, user, passwordOrToken string) (Jenkins, error) { func NewUserAndPasswordAuthorization(url, userName, passwordOrToken string) (Jenkins, error) {
return newClient(url, userName, passwordOrToken)
}
// NewBearerTokenAuthorization creates Jenkins API client with bearer token authorization
func NewBearerTokenAuthorization(url, token string) (Jenkins, error) {
return newClient(url, "", token)
}
func newClient(url, userName, passwordOrToken string) (Jenkins, error) {
if strings.HasSuffix(url, "/") { if strings.HasSuffix(url, "/") {
url = url[:len(url)-1] url = url[:len(url)-1]
} }
jenkinsClient := &jenkins{} jenkinsClient := &jenkins{}
jenkinsClient.Server = url jenkinsClient.Server = url
var basicAuth *gojenkins.BasicAuth
httpClient := http.DefaultClient
if len(userName) > 0 && len(passwordOrToken) > 0 {
basicAuth = &gojenkins.BasicAuth{Username: userName, Password: passwordOrToken}
} else {
httpClient.Transport = &setBearerToken{token: passwordOrToken, rt: httpClient.Transport}
}
jenkinsClient.Requester = &gojenkins.Requester{ jenkinsClient.Requester = &gojenkins.Requester{
Base: url, Base: url,
SslVerify: true, SslVerify: true,
Client: http.DefaultClient, Client: httpClient,
BasicAuth: &gojenkins.BasicAuth{Username: user, Password: passwordOrToken}, BasicAuth: basicAuth,
} }
if _, err := jenkinsClient.Init(); err != nil { if _, err := jenkinsClient.Init(); err != nil {
return nil, errors.Wrap(err, "couldn't init Jenkins API client") return nil, errors.Wrap(err, "couldn't init Jenkins API client")

View File

@ -1,25 +1,19 @@
package backuprestore package backuprestore
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"time" "time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
"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/controller/jenkins/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
k8s "sigs.k8s.io/controller-runtime/pkg/client" k8s "sigs.k8s.io/controller-runtime/pkg/client"
) )
@ -61,29 +55,27 @@ var triggers = backupTriggers{triggers: make(map[string]backupTrigger)}
// BackupAndRestore represents Jenkins backup and restore client // BackupAndRestore represents Jenkins backup and restore client
type BackupAndRestore struct { type BackupAndRestore struct {
config rest.Config configuration.Configuration
k8sClient k8s.Client logger logr.Logger
clientSet kubernetes.Clientset
logger logr.Logger
jenkins *v1alpha2.Jenkins
} }
// New returns Jenkins backup and restore client // New returns Jenkins backup and restore client
func New(k8sClient k8s.Client, clientSet kubernetes.Clientset, func New(configuration configuration.Configuration, logger logr.Logger) *BackupAndRestore {
logger logr.Logger, jenkins *v1alpha2.Jenkins, config rest.Config) *BackupAndRestore { return &BackupAndRestore{
return &BackupAndRestore{k8sClient: k8sClient, clientSet: clientSet, logger: logger, jenkins: jenkins, config: config} Configuration: configuration,
logger: logger,
}
} }
// Validate validates backup and restore configuration // Validate validates backup and restore configuration
func (bar *BackupAndRestore) Validate() []string { func (bar *BackupAndRestore) Validate() []string {
var messages []string var messages []string
allContainers := map[string]v1alpha2.Container{} allContainers := map[string]v1alpha2.Container{}
for _, container := range bar.jenkins.Spec.Master.Containers { for _, container := range bar.Configuration.Jenkins.Spec.Master.Containers {
allContainers[container.Name] = container allContainers[container.Name] = container
} }
restore := bar.jenkins.Spec.Restore restore := bar.Configuration.Jenkins.Spec.Restore
if len(restore.ContainerName) > 0 { if len(restore.ContainerName) > 0 {
_, found := allContainers[restore.ContainerName] _, found := allContainers[restore.ContainerName]
if !found { if !found {
@ -94,7 +86,7 @@ func (bar *BackupAndRestore) Validate() []string {
} }
} }
backup := bar.jenkins.Spec.Backup backup := bar.Configuration.Jenkins.Spec.Backup
if len(backup.ContainerName) > 0 { if len(backup.ContainerName) > 0 {
_, found := allContainers[backup.ContainerName] _, found := allContainers[backup.ContainerName]
if !found { if !found {
@ -120,7 +112,7 @@ func (bar *BackupAndRestore) Validate() []string {
// Restore performs Jenkins restore backup operation // Restore performs Jenkins restore backup operation
func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error { func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error {
jenkins := bar.jenkins jenkins := bar.Configuration.Jenkins
if len(jenkins.Spec.Restore.ContainerName) == 0 || jenkins.Spec.Restore.Action.Exec == nil { if len(jenkins.Spec.Restore.ContainerName) == 0 || jenkins.Spec.Restore.Action.Exec == nil {
bar.logger.V(log.VDebug).Info("Skipping restore backup, backup restore not configured") bar.logger.V(log.VDebug).Info("Skipping restore backup, backup restore not configured")
return nil return nil
@ -133,7 +125,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
bar.logger.V(log.VDebug).Info("Skipping restore backup") bar.logger.V(log.VDebug).Info("Skipping restore backup")
if jenkins.Status.PendingBackup == 0 { if jenkins.Status.PendingBackup == 0 {
jenkins.Status.PendingBackup = 1 jenkins.Status.PendingBackup = 1
return bar.k8sClient.Update(context.TODO(), jenkins) return bar.Client.Update(context.TODO(), jenkins)
} }
return nil return nil
} }
@ -148,7 +140,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
podName := resources.GetJenkinsMasterPodName(*jenkins) podName := resources.GetJenkinsMasterPodName(*jenkins)
command := jenkins.Spec.Restore.Action.Exec.Command command := jenkins.Spec.Restore.Action.Exec.Command
command = append(command, fmt.Sprintf("%d", backupNumber)) command = append(command, fmt.Sprintf("%d", backupNumber))
_, _, err := bar.exec(podName, jenkins.Spec.Restore.ContainerName, command) _, _, err := bar.Exec(podName, jenkins.Spec.Restore.ContainerName, command)
if err == nil { if err == nil {
_, err := jenkinsClient.ExecuteScript("Jenkins.instance.reload()") _, err := jenkinsClient.ExecuteScript("Jenkins.instance.reload()")
@ -159,7 +151,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
jenkins.Spec.Restore.RecoveryOnce = 0 jenkins.Spec.Restore.RecoveryOnce = 0
jenkins.Status.RestoredBackup = backupNumber jenkins.Status.RestoredBackup = backupNumber
jenkins.Status.PendingBackup = backupNumber + 1 jenkins.Status.PendingBackup = backupNumber + 1
return bar.k8sClient.Update(context.TODO(), jenkins) return bar.Client.Update(context.TODO(), jenkins)
} }
return err return err
@ -167,7 +159,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
// Backup performs Jenkins backup operation // Backup performs Jenkins backup operation
func (bar *BackupAndRestore) Backup() error { func (bar *BackupAndRestore) Backup() error {
jenkins := bar.jenkins jenkins := bar.Configuration.Jenkins
if len(jenkins.Spec.Backup.ContainerName) == 0 || jenkins.Spec.Backup.Action.Exec == nil { if len(jenkins.Spec.Backup.ContainerName) == 0 || jenkins.Spec.Backup.Action.Exec == nil {
bar.logger.V(log.VDebug).Info("Skipping restore backup, backup restore not configured") bar.logger.V(log.VDebug).Info("Skipping restore backup, backup restore not configured")
return nil return nil
@ -181,7 +173,7 @@ func (bar *BackupAndRestore) Backup() error {
podName := resources.GetJenkinsMasterPodName(*jenkins) podName := resources.GetJenkinsMasterPodName(*jenkins)
command := jenkins.Spec.Backup.Action.Exec.Command command := jenkins.Spec.Backup.Action.Exec.Command
command = append(command, fmt.Sprintf("%d", backupNumber)) command = append(command, fmt.Sprintf("%d", backupNumber))
_, _, err := bar.exec(podName, jenkins.Spec.Backup.ContainerName, command) _, _, err := bar.Exec(podName, jenkins.Spec.Backup.ContainerName, command)
if err == nil { if err == nil {
if jenkins.Status.RestoredBackup == 0 { if jenkins.Status.RestoredBackup == 0 {
@ -189,7 +181,7 @@ func (bar *BackupAndRestore) Backup() error {
} }
jenkins.Status.LastBackup = backupNumber jenkins.Status.LastBackup = backupNumber
jenkins.Status.PendingBackup = backupNumber jenkins.Status.PendingBackup = backupNumber
return bar.k8sClient.Update(context.TODO(), jenkins) return bar.Client.Update(context.TODO(), jenkins)
} }
return err return err
@ -217,9 +209,9 @@ func triggerBackup(ticker *time.Ticker, k8sClient k8s.Client, logger logr.Logger
// EnsureBackupTrigger creates or update trigger which update CR to make backup // EnsureBackupTrigger creates or update trigger which update CR to make backup
func (bar *BackupAndRestore) EnsureBackupTrigger() error { func (bar *BackupAndRestore) EnsureBackupTrigger() error {
trigger, found := triggers.get(bar.jenkins.Namespace, bar.jenkins.Name) trigger, found := triggers.get(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
isBackupConfigured := len(bar.jenkins.Spec.Backup.ContainerName) > 0 && bar.jenkins.Spec.Backup.Interval > 0 isBackupConfigured := len(bar.Configuration.Jenkins.Spec.Backup.ContainerName) > 0 && bar.Configuration.Jenkins.Spec.Backup.Interval > 0
if found && !isBackupConfigured { if found && !isBackupConfigured {
bar.StopBackupTrigger() bar.StopBackupTrigger()
return nil return nil
@ -231,7 +223,7 @@ func (bar *BackupAndRestore) EnsureBackupTrigger() error {
return nil return nil
} }
if found && isBackupConfigured && bar.jenkins.Spec.Backup.Interval != trigger.interval { if found && isBackupConfigured && bar.Configuration.Jenkins.Spec.Backup.Interval != trigger.interval {
bar.StopBackupTrigger() bar.StopBackupTrigger()
bar.startBackupTrigger() bar.startBackupTrigger()
} }
@ -241,55 +233,21 @@ func (bar *BackupAndRestore) EnsureBackupTrigger() error {
// StopBackupTrigger stops trigger which update CR to make backup // StopBackupTrigger stops trigger which update CR to make backup
func (bar *BackupAndRestore) StopBackupTrigger() { func (bar *BackupAndRestore) StopBackupTrigger() {
triggers.stop(bar.logger, bar.jenkins.Namespace, bar.jenkins.Name) triggers.stop(bar.logger, bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
} }
//IsBackupTriggerEnabled returns true if the backup trigger is enabled //IsBackupTriggerEnabled returns true if the backup trigger is enabled
func (bar *BackupAndRestore) IsBackupTriggerEnabled() bool { func (bar *BackupAndRestore) IsBackupTriggerEnabled() bool {
_, enabled := triggers.get(bar.jenkins.Namespace, bar.jenkins.Name) _, enabled := triggers.get(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
return enabled return enabled
} }
func (bar *BackupAndRestore) startBackupTrigger() { func (bar *BackupAndRestore) startBackupTrigger() {
bar.logger.Info("Starting backup trigger") bar.logger.Info("Starting backup trigger")
ticker := time.NewTicker(time.Duration(bar.jenkins.Spec.Backup.Interval) * time.Second) ticker := time.NewTicker(time.Duration(bar.Configuration.Jenkins.Spec.Backup.Interval) * time.Second)
triggers.add(bar.jenkins.Namespace, bar.jenkins.Name, backupTrigger{ triggers.add(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name, backupTrigger{
interval: bar.jenkins.Spec.Backup.Interval, interval: bar.Configuration.Jenkins.Spec.Backup.Interval,
ticker: ticker, ticker: ticker,
}) })
go triggerBackup(ticker, bar.k8sClient, bar.logger, bar.jenkins.Namespace, bar.jenkins.Name) go triggerBackup(ticker, bar.Client, bar.logger, bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
}
func (bar *BackupAndRestore) exec(podName, containerName string, command []string) (stdout, stderr bytes.Buffer, err error) {
req := bar.clientSet.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(bar.jenkins.Namespace).
SubResource("exec")
req.VersionedParams(&corev1.PodExecOptions{
Command: command,
Container: containerName,
Stdin: false,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(&bar.config, "POST", req.URL())
if err != nil {
return stdout, stderr, errors.Wrap(err, "pod exec error while creating Executor")
}
err = exec.Stream(remotecommand.StreamOptions{
Stdin: nil,
Stdout: &stdout,
Stderr: &stderr,
Tty: false,
})
bar.logger.V(log.VDebug).Info(fmt.Sprintf("pod exec: stdout '%s' stderr '%s'", stdout.String(), stderr.String()))
if err != nil {
return stdout, stderr, errors.Wrapf(err, "pod exec error operation on stream: stdout '%s' stderr '%s'", stdout.String(), stderr.String())
}
return
} }

View File

@ -29,7 +29,6 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -43,16 +42,14 @@ type ReconcileJenkinsBaseConfiguration struct {
configuration.Configuration configuration.Configuration
logger logr.Logger logger logr.Logger
jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings
config *rest.Config
} }
// New create structure which takes care of base configuration // New create structure which takes care of base configuration
func New(config configuration.Configuration, logger logr.Logger, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings, restConfig *rest.Config) *ReconcileJenkinsBaseConfiguration { func New(config configuration.Configuration, logger logr.Logger, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings) *ReconcileJenkinsBaseConfiguration {
return &ReconcileJenkinsBaseConfiguration{ return &ReconcileJenkinsBaseConfiguration{
Configuration: config, Configuration: config,
logger: logger, logger: logger,
jenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings, jenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings,
config: restConfig,
} }
} }
@ -528,7 +525,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
} }
if r.IsJenkinsTerminating(*currentJenkinsMasterPod) && r.Configuration.Jenkins.Status.UserConfigurationCompletedTime != nil { if r.IsJenkinsTerminating(*currentJenkinsMasterPod) && r.Configuration.Jenkins.Status.UserConfigurationCompletedTime != nil {
backupAndRestore := backuprestore.New(r.Client, r.ClientSet, r.logger, r.Configuration.Jenkins, *r.config) backupAndRestore := backuprestore.New(r.Configuration, r.logger)
if backupAndRestore.IsBackupTriggerEnabled() { if backupAndRestore.IsBackupTriggerEnabled() {
backupAndRestore.StopBackupTrigger() backupAndRestore.StopBackupTrigger()
} }
@ -904,6 +901,17 @@ func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins() (reconcile.Result,
} }
func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient.Jenkins, error) { 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 var service corev1.Service
err := r.Client.Get(context.TODO(), types.NamespacedName{ err := r.Client.Get(context.TODO(), types.NamespacedName{
@ -912,7 +920,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient
}, &service) }, &service)
if err != nil { if err != nil {
return nil, err return "", err
} }
jenkinsURL := r.jenkinsAPIConnectionSettings.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort) jenkinsURL := r.jenkinsAPIConnectionSettings.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort)
@ -921,6 +929,30 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient
jenkinsURL = jenkinsURL + prefix 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)) r.logger.V(log.VDebug).Info(fmt.Sprintf("Jenkins API URL '%s'", jenkinsURL))
credentialsSecret := &corev1.Secret{} credentialsSecret := &corev1.Secret{}
@ -948,7 +980,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient
currentJenkinsMasterPod.ObjectMeta.CreationTimestamp.Time.UTC().After(tokenCreationTime.UTC()) { currentJenkinsMasterPod.ObjectMeta.CreationTimestamp.Time.UTC().After(tokenCreationTime.UTC()) {
r.logger.Info("Generating Jenkins API token for operator") r.logger.Info("Generating Jenkins API token for operator")
userName := string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]) userName := string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey])
jenkinsClient, err := jenkinsclient.New( jenkinsClient, err := jenkinsclient.NewUserAndPasswordAuthorization(
jenkinsURL, jenkinsURL,
userName, userName,
string(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey])) string(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey]))
@ -970,7 +1002,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient() (jenkinsclient
} }
} }
return jenkinsclient.New( return jenkinsclient.NewUserAndPasswordAuthorization(
jenkinsURL, jenkinsURL,
string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]), string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]),
string(credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey])) string(credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey]))

View File

@ -240,7 +240,7 @@ func TestCompareVolumes(t *testing.T) {
Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins), Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins),
}, },
} }
reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{})
got := reconciler.compareVolumes(pod) got := reconciler.compareVolumes(pod)
@ -264,7 +264,7 @@ func TestCompareVolumes(t *testing.T) {
Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins), Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins),
}, },
} }
reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{})
got := reconciler.compareVolumes(pod) got := reconciler.compareVolumes(pod)
@ -288,7 +288,7 @@ func TestCompareVolumes(t *testing.T) {
Volumes: append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "added"}), Volumes: append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "added"}),
}, },
} }
reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, client.JenkinsAPIConnectionSettings{})
got := reconciler.compareVolumes(pod) got := reconciler.compareVolumes(pod)
@ -766,7 +766,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
Roles: []rbacv1.RoleRef{}, Roles: []rbacv1.RoleRef{},
}, },
} }
reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{})
metaObject := resources.NewResourceObjectMeta(jenkins) metaObject := resources.NewResourceObjectMeta(jenkins)
// when // when
@ -802,7 +802,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
}, },
}, },
} }
reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{})
metaObject := resources.NewResourceObjectMeta(jenkins) metaObject := resources.NewResourceObjectMeta(jenkins)
// when // when
@ -844,7 +844,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
}, },
}, },
} }
reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, nil, client.JenkinsAPIConnectionSettings{})
metaObject := resources.NewResourceObjectMeta(jenkins) metaObject := resources.NewResourceObjectMeta(jenkins)
// when // when
@ -887,7 +887,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
}, },
}, },
} }
reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, log.Log, client.JenkinsAPIConnectionSettings{}, nil) reconciler := New(configuration.Configuration{Client: fakeClient, Jenkins: jenkins, Scheme: scheme.Scheme}, log.Log, client.JenkinsAPIConnectionSettings{})
metaObject := resources.NewResourceObjectMeta(jenkins) metaObject := resources.NewResourceObjectMeta(jenkins)
// when // when

View File

@ -17,6 +17,7 @@ const createOperatorUserFileName = "createOperatorUser.groovy"
var createOperatorUserGroovyFmtTemplate = template.Must(template.New(createOperatorUserFileName).Parse(` var createOperatorUserGroovyFmtTemplate = template.Must(template.New(createOperatorUserFileName).Parse(`
import hudson.security.* import hudson.security.*
{{- if .Enable }}
def jenkins = jenkins.model.Jenkins.getInstance() def jenkins = jenkins.model.Jenkins.getInstance()
def operatorUserCreatedFile = new File('{{ .OperatorUserCreatedFilePath }}') def operatorUserCreatedFile = new File('{{ .OperatorUserCreatedFilePath }}')
@ -34,15 +35,18 @@ if (!operatorUserCreatedFile.exists()) {
operatorUserCreatedFile.createNewFile() operatorUserCreatedFile.createNewFile()
} }
{{- end }}
`)) `))
func buildCreateJenkinsOperatorUserGroovyScript(jenkins *v1alpha2.Jenkins) (*string, error) { func buildCreateJenkinsOperatorUserGroovyScript(jenkins *v1alpha2.Jenkins) (*string, error) {
data := struct { data := struct {
Enable bool
OperatorCredentialsPath string OperatorCredentialsPath string
OperatorUserNameFile string OperatorUserNameFile string
OperatorPasswordFile string OperatorPasswordFile string
OperatorUserCreatedFilePath string OperatorUserCreatedFilePath string
}{ }{
Enable: jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy == v1alpha2.CreateUserAuthorizationStrategy,
OperatorCredentialsPath: jenkinsOperatorCredentialsVolumePath, OperatorCredentialsPath: jenkinsOperatorCredentialsVolumePath,
OperatorUserNameFile: OperatorCredentialsSecretUserNameKey, OperatorUserNameFile: OperatorCredentialsSecretUserNameKey,
OperatorPasswordFile: OperatorCredentialsSecretPasswordKey, OperatorPasswordFile: OperatorCredentialsSecretPasswordKey,

View File

@ -62,6 +62,10 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins)
messages = append(messages, msg...) messages = append(messages, msg...)
} }
if jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy != v1alpha2.CreateUserAuthorizationStrategy && jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy != v1alpha2.ServiceAccountAuthorizationStrategy {
messages = append(messages, fmt.Sprintf("unrecognized '%s' spec.jenkinsAPISettings.authorizationStrategy", jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy))
}
return messages, nil return messages, nil
} }

View File

@ -23,7 +23,7 @@ import (
func TestValidatePlugins(t *testing.T) { func TestValidatePlugins(t *testing.T) {
log.SetupLogger(true) log.SetupLogger(true)
baseReconcileLoop := New(configuration.Configuration{}, log.Log, client.JenkinsAPIConnectionSettings{}, nil) baseReconcileLoop := New(configuration.Configuration{}, log.Log, client.JenkinsAPIConnectionSettings{})
t.Run("empty", func(t *testing.T) { t.Run("empty", func(t *testing.T) {
var requiredBasePlugins []plugins.Plugin var requiredBasePlugins []plugins.Plugin
var basePlugins []v1alpha2.Plugin var basePlugins []v1alpha2.Plugin
@ -166,7 +166,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateImagePullSecrets() got, err := baseReconcileLoop.validateImagePullSecrets()
fmt.Println(got) fmt.Println(got)
@ -190,7 +190,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, nil, client.JenkinsAPIConnectionSettings{}, nil) }, nil, client.JenkinsAPIConnectionSettings{})
got, _ := baseReconcileLoop.validateImagePullSecrets() got, _ := baseReconcileLoop.validateImagePullSecrets()
@ -226,7 +226,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, nil, client.JenkinsAPIConnectionSettings{}, nil) }, nil, client.JenkinsAPIConnectionSettings{})
got, _ := baseReconcileLoop.validateImagePullSecrets() got, _ := baseReconcileLoop.validateImagePullSecrets()
@ -262,7 +262,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, _ := baseReconcileLoop.validateImagePullSecrets() got, _ := baseReconcileLoop.validateImagePullSecrets()
@ -298,7 +298,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, _ := baseReconcileLoop.validateImagePullSecrets() got, _ := baseReconcileLoop.validateImagePullSecrets()
@ -334,7 +334,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, _ := baseReconcileLoop.validateImagePullSecrets() got, _ := baseReconcileLoop.validateImagePullSecrets()
@ -367,7 +367,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateJenkinsMasterPodEnvs() got := baseReconcileLoop.validateJenkinsMasterPodEnvs()
assert.Nil(t, got) assert.Nil(t, got)
}) })
@ -390,7 +390,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateJenkinsMasterPodEnvs() got := baseReconcileLoop.validateJenkinsMasterPodEnvs()
assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djava.awt.headless=true'"}) assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djava.awt.headless=true'"})
@ -414,7 +414,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateJenkinsMasterPodEnvs() got := baseReconcileLoop.validateJenkinsMasterPodEnvs()
assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djenkins.install.runSetupWizard=false'"}) assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djenkins.install.runSetupWizard=false'"})
@ -436,7 +436,7 @@ func TestValidateReservedVolumes(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateReservedVolumes() got := baseReconcileLoop.validateReservedVolumes()
assert.Nil(t, got) assert.Nil(t, got)
}) })
@ -454,7 +454,7 @@ func TestValidateReservedVolumes(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateReservedVolumes() got := baseReconcileLoop.validateReservedVolumes()
assert.Equal(t, got, []string{"Jenkins Master pod volume 'jenkins-home' is reserved please choose different one"}) assert.Equal(t, got, []string{"Jenkins Master pod volume 'jenkins-home' is reserved please choose different one"})
@ -470,7 +470,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateContainerVolumeMounts(v1alpha2.Container{}) got := baseReconcileLoop.validateContainerVolumeMounts(v1alpha2.Container{})
assert.Nil(t, got) assert.Nil(t, got)
}) })
@ -498,7 +498,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Nil(t, got) assert.Nil(t, got)
}) })
@ -526,7 +526,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Equal(t, got, []string{"mountPath not set for 'example' volume mount in container ''"}) assert.Equal(t, got, []string{"mountPath not set for 'example' volume mount in container ''"})
}) })
@ -549,7 +549,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
} }
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: &jenkins, Jenkins: &jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Equal(t, got, []string{"Not found volume for 'missing-volume' volume mount in container ''"}) assert.Equal(t, got, []string{"Not found volume for 'missing-volume' volume mount in container ''"})
@ -571,7 +571,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
fakeClient := fake.NewFakeClient() fakeClient := fake.NewFakeClient()
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateConfigMapVolume(volume) got, err := baseReconcileLoop.validateConfigMapVolume(volume)
@ -599,7 +599,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: jenkins, Jenkins: jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateConfigMapVolume(volume) got, err := baseReconcileLoop.validateConfigMapVolume(volume)
@ -625,7 +625,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: jenkins, Jenkins: jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateConfigMapVolume(volume) got, err := baseReconcileLoop.validateConfigMapVolume(volume)
@ -650,7 +650,7 @@ func TestValidateSecretVolume(t *testing.T) {
fakeClient := fake.NewFakeClient() fakeClient := fake.NewFakeClient()
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateSecretVolume(volume) got, err := baseReconcileLoop.validateSecretVolume(volume)
@ -676,7 +676,7 @@ func TestValidateSecretVolume(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: jenkins, Jenkins: jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateSecretVolume(volume) got, err := baseReconcileLoop.validateSecretVolume(volume)
@ -700,7 +700,7 @@ func TestValidateSecretVolume(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient, Client: fakeClient,
Jenkins: jenkins, Jenkins: jenkins,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateSecretVolume(volume) got, err := baseReconcileLoop.validateSecretVolume(volume)
assert.NoError(t, err) assert.NoError(t, err)
@ -724,7 +724,7 @@ func TestValidateCustomization(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins, Jenkins: jenkins,
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts")
@ -746,7 +746,7 @@ func TestValidateCustomization(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins, Jenkins: jenkins,
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
err := fakeClient.Create(context.TODO(), secret) err := fakeClient.Create(context.TODO(), secret)
require.NoError(t, err) require.NoError(t, err)
@ -777,7 +777,7 @@ func TestValidateCustomization(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins, Jenkins: jenkins,
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
err := fakeClient.Create(context.TODO(), secret) err := fakeClient.Create(context.TODO(), secret)
require.NoError(t, err) require.NoError(t, err)
err = fakeClient.Create(context.TODO(), configMap) err = fakeClient.Create(context.TODO(), configMap)
@ -804,7 +804,7 @@ func TestValidateCustomization(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins, Jenkins: jenkins,
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
err := fakeClient.Create(context.TODO(), configMap) err := fakeClient.Create(context.TODO(), configMap)
require.NoError(t, err) require.NoError(t, err)
@ -829,7 +829,7 @@ func TestValidateCustomization(t *testing.T) {
baseReconcileLoop := New(configuration.Configuration{ baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins, Jenkins: jenkins,
Client: fakeClient, Client: fakeClient,
}, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{}, nil) }, logf.ZapLogger(false), client.JenkinsAPIConnectionSettings{})
err := fakeClient.Create(context.TODO(), secret) err := fakeClient.Create(context.TODO(), secret)
require.NoError(t, err) require.NoError(t, err)

View File

@ -1,6 +1,7 @@
package configuration package configuration
import ( import (
"bytes"
"context" "context"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
@ -15,6 +16,9 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
) )
@ -26,6 +30,7 @@ type Configuration struct {
Notifications *chan event.Event Notifications *chan event.Event
Jenkins *v1alpha2.Jenkins Jenkins *v1alpha2.Jenkins
Scheme *runtime.Scheme Scheme *runtime.Scheme
Config *rest.Config
} }
// RestartJenkinsMasterPod terminate Jenkins master pod and notifies about it // RestartJenkinsMasterPod terminate Jenkins master pod and notifies about it
@ -111,3 +116,37 @@ func (c *Configuration) CreateOrUpdateResource(obj metav1.Object) error {
return nil return nil
} }
// Exec executes command in the given pod and it's container
func (c *Configuration) Exec(podName, containerName string, command []string) (stdout, stderr bytes.Buffer, err error) {
req := c.ClientSet.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(c.Jenkins.Namespace).
SubResource("exec")
req.VersionedParams(&corev1.PodExecOptions{
Command: command,
Container: containerName,
Stdin: false,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(c.Config, "POST", req.URL())
if err != nil {
return stdout, stderr, stackerr.Wrap(err, "pod exec error while creating Executor")
}
err = exec.Stream(remotecommand.StreamOptions{
Stdin: nil,
Stdout: &stdout,
Stderr: &stderr,
Tty: false,
})
if err != nil {
return stdout, stderr, stackerr.Wrapf(err, "pod exec error operation on stream: stdout '%s' stderr '%s'", stdout.String(), stderr.String())
}
return
}

View File

@ -12,7 +12,6 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/groovy" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/groovy"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -21,22 +20,20 @@ type ReconcileUserConfiguration struct {
configuration.Configuration configuration.Configuration
jenkinsClient jenkinsclient.Jenkins jenkinsClient jenkinsclient.Jenkins
logger logr.Logger logger logr.Logger
config rest.Config
} }
// New create structure which takes care of user configuration // New create structure which takes care of user configuration
func New(configuration configuration.Configuration, jenkinsClient jenkinsclient.Jenkins, logger logr.Logger, config rest.Config) *ReconcileUserConfiguration { func New(configuration configuration.Configuration, jenkinsClient jenkinsclient.Jenkins, logger logr.Logger) *ReconcileUserConfiguration {
return &ReconcileUserConfiguration{ return &ReconcileUserConfiguration{
Configuration: configuration, Configuration: configuration,
jenkinsClient: jenkinsClient, jenkinsClient: jenkinsClient,
logger: logger, logger: logger,
config: config,
} }
} }
// Reconcile it's a main reconciliation loop for user supplied configuration // Reconcile it's a main reconciliation loop for user supplied configuration
func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) { func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) {
backupAndRestore := backuprestore.New(r.Client, r.ClientSet, r.logger, r.Configuration.Jenkins, r.config) backupAndRestore := backuprestore.New(r.Configuration, r.logger)
result, err := r.ensureSeedJobs() result, err := r.ensureSeedJobs()
if err != nil { if err != nil {

View File

@ -8,7 +8,7 @@ import (
// Validate validates Jenkins CR Spec section // Validate validates Jenkins CR Spec section
func (r *ReconcileUserConfiguration) Validate(jenkins *v1alpha2.Jenkins) ([]string, error) { func (r *ReconcileUserConfiguration) Validate(jenkins *v1alpha2.Jenkins) ([]string, error) {
backupAndRestore := backuprestore.New(r.Client, r.ClientSet, r.logger, r.Configuration.Jenkins, r.config) backupAndRestore := backuprestore.New(r.Configuration, r.logger)
if msg := backupAndRestore.Validate(); msg != nil { if msg := backupAndRestore.Validate(); msg != nil {
return msg, nil return msg, nil
} }

View File

@ -232,10 +232,11 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg
Notifications: r.notificationEvents, Notifications: r.notificationEvents,
Jenkins: jenkins, Jenkins: jenkins,
Scheme: r.scheme, Scheme: r.scheme,
Config: &r.config,
} }
// Reconcile base configuration // Reconcile base configuration
baseConfiguration := base.New(config, logger, r.jenkinsAPIConnectionSettings, &r.config) baseConfiguration := base.New(config, logger, r.jenkinsAPIConnectionSettings)
var baseMessages []string var baseMessages []string
baseMessages, err = baseConfiguration.Validate(jenkins) baseMessages, err = baseConfiguration.Validate(jenkins)
@ -289,7 +290,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg
logger.Info(message) logger.Info(message)
} }
// Reconcile user configuration // Reconcile user configuration
userConfiguration := user.New(config, jenkinsClient, logger, r.config) userConfiguration := user.New(config, jenkinsClient, logger)
var messages []string var messages []string
messages, err = userConfiguration.Validate(jenkins) messages, err = userConfiguration.Validate(jenkins)
@ -496,6 +497,18 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
jenkins.Spec.Master.SecurityContext = &securityContext jenkins.Spec.Master.SecurityContext = &securityContext
} }
if reflect.DeepEqual(jenkins.Spec.JenkinsAPISettings, v1alpha2.JenkinsAPISettings{}) {
logger.Info("Setting default Jenkins API settings")
changed = true
jenkins.Spec.JenkinsAPISettings = v1alpha2.JenkinsAPISettings{AuthorizationStrategy: v1alpha2.CreateUserAuthorizationStrategy}
}
if jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy == "" {
logger.Info("Setting default Jenkins API settings authorization strategy")
changed = true
jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy = v1alpha2.CreateUserAuthorizationStrategy
}
if changed { if changed {
return changed, errors.WithStack(r.client.Update(context.TODO(), jenkins)) return changed, errors.WithStack(r.client.Update(context.TODO(), jenkins))
} }

View File

@ -70,7 +70,7 @@ func createJenkinsAPIClient(jenkins *v1alpha2.Jenkins, hostname string, port int
UseNodePort: useNodePort, UseNodePort: useNodePort,
}.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort) }.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort)
return jenkinsclient.New( return jenkinsclient.NewUserAndPasswordAuthorization(
jenkinsAPIURL, jenkinsAPIURL,
string(adminSecret.Data[resources.OperatorCredentialsSecretUserNameKey]), string(adminSecret.Data[resources.OperatorCredentialsSecretUserNameKey]),
string(adminSecret.Data[resources.OperatorCredentialsSecretTokenKey]), string(adminSecret.Data[resources.OperatorCredentialsSecretTokenKey]),