mirror of
https://github.com/jenkinsci/kubernetes-operator.git
synced 2026-10-09 17:35:32 +02:00
#14 Add username/password authentication for seed jobs
This commit is contained in:
@@ -5,12 +5,10 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/seedjobs"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
|
||||
"github.com/bndr/gojenkins"
|
||||
@@ -19,8 +17,6 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
@@ -33,14 +29,24 @@ func TestConfiguration(t *testing.T) {
|
||||
numberOfExecutors := 6
|
||||
systemMessage := "Configuration as Code integration works!!!"
|
||||
systemMessageEnvName := "SYSTEM_MESSAGE"
|
||||
jenkinsCredentialName := "kubernetes-credentials-provider-plugin"
|
||||
mySeedJob := seedJobConfig{
|
||||
SeedJob: v1alpha1.SeedJob{
|
||||
ID: "jenkins-operator",
|
||||
CredentialID: "jenkins-operator",
|
||||
JenkinsCredentialType: v1alpha1.NoJenkinsCredentialCredentialType,
|
||||
Targets: "cicd/jobs/*.jenkins",
|
||||
Description: "Jenkins Operator repository",
|
||||
RepositoryBranch: "master",
|
||||
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||
},
|
||||
}
|
||||
|
||||
// base
|
||||
createUserConfigurationSecret(t, jenkinsCRName, namespace, systemMessageEnvName, systemMessage)
|
||||
createUserConfigurationConfigMap(t, jenkinsCRName, namespace, numberOfExecutors, fmt.Sprintf("${%s}", systemMessageEnvName))
|
||||
jenkins := createJenkinsCR(t, jenkinsCRName, namespace)
|
||||
jenkins := createJenkinsCR(t, jenkinsCRName, namespace, &[]v1alpha1.SeedJob{mySeedJob.SeedJob})
|
||||
createDefaultLimitsForContainersInNamespace(t, namespace)
|
||||
createKubernetesCredentialsProviderSecret(t, namespace, jenkinsCredentialName)
|
||||
createKubernetesCredentialsProviderSecret(t, namespace, mySeedJob)
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
|
||||
verifyJenkinsMasterPodAttributes(t, jenkins)
|
||||
@@ -49,9 +55,8 @@ func TestConfiguration(t *testing.T) {
|
||||
|
||||
// user
|
||||
waitForJenkinsUserConfigurationToComplete(t, jenkins)
|
||||
verifyJenkinsSeedJobs(t, client, jenkins)
|
||||
verifyUserConfiguration(t, client, numberOfExecutors, systemMessage)
|
||||
verifyIfJenkinsCredentialExists(t, client, jenkinsCredentialName)
|
||||
verifyJenkinsSeedJobs(t, client, []seedJobConfig{mySeedJob})
|
||||
}
|
||||
|
||||
func createUserConfigurationSecret(t *testing.T, jenkinsCRName string, namespace string, systemMessageEnvName, systemMessage string) {
|
||||
@@ -71,30 +76,6 @@ func createUserConfigurationSecret(t *testing.T, jenkinsCRName string, namespace
|
||||
}
|
||||
}
|
||||
|
||||
func createKubernetesCredentialsProviderSecret(t *testing.T, namespace, name string) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
"jenkins.io/credentials-description": "credentials from Kubernetes",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"jenkins.io/credentials-type": "usernamePassword",
|
||||
},
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Secret for Kubernetes credentials provider plugin %+v", *secret)
|
||||
if err := framework.Global.Client.Create(context.TODO(), secret, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func createUserConfigurationConfigMap(t *testing.T, jenkinsCRName string, namespace string, numberOfExecutors int, systemMessage string) {
|
||||
userConfiguration := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -223,43 +204,6 @@ func isPluginValid(plugins *gojenkins.Plugins, requiredPlugin plugins.Plugin) (*
|
||||
return p, requiredPlugin.Version == p.Version
|
||||
}
|
||||
|
||||
func verifyJenkinsSeedJobs(t *testing.T, client jenkinsclient.Jenkins, jenkins *v1alpha1.Jenkins) {
|
||||
t.Logf("Attempting to get configure seed job status '%v'", seedjobs.ConfigureSeedJobsName)
|
||||
|
||||
configureSeedJobs, err := client.GetJob(seedjobs.ConfigureSeedJobsName)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, configureSeedJobs)
|
||||
build, err := configureSeedJobs.GetLastSuccessfulBuild()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, build)
|
||||
|
||||
seedJobName := "jenkins-operator-configure-seed-job"
|
||||
t.Logf("Attempting to verify if seed job has been created '%v'", seedJobName)
|
||||
seedJob, err := client.GetJob(seedJobName)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, seedJob)
|
||||
|
||||
build, err = seedJob.GetLastSuccessfulBuild()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, build)
|
||||
|
||||
err = framework.Global.Client.Get(context.TODO(), types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name}, jenkins)
|
||||
assert.NoError(t, err, "couldn't get jenkins custom resource")
|
||||
assert.NotNil(t, jenkins.Status.Builds)
|
||||
assert.NotEmpty(t, jenkins.Status.Builds)
|
||||
|
||||
jobCreatedByDSLPluginName := "build-jenkins-operator"
|
||||
err = wait.Poll(time.Second*10, time.Minute*2, func() (bool, error) {
|
||||
t.Logf("Attempting to verify if job '%s' has been created ", jobCreatedByDSLPluginName)
|
||||
seedJob, err := client.GetJob(jobCreatedByDSLPluginName)
|
||||
if err != nil || seedJob == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func verifyUserConfiguration(t *testing.T, jenkinsClient jenkinsclient.Jenkins, amountOfExecutors int, systemMessage string) {
|
||||
checkConfigurationViaGroovyScript := fmt.Sprintf(`
|
||||
if (!new Integer(%d).equals(Jenkins.instance.numExecutors)) {
|
||||
@@ -275,32 +219,3 @@ if (!"%s".equals(Jenkins.instance.systemMessage)) {
|
||||
logs, err = jenkinsClient.ExecuteScript(checkConfigurationAsCode)
|
||||
assert.NoError(t, err, logs)
|
||||
}
|
||||
|
||||
func verifyIfJenkinsCredentialExists(t *testing.T, jenkinsClient jenkinsclient.Jenkins, credentialName string) {
|
||||
groovyScriptFmt := `import com.cloudbees.plugins.credentials.Credentials
|
||||
|
||||
Set<Credentials> allCredentials = new HashSet<Credentials>();
|
||||
|
||||
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
|
||||
com.cloudbees.plugins.credentials.Credentials.class
|
||||
);
|
||||
|
||||
allCredentials.addAll(creds)
|
||||
|
||||
Jenkins.instance.getAllItems(com.cloudbees.hudson.plugins.folder.Folder.class).each{ f ->
|
||||
creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
|
||||
com.cloudbees.plugins.credentials.Credentials.class, f)
|
||||
allCredentials.addAll(creds)
|
||||
}
|
||||
|
||||
def found = false
|
||||
for (c in allCredentials) {
|
||||
if("%s".equals(c.id)) found = true
|
||||
}
|
||||
if(!found) {
|
||||
throw new Exception("Expected credential not found")
|
||||
}`
|
||||
groovyScript := fmt.Sprintf(groovyScriptFmt, credentialName)
|
||||
logs, err := jenkinsClient.ExecuteScript(groovyScript)
|
||||
assert.NoError(t, err, logs)
|
||||
}
|
||||
|
||||
+7
-11
@@ -58,7 +58,12 @@ func createJenkinsAPIClient(jenkins *v1alpha1.Jenkins) (jenkinsclient.Jenkins, e
|
||||
)
|
||||
}
|
||||
|
||||
func createJenkinsCR(t *testing.T, name, namespace string) *v1alpha1.Jenkins {
|
||||
func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha1.SeedJob) *v1alpha1.Jenkins {
|
||||
var seedJobs []v1alpha1.SeedJob
|
||||
if seedJob != nil {
|
||||
seedJobs = append(seedJobs, *seedJob...)
|
||||
}
|
||||
|
||||
jenkins := &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
@@ -80,16 +85,7 @@ func createJenkinsCR(t *testing.T, name, namespace string) *v1alpha1.Jenkins {
|
||||
},
|
||||
},
|
||||
},
|
||||
//TODO(bantoniak) add seed job with private key
|
||||
SeedJobs: []v1alpha1.SeedJob{
|
||||
{
|
||||
ID: "jenkins-operator",
|
||||
Targets: "cicd/jobs/*.jenkins",
|
||||
Description: "Jenkins Operator repository",
|
||||
RepositoryBranch: "master",
|
||||
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||
},
|
||||
},
|
||||
SeedJobs: seedJobs,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis"
|
||||
@@ -14,10 +15,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
jenkinsOperatorDeploymentName = constants.OperatorName
|
||||
jenkinsOperatorDeploymentName = constants.OperatorName
|
||||
seedJobConfigurationParameterName = "seed-job-config"
|
||||
)
|
||||
|
||||
var (
|
||||
seedJobConfigurationFile *string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
seedJobConfigurationFile = flag.String(seedJobConfigurationParameterName, "", "path to seed job config")
|
||||
f.MainEntry(m)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestJenkinsMasterPodRestart(t *testing.T) {
|
||||
// Deletes test namespace
|
||||
defer ctx.Cleanup()
|
||||
|
||||
jenkins := createJenkinsCR(t, "e2e", namespace)
|
||||
jenkins := createJenkinsCR(t, "e2e", namespace, nil)
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
restartJenkinsMasterPod(t, jenkins)
|
||||
waitForRecreateJenkinsMasterPod(t, jenkins)
|
||||
@@ -37,7 +37,7 @@ func TestSafeRestart(t *testing.T) {
|
||||
|
||||
jenkinsCRName := "e2e"
|
||||
configureAuthorizationToUnSecure(t, jenkinsCRName, namespace)
|
||||
jenkins := createJenkinsCR(t, jenkinsCRName, namespace)
|
||||
jenkins := createJenkinsCR(t, jenkinsCRName, namespace, nil)
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
waitForJenkinsUserConfigurationToComplete(t, jenkins)
|
||||
jenkinsClient := verifyJenkinsAPIConnection(t, jenkins)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/internal/try"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/seedjobs"
|
||||
|
||||
framework "github.com/operator-framework/operator-sdk/pkg/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type seedJobConfig struct {
|
||||
v1alpha1.SeedJob
|
||||
JobNames []string `json:"jobNames,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PrivateKey string `json:"privateKey,omitempty"`
|
||||
}
|
||||
|
||||
type seedJobsConfig struct {
|
||||
SeedJobs []seedJobConfig `json:"seedJobs,omitempty"`
|
||||
}
|
||||
|
||||
func TestSeedJobs(t *testing.T) {
|
||||
t.Parallel()
|
||||
if seedJobConfigurationFile == nil || len(*seedJobConfigurationFile) == 0 {
|
||||
t.Skipf("Skipping test because flag '%+v' is not set", seedJobConfigurationFile)
|
||||
}
|
||||
seedJobsConfig := loadSeedJobsConfig(t)
|
||||
namespace, ctx := setupTest(t)
|
||||
// Deletes test namespace
|
||||
defer ctx.Cleanup()
|
||||
|
||||
jenkinsCRName := "e2e"
|
||||
var seedJobs []v1alpha1.SeedJob
|
||||
|
||||
// base
|
||||
for _, seedJobConfig := range seedJobsConfig.SeedJobs {
|
||||
createKubernetesCredentialsProviderSecret(t, namespace, seedJobConfig)
|
||||
seedJobs = append(seedJobs, seedJobConfig.SeedJob)
|
||||
}
|
||||
jenkins := createJenkinsCR(t, jenkinsCRName, namespace, &seedJobs)
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
|
||||
verifyJenkinsMasterPodAttributes(t, jenkins)
|
||||
client := verifyJenkinsAPIConnection(t, jenkins)
|
||||
verifyPlugins(t, client, jenkins)
|
||||
|
||||
// user
|
||||
waitForJenkinsUserConfigurationToComplete(t, jenkins)
|
||||
verifyJenkinsSeedJobs(t, client, seedJobsConfig.SeedJobs)
|
||||
}
|
||||
|
||||
func loadSeedJobsConfig(t *testing.T) seedJobsConfig {
|
||||
jsonFile, err := os.Open(*seedJobConfigurationFile)
|
||||
assert.NoError(t, err)
|
||||
defer func() { _ = jsonFile.Close() }()
|
||||
|
||||
byteValue, err := ioutil.ReadAll(jsonFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var result seedJobsConfig
|
||||
err = json.Unmarshal([]byte(byteValue), &result)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, result.SeedJobs)
|
||||
return result
|
||||
}
|
||||
|
||||
func createKubernetesCredentialsProviderSecret(t *testing.T, namespace string, config seedJobConfig) {
|
||||
if config.JenkinsCredentialType == v1alpha1.NoJenkinsCredentialCredentialType {
|
||||
return
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: config.CredentialID,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
"jenkins.io/credentials-description": "credentials from Kubernetes " + config.ID,
|
||||
},
|
||||
Labels: map[string]string{
|
||||
seedjobs.JenkinsCredentialTypeLabelName: string(config.CredentialID),
|
||||
},
|
||||
},
|
||||
StringData: map[string]string{
|
||||
seedjobs.UsernameSecretKey: config.Username,
|
||||
seedjobs.PasswordSecretKey: config.Password,
|
||||
seedjobs.PrivateKeySecretKey: config.PrivateKey,
|
||||
},
|
||||
}
|
||||
|
||||
err := framework.Global.Client.Create(context.TODO(), secret, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func verifyJenkinsSeedJobs(t *testing.T, jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJobConfig) {
|
||||
var err error
|
||||
for _, seedJob := range seedJobs {
|
||||
if seedJob.JenkinsCredentialType == v1alpha1.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha1.UsernamePasswordCredentialType {
|
||||
err = verifyIfJenkinsCredentialExists(jenkinsClient, seedJob.CredentialID)
|
||||
assert.NoErrorf(t, err, "Jenkins credential '%s' not created for seed job ID '%s'", seedJob.CredentialID, seedJob.ID)
|
||||
}
|
||||
|
||||
for _, requireJobName := range seedJob.JobNames {
|
||||
err = try.Until(func() (end bool, err error) {
|
||||
_, err = jenkinsClient.GetJob(requireJobName)
|
||||
return err == nil, err
|
||||
}, time.Second*2, time.Minute*2)
|
||||
assert.NoErrorf(t, err, "Jenkins job '%s' not created by seed job ID '%s'", requireJobName, seedJob.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyIfJenkinsCredentialExists(jenkinsClient jenkinsclient.Jenkins, credentialName string) error {
|
||||
groovyScriptFmt := `import com.cloudbees.plugins.credentials.Credentials
|
||||
|
||||
Set<Credentials> allCredentials = new HashSet<Credentials>();
|
||||
|
||||
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
|
||||
com.cloudbees.plugins.credentials.Credentials.class
|
||||
);
|
||||
|
||||
allCredentials.addAll(creds)
|
||||
|
||||
Jenkins.instance.getAllItems(com.cloudbees.hudson.plugins.folder.Folder.class).each{ f ->
|
||||
creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
|
||||
com.cloudbees.plugins.credentials.Credentials.class, f)
|
||||
allCredentials.addAll(creds)
|
||||
}
|
||||
|
||||
def found = false
|
||||
for (c in allCredentials) {
|
||||
if("%s".equals(c.id)) found = true
|
||||
}
|
||||
if(!found) {
|
||||
throw new Exception("Expected credential not found")
|
||||
}`
|
||||
groovyScript := fmt.Sprintf(groovyScriptFmt, credentialName)
|
||||
_, err := jenkinsClient.ExecuteScript(groovyScript)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user