#45 Add support for imagePullSecrets parameter
This commit is contained in:
parent
164fe30aef
commit
e049218b78
|
|
@ -210,6 +210,13 @@ type JenkinsMaster struct {
|
||||||
// memory: 600Mi
|
// memory: 600Mi
|
||||||
Containers []Container `json:"containers,omitempty"`
|
Containers []Container `json:"containers,omitempty"`
|
||||||
|
|
||||||
|
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
|
||||||
|
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
|
||||||
|
// in the case of docker, only DockerConfig type secrets are honored.
|
||||||
|
// More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod
|
||||||
|
// +optional
|
||||||
|
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
||||||
|
|
||||||
// List of volumes that can be mounted by containers belonging to the pod.
|
// List of volumes that can be mounted by containers belonging to the pod.
|
||||||
// More info: https://kubernetes.io/docs/concepts/storage/volumes
|
// More info: https://kubernetes.io/docs/concepts/storage/volumes
|
||||||
// +optional
|
// +optional
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,11 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.ImagePullSecrets != nil {
|
||||||
|
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
||||||
|
*out = make([]v1.LocalObjectReference, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
if in.Volumes != nil {
|
if in.Volumes != nil {
|
||||||
in, out := &in.Volumes, &out.Volumes
|
in, out := &in.Volumes, &out.Volumes
|
||||||
*out = make([]v1.Volume, len(*in))
|
*out = make([]v1.Volume, len(*in))
|
||||||
|
|
|
||||||
|
|
@ -527,6 +527,12 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(r.jenkins.Spec.Master.ImagePullSecrets, currentJenkinsMasterPod.Spec.ImagePullSecrets) {
|
||||||
|
r.logger.Info(fmt.Sprintf("Jenkins Pod ImagePullSecrets has changed, actual '%+v' required '%+v', recreating pod",
|
||||||
|
currentJenkinsMasterPod.Spec.ImagePullSecrets, r.jenkins.Spec.Master.ImagePullSecrets))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) {
|
if !reflect.DeepEqual(r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) {
|
||||||
r.logger.Info(fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v', recreating pod",
|
r.logger.Info(fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v', recreating pod",
|
||||||
currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector))
|
currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector))
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,29 @@ func TestGetJenkinsOpts(t *testing.T) {
|
||||||
assert.Contains(t, opts, "httpPort")
|
assert.Contains(t, opts, "httpPort")
|
||||||
assert.Equal(t, opts["httpPort"], "8080")
|
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) {
|
func TestCompareContainerVolumeMounts(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,7 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins
|
||||||
Containers: newContainers(jenkins),
|
Containers: newContainers(jenkins),
|
||||||
Volumes: append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...),
|
Volumes: append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...),
|
||||||
SecurityContext: jenkins.Spec.Master.SecurityContext,
|
SecurityContext: jenkins.Spec.Master.SecurityContext,
|
||||||
|
ImagePullSecrets: jenkins.Spec.Master.ImagePullSecrets,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
|
@ -60,6 +61,46 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecrets() (bool, error) {
|
||||||
|
var err error
|
||||||
|
valid := true
|
||||||
|
ips := r.jenkins.Spec.Master.ImagePullSecrets
|
||||||
|
for _, sr := range ips {
|
||||||
|
valid, err = r.validateImagePullSecret(sr.Name)
|
||||||
|
if err != nil || !valid {
|
||||||
|
return valid, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecret(name string) (bool, error) {
|
||||||
|
secret := &corev1.Secret{}
|
||||||
|
err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: r.jenkins.ObjectMeta.Namespace}, secret)
|
||||||
|
if err != nil && apierrors.IsNotFound(err) {
|
||||||
|
r.logger.V(log.VWarn).Info(fmt.Sprintf("Secret '%s' not found", name))
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil && !apierrors.IsNotFound(err) {
|
||||||
|
return false, stackerr.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret.Data["docker-server"] == nil {
|
||||||
|
return false, errors.New("docker server not set")
|
||||||
|
}
|
||||||
|
if secret.Data["docker-username"] == nil {
|
||||||
|
return false, errors.New("docker username not set")
|
||||||
|
}
|
||||||
|
if secret.Data["docker-password"] == nil {
|
||||||
|
return false, errors.New("docker password not set")
|
||||||
|
}
|
||||||
|
if secret.Data["docker-email"] == nil {
|
||||||
|
return false, errors.New("docker email not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() (bool, error) {
|
func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() (bool, error) {
|
||||||
valid := true
|
valid := true
|
||||||
for _, volume := range r.jenkins.Spec.Master.Volumes {
|
for _, volume := range r.jenkins.Spec.Master.Volumes {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,199 @@ func TestValidatePlugins(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T) {
|
||||||
|
t.Run("happy", func(t *testing.T) {
|
||||||
|
lor := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-ref",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"docker-server": []byte("test_server"),
|
||||||
|
"docker-username": []byte("test_user"),
|
||||||
|
"docker-password": []byte("test_password"),
|
||||||
|
"docker-email": []byte("test_email"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: lor.ObjectMeta.Name},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
err := fakeClient.Create(context.TODO(), lor)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, err := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no secret", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: "test-ref"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, _ := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no docker email", func(t *testing.T) {
|
||||||
|
lor := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-ref",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"docker-server": []byte("test_server"),
|
||||||
|
"docker-username": []byte("test_user"),
|
||||||
|
"docker-password": []byte("test_password"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: lor.ObjectMeta.Name},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
err := fakeClient.Create(context.TODO(), lor)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, err := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, false)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no docker password", func(t *testing.T) {
|
||||||
|
lor := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-ref",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"docker-server": []byte("test_server"),
|
||||||
|
"docker-username": []byte("test_user"),
|
||||||
|
"docker-email": []byte("test_email"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: lor.ObjectMeta.Name},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
err := fakeClient.Create(context.TODO(), lor)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, err := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, false)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no docker username", func(t *testing.T) {
|
||||||
|
lor := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-ref",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"docker-server": []byte("test_server"),
|
||||||
|
"docker-password": []byte("test_password"),
|
||||||
|
"docker-email": []byte("test_email"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: lor.ObjectMeta.Name},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
err := fakeClient.Create(context.TODO(), lor)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, err := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, false)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no docker server", func(t *testing.T) {
|
||||||
|
lor := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-ref",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"docker-username": []byte("test_user"),
|
||||||
|
"docker-password": []byte("test_password"),
|
||||||
|
"docker-email": []byte("test_email"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
Master: v1alpha2.JenkinsMaster{
|
||||||
|
ImagePullSecrets: []corev1.LocalObjectReference{
|
||||||
|
{Name: lor.ObjectMeta.Name},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClient := fake.NewFakeClient()
|
||||||
|
err := fakeClient.Create(context.TODO(), lor)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false),
|
||||||
|
&jenkins, false, false, nil, nil)
|
||||||
|
|
||||||
|
got, err := baseReconcileLoop.validateImagePullSecrets()
|
||||||
|
assert.Equal(t, got, false)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
|
func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
|
||||||
t.Run("happy", func(t *testing.T) {
|
t.Run("happy", func(t *testing.T) {
|
||||||
jenkins := v1alpha2.Jenkins{
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue