#29 Restart Jenkins pod when any seed job has been deleted
This commit is contained in:
parent
3b26e1c5ba
commit
dda0a0075e
|
|
@ -334,6 +334,10 @@ type JenkinsStatus struct {
|
||||||
// UserAndPasswordHash is a SHA256 hash made from user and password
|
// UserAndPasswordHash is a SHA256 hash made from user and password
|
||||||
// +optional
|
// +optional
|
||||||
UserAndPasswordHash string `json:"userAndPasswordHash,omitempty"`
|
UserAndPasswordHash string `json:"userAndPasswordHash,omitempty"`
|
||||||
|
|
||||||
|
// CreatedSeedJobs contains list of seed job id already created in Jenkins
|
||||||
|
// +optional
|
||||||
|
CreatedSeedJobs []string `json:"createdSeedJobs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildStatus defines type of Jenkins build job status
|
// BuildStatus defines type of Jenkins build job status
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
r.logger.Info("Some plugins have changed, restarting Jenkins")
|
r.logger.Info("Some plugins have changed, restarting Jenkins")
|
||||||
return reconcile.Result{Requeue: true}, nil, r.restartJenkinsMasterPod(metaObject)
|
return reconcile.Result{Requeue: true}, nil, r.restartJenkinsMasterPod()
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = r.ensureBaseConfiguration(jenkinsClient)
|
result, err = r.ensureBaseConfiguration(jenkinsClient)
|
||||||
|
|
@ -368,7 +368,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta
|
||||||
return stackerr.WithStack(r.updateResource(&service))
|
return stackerr.WithStack(r.updateResource(&service))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod(meta metav1.ObjectMeta) (*corev1.Pod, error) {
|
func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod() (*corev1.Pod, error) {
|
||||||
jenkinsMasterPodName := resources.GetJenkinsMasterPodName(*r.jenkins)
|
jenkinsMasterPodName := resources.GetJenkinsMasterPodName(*r.jenkins)
|
||||||
currentJenkinsMasterPod := &corev1.Pod{}
|
currentJenkinsMasterPod := &corev1.Pod{}
|
||||||
err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPodName, Namespace: r.jenkins.Namespace}, currentJenkinsMasterPod)
|
err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPodName, Namespace: r.jenkins.Namespace}, currentJenkinsMasterPod)
|
||||||
|
|
@ -385,7 +385,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this Pod already exists
|
// Check if this Pod already exists
|
||||||
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
currentJenkinsMasterPod, err := r.getJenkinsMasterPod()
|
||||||
if err != nil && errors.IsNotFound(err) {
|
if err != nil && errors.IsNotFound(err) {
|
||||||
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins)
|
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins)
|
||||||
if !reflect.DeepEqual(jenkinsMasterPod.Spec.Containers[0].Command, resources.GetJenkinsMasterContainerBaseCommand()) {
|
if !reflect.DeepEqual(jenkinsMasterPod.Spec.Containers[0].Command, resources.GetJenkinsMasterContainerBaseCommand()) {
|
||||||
|
|
@ -433,7 +433,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
if currentJenkinsMasterPod != nil && r.isRecreatePodNeeded(*currentJenkinsMasterPod, userAndPasswordHash) {
|
if currentJenkinsMasterPod != nil && r.isRecreatePodNeeded(*currentJenkinsMasterPod, userAndPasswordHash) {
|
||||||
return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod(meta)
|
return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod()
|
||||||
}
|
}
|
||||||
|
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
|
|
@ -638,8 +638,8 @@ func (r *ReconcileJenkinsBaseConfiguration) compareVolumes(actualPod corev1.Pod)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) restartJenkinsMasterPod(meta metav1.ObjectMeta) error {
|
func (r *ReconcileJenkinsBaseConfiguration) restartJenkinsMasterPod() error {
|
||||||
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
currentJenkinsMasterPod, err := r.getJenkinsMasterPod()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -648,7 +648,7 @@ func (r *ReconcileJenkinsBaseConfiguration) restartJenkinsMasterPod(meta metav1.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) detectJenkinsMasterPodStartingIssues(meta metav1.ObjectMeta) (stopReconcileLoop bool, err error) {
|
func (r *ReconcileJenkinsBaseConfiguration) detectJenkinsMasterPodStartingIssues(meta metav1.ObjectMeta) (stopReconcileLoop bool, err error) {
|
||||||
jenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
jenkinsMasterPod, err := r.getJenkinsMasterPod()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -696,7 +696,7 @@ func (r *ReconcileJenkinsBaseConfiguration) filterEvents(source corev1.EventList
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins(meta metav1.ObjectMeta) (reconcile.Result, error) {
|
func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins(meta metav1.ObjectMeta) (reconcile.Result, error) {
|
||||||
jenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
jenkinsMasterPod, err := r.getJenkinsMasterPod()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -715,7 +715,7 @@ func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins(meta metav1.ObjectMet
|
||||||
for _, containerStatus := range jenkinsMasterPod.Status.ContainerStatuses {
|
for _, containerStatus := range jenkinsMasterPod.Status.ContainerStatuses {
|
||||||
if containerStatus.State.Terminated != nil {
|
if containerStatus.State.Terminated != nil {
|
||||||
r.logger.Info(fmt.Sprintf("Container '%s' is terminated, status '%+v', recreating pod", containerStatus.Name, containerStatus))
|
r.logger.Info(fmt.Sprintf("Container '%s' is terminated, status '%+v', recreating pod", containerStatus.Name, containerStatus))
|
||||||
return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod(meta)
|
return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod()
|
||||||
}
|
}
|
||||||
if !containerStatus.Ready {
|
if !containerStatus.Ready {
|
||||||
r.logger.V(log.VDebug).Info(fmt.Sprintf("Container '%s' not ready, readiness probe failed", containerStatus.Name))
|
r.logger.V(log.VDebug).Info(fmt.Sprintf("Container '%s' not ready, readiness probe failed", containerStatus.Name))
|
||||||
|
|
@ -743,7 +743,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient(meta metav1.Obje
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, stackerr.WithStack(err)
|
return nil, stackerr.WithStack(err)
|
||||||
}
|
}
|
||||||
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
currentJenkinsMasterPod, err := r.getJenkinsMasterPod()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (r *ReconcileUserConfiguration) ensureSeedJobs() (reconcile.Result, error)
|
||||||
}
|
}
|
||||||
// build not finished yet - requeue reconciliation loop with timeout
|
// build not finished yet - requeue reconciliation loop with timeout
|
||||||
if !done {
|
if !done {
|
||||||
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil
|
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||||
}
|
}
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"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"
|
||||||
|
|
@ -61,6 +62,11 @@ func New(jenkinsClient jenkinsclient.Jenkins, k8sClient k8s.Client, logger logr.
|
||||||
|
|
||||||
// EnsureSeedJobs configures seed job and runs it for every entry from Jenkins.Spec.SeedJobs
|
// EnsureSeedJobs configures seed job and runs it for every entry from Jenkins.Spec.SeedJobs
|
||||||
func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err error) {
|
func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err error) {
|
||||||
|
if s.isRecreatePodNeeded(*jenkins) {
|
||||||
|
s.logger.Info("Some seed job has been deleted, recreating pod")
|
||||||
|
return false, s.restartJenkinsMasterPod(*jenkins)
|
||||||
|
}
|
||||||
|
|
||||||
if err = s.createJob(); err != nil {
|
if err = s.createJob(); err != nil {
|
||||||
s.logger.V(log.VWarn).Info("Couldn't create jenkins seed job")
|
s.logger.V(log.VWarn).Info("Couldn't create jenkins seed job")
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -75,6 +81,13 @@ func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err err
|
||||||
s.logger.V(log.VWarn).Info("Couldn't build jenkins seed job")
|
s.logger.V(log.VWarn).Info("Couldn't build jenkins seed job")
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seedJobIDs := s.getAllSeedJobIDs(*jenkins)
|
||||||
|
if done && !reflect.DeepEqual(seedJobIDs, jenkins.Status.CreatedSeedJobs) {
|
||||||
|
jenkins.Status.CreatedSeedJobs = seedJobIDs
|
||||||
|
return false, stackerr.WithStack(s.k8sClient.Update(context.TODO(), jenkins))
|
||||||
|
}
|
||||||
|
|
||||||
return done, nil
|
return done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,6 +188,51 @@ func (s *SeedJobs) credentialValue(namespace string, seedJob v1alpha2.SeedJob) (
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SeedJobs) getAllSeedJobIDs(jenkins v1alpha2.Jenkins) []string {
|
||||||
|
var ids []string
|
||||||
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
||||||
|
ids = append(ids, seedJob.ID)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO move to k8sClient
|
||||||
|
func (s *SeedJobs) getJenkinsMasterPod(jenkins v1alpha2.Jenkins) (*corev1.Pod, error) {
|
||||||
|
jenkinsMasterPodName := resources.GetJenkinsMasterPodName(jenkins)
|
||||||
|
currentJenkinsMasterPod := &corev1.Pod{}
|
||||||
|
err := s.k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPodName, Namespace: jenkins.Namespace}, currentJenkinsMasterPod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err // don't wrap error
|
||||||
|
}
|
||||||
|
return currentJenkinsMasterPod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO move to k8sClient
|
||||||
|
func (s *SeedJobs) restartJenkinsMasterPod(jenkins v1alpha2.Jenkins) error {
|
||||||
|
currentJenkinsMasterPod, err := s.getJenkinsMasterPod(jenkins)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.logger.Info(fmt.Sprintf("Terminating Jenkins Master Pod %s/%s", currentJenkinsMasterPod.Namespace, currentJenkinsMasterPod.Name))
|
||||||
|
return stackerr.WithStack(s.k8sClient.Delete(context.TODO(), currentJenkinsMasterPod))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SeedJobs) isRecreatePodNeeded(jenkins v1alpha2.Jenkins) bool {
|
||||||
|
for _, createdSeedJob := range jenkins.Status.CreatedSeedJobs {
|
||||||
|
found := false
|
||||||
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
||||||
|
if createdSeedJob == seedJob.ID {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// seedJobConfigXML this is the XML representation of seed job
|
// seedJobConfigXML this is the XML representation of seed job
|
||||||
var seedJobConfigXML = `
|
var seedJobConfigXML = `
|
||||||
<flow-definition plugin="workflow-job@2.30">
|
<flow-definition plugin="workflow-job@2.30">
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ func TestEnsureSeedJobs(t *testing.T) {
|
||||||
|
|
||||||
// second run - should update and finish job
|
// second run - should update and finish job
|
||||||
if reconcileAttempt == 2 {
|
if reconcileAttempt == 2 {
|
||||||
assert.True(t, done)
|
assert.False(t, done)
|
||||||
assert.Equal(t, string(v1alpha2.BuildSuccessStatus), string(build.Status))
|
assert.Equal(t, string(v1alpha2.BuildSuccessStatus), string(build.Status))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,3 +151,71 @@ func jenkinsCustomResource() *v1alpha2.Jenkins {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSeedJobs_isRecreatePodNeeded(t *testing.T) {
|
||||||
|
seedJobsClient := New(nil, nil, nil)
|
||||||
|
t.Run("empty", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{}
|
||||||
|
|
||||||
|
got := seedJobsClient.isRecreatePodNeeded(jenkins)
|
||||||
|
|
||||||
|
assert.False(t, got)
|
||||||
|
})
|
||||||
|
t.Run("same", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1alpha2.JenkinsStatus{
|
||||||
|
CreatedSeedJobs: []string{"name"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := seedJobsClient.isRecreatePodNeeded(jenkins)
|
||||||
|
|
||||||
|
assert.False(t, got)
|
||||||
|
})
|
||||||
|
t.Run("removed one", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "name1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1alpha2.JenkinsStatus{
|
||||||
|
CreatedSeedJobs: []string{"name1", "name2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := seedJobsClient.isRecreatePodNeeded(jenkins)
|
||||||
|
|
||||||
|
assert.True(t, got)
|
||||||
|
})
|
||||||
|
t.Run("renamed one", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "name1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "name3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1alpha2.JenkinsStatus{
|
||||||
|
CreatedSeedJobs: []string{"name1", "name2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := seedJobsClient.isRecreatePodNeeded(jenkins)
|
||||||
|
|
||||||
|
assert.True(t, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue