package seedjobs import ( "context" "testing" "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client" "github.com/jenkinsci/kubernetes-operator/pkg/configuration" "github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources" "github.com/bndr/gojenkins" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) const agentSecret = "test-secret" func jenkinsCustomResource() *v1alpha2.Jenkins { return &v1alpha2.Jenkins{ ObjectMeta: metav1.ObjectMeta{ Name: "jenkins", Namespace: "default", }, Spec: v1alpha2.JenkinsSpec{ Master: v1alpha2.JenkinsMaster{ Annotations: map[string]string{"test": "label"}, Containers: []v1alpha2.Container{ { Name: resources.JenkinsMasterContainerName, Image: "jenkins/jenkins", Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("300m"), corev1.ResourceMemory: resource.MustParse("500Mi"), }, Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("2"), corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, }, }, SeedJobs: []v1alpha2.SeedJob{ { ID: "jenkins-operator-e2e", JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType, Targets: "cicd/jobs/*.jenkins", Description: "Jenkins Operator e2e tests repository", RepositoryBranch: "master", RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git", }, }, }, } } func TestEnsureSeedJobs(t *testing.T) { t.Run("happy", func(t *testing.T) { // given ctrl := gomock.NewController(t) ctx := context.TODO() defer ctrl.Finish() jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) fakeClient := fake.NewClientBuilder().Build() err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) assert.NoError(t, err) jenkins := jenkinsCustomResource() err = fakeClient.Create(ctx, jenkins) assert.NoError(t, err) testNode := &gojenkins.Node{ Raw: &gojenkins.NodeResponse{ DisplayName: AgentName, }, } config := configuration.Configuration{ Client: fakeClient, ClientSet: kubernetes.Clientset{}, Notifications: nil, Jenkins: jenkins, } seedJobCreatingScript, err := seedJobCreatingGroovyScript(jenkins.Spec.SeedJobs[0]) assert.NoError(t, err) jenkinsClient.EXPECT().GetNode(context.TODO(), AgentName).Return(nil, nil).AnyTimes() jenkinsClient.EXPECT().CreateNode(context.TODO(), AgentName, 1, "The jenkins-operator generated agent", "/home/jenkins", AgentName).Return(testNode, nil).AnyTimes() jenkinsClient.EXPECT().GetNodeSecret(AgentName).Return(agentSecret, nil).AnyTimes() jenkinsClient.EXPECT().ExecuteScript(seedJobCreatingScript).AnyTimes() seedJobClient := New(jenkinsClient, config) // when _, err = seedJobClient.EnsureSeedJobs(jenkins) // then assert.NoError(t, err) var agentDeployment appsv1.Deployment err = fakeClient.Get(ctx, types.NamespacedName{Namespace: jenkins.Namespace, Name: agentDeploymentName(*jenkins, AgentName)}, &agentDeployment) assert.NoError(t, err) assert.Equal(t, "jenkins/inbound-agent:3248.v65ecb_254c298-6", agentDeployment.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, "JENKINS_WEB_SOCKET", agentDeployment.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, "true", agentDeployment.Spec.Template.Spec.Containers[0].Env[0].Value) }) t.Run("delete agent deployment when no seed jobs", func(t *testing.T) { // given ctrl := gomock.NewController(t) ctx := context.TODO() defer ctrl.Finish() jenkins := jenkinsCustomResource() jenkins.Spec.SeedJobs = []v1alpha2.SeedJob{} jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) fakeClient := fake.NewClientBuilder(). WithRuntimeObjects(jenkins). WithStatusSubresource(jenkins). Build() err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) assert.NoError(t, err) config := configuration.Configuration{ Client: fakeClient, ClientSet: kubernetes.Clientset{}, Notifications: nil, Jenkins: jenkins, } jenkinsClient.EXPECT().GetNode(ctx, AgentName).AnyTimes() jenkinsClient.EXPECT().CreateNode(ctx, AgentName, 1, "The jenkins-operator generated agent", "/home/jenkins", AgentName).AnyTimes() jenkinsClient.EXPECT().GetNodeSecret(AgentName).Return(agentSecret, nil).AnyTimes() seedJobsClient := New(jenkinsClient, config) err = fakeClient.Create(ctx, &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: agentDeploymentName(*jenkins, AgentName), Namespace: jenkins.Namespace, }, }) assert.NoError(t, err) // when _, err = seedJobsClient.EnsureSeedJobs(jenkins) // then assert.NoError(t, err) var deployment appsv1.Deployment err = fakeClient.Get(ctx, types.NamespacedName{ Namespace: jenkins.Namespace, Name: agentDeploymentName(*jenkins, AgentName), }, &deployment) assert.True(t, errors.IsNotFound(err), "Agent deployment hasn't been deleted") }) } func TestCreateAgent(t *testing.T) { t.Run("don't fail when deployment is already created", func(t *testing.T) { // given ctrl := gomock.NewController(t) ctx := context.TODO() defer ctrl.Finish() jenkins := jenkinsCustomResource() jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) fakeClient := fake.NewClientBuilder().Build() err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) assert.NoError(t, err) jenkinsClient.EXPECT().GetNode(context.TODO(), AgentName).AnyTimes() jenkinsClient.EXPECT().CreateNode(context.TODO(), AgentName, 1, "The jenkins-operator generated agent", "/home/jenkins", AgentName).AnyTimes() jenkinsClient.EXPECT().GetNodeSecret(AgentName).Return(agentSecret, nil).AnyTimes() config := configuration.Configuration{ Client: fakeClient, ClientSet: kubernetes.Clientset{}, Notifications: nil, Jenkins: &v1alpha2.Jenkins{}, } seedJobsClient := New(jenkinsClient, config) err = fakeClient.Create(ctx, &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: agentDeploymentName(*jenkins, AgentName), Namespace: jenkins.Namespace, }, }) assert.NoError(t, err) // when err = seedJobsClient.createAgent(jenkinsClient, fakeClient, jenkinsCustomResource(), jenkins.Namespace, AgentName) // then assert.NoError(t, err) }) } func TestSeedJobs_isRecreatePodNeeded(t *testing.T) { config := configuration.Configuration{ Client: nil, ClientSet: kubernetes.Clientset{}, Notifications: nil, Jenkins: &v1alpha2.Jenkins{}, } seedJobsClient := New(nil, config) 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) }) }