Seed job SCM may now be configured with github-branch-source/GitHubAppCredentials (#719)
* Now allowing seed jobs to be configured with GithubAppCredential * fmt * Validation for GithubAppCredentials type seed job SCM secret * GithubAppCredentials validation error messages were not referring to actual GithubAppCredentials fields * cleanup
This commit is contained in:
parent
13f871b1e4
commit
46f64fea6a
|
|
@ -527,6 +527,7 @@ const (
|
||||||
BasicSSHCredentialType JenkinsCredentialType = "basicSSHUserPrivateKey"
|
BasicSSHCredentialType JenkinsCredentialType = "basicSSHUserPrivateKey"
|
||||||
// UsernamePasswordCredentialType define username & password Jenkins credential type
|
// UsernamePasswordCredentialType define username & password Jenkins credential type
|
||||||
UsernamePasswordCredentialType JenkinsCredentialType = "usernamePassword"
|
UsernamePasswordCredentialType JenkinsCredentialType = "usernamePassword"
|
||||||
|
GithubAppCredentialType JenkinsCredentialType = "githubApp"
|
||||||
// ExternalCredentialType defines other credential type
|
// ExternalCredentialType defines other credential type
|
||||||
ExternalCredentialType JenkinsCredentialType = "external"
|
ExternalCredentialType JenkinsCredentialType = "external"
|
||||||
)
|
)
|
||||||
|
|
@ -536,6 +537,7 @@ var AllowedJenkinsCredentialMap = map[string]string{
|
||||||
string(NoJenkinsCredentialCredentialType): "",
|
string(NoJenkinsCredentialCredentialType): "",
|
||||||
string(BasicSSHCredentialType): "",
|
string(BasicSSHCredentialType): "",
|
||||||
string(UsernamePasswordCredentialType): "",
|
string(UsernamePasswordCredentialType): "",
|
||||||
|
string(GithubAppCredentialType): "",
|
||||||
string(ExternalCredentialType): "",
|
string(ExternalCredentialType): "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ const (
|
||||||
// PrivateKeySecretKey is private key data key in Kubernetes secret used to create Jenkins SSH credential
|
// PrivateKeySecretKey is private key data key in Kubernetes secret used to create Jenkins SSH credential
|
||||||
PrivateKeySecretKey = "privateKey"
|
PrivateKeySecretKey = "privateKey"
|
||||||
|
|
||||||
|
AppIDSecretKey = "appId"
|
||||||
|
|
||||||
// JenkinsCredentialTypeLabelName is label for kubernetes-credentials-provider-plugin which determine Jenkins
|
// JenkinsCredentialTypeLabelName is label for kubernetes-credentials-provider-plugin which determine Jenkins
|
||||||
// credential type
|
// credential type
|
||||||
JenkinsCredentialTypeLabelName = "jenkins.io/credentials-type"
|
JenkinsCredentialTypeLabelName = "jenkins.io/credentials-type"
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,8 @@ func (s *seedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType ||
|
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType ||
|
||||||
seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType {
|
seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType ||
|
||||||
|
seedJob.JenkinsCredentialType == v1alpha2.GithubAppCredentialType {
|
||||||
secret := &v1.Secret{}
|
secret := &v1.Secret{}
|
||||||
namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID}
|
namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID}
|
||||||
err := s.Client.Get(context.TODO(), namespaceName, secret)
|
err := s.Client.Get(context.TODO(), namespaceName, secret)
|
||||||
|
|
@ -79,6 +80,13 @@ func (s *seedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) ([]string, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if seedJob.JenkinsCredentialType == v1alpha2.GithubAppCredentialType {
|
||||||
|
if msg := validateGithubAppSecret(*secret); len(msg) > 0 {
|
||||||
|
for _, m := range msg {
|
||||||
|
messages = append(messages, fmt.Sprintf("seedJob `%s` %s", seedJob.ID, m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(seedJob.BuildPeriodically) > 0 {
|
if len(seedJob.BuildPeriodically) > 0 {
|
||||||
|
|
@ -219,6 +227,26 @@ func validateUsernamePasswordSecret(secret v1.Secret) []string {
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateGithubAppSecret(secret v1.Secret) []string {
|
||||||
|
var messages []string
|
||||||
|
appid, exists := secret.Data[AppIDSecretKey]
|
||||||
|
if !exists {
|
||||||
|
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", AppIDSecretKey, secret.ObjectMeta.Name))
|
||||||
|
}
|
||||||
|
if len(appid) == 0 {
|
||||||
|
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", AppIDSecretKey, secret.ObjectMeta.Name))
|
||||||
|
}
|
||||||
|
pkey, exists := secret.Data[PrivateKeySecretKey]
|
||||||
|
if !exists {
|
||||||
|
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
|
||||||
|
}
|
||||||
|
if len(pkey) == 0 {
|
||||||
|
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
func validatePrivateKey(privateKey string) error {
|
func validatePrivateKey(privateKey string) error {
|
||||||
_, err := ssh.ParseRawPrivateKey([]byte(privateKey))
|
_, err := ssh.ParseRawPrivateKey([]byte(privateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -676,6 +676,213 @@ func TestValidateSeedJobs(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, result, []string{"seedJob `example` required data 'password' not found in secret 'deploy-keys'", "seedJob `example` required data 'password' is empty in secret 'deploy-keys'"})
|
assert.Equal(t, result, []string{"seedJob `example` required data 'password' not found in secret 'deploy-keys'", "seedJob `example` required data 'password' is empty in secret 'deploy-keys'"})
|
||||||
})
|
})
|
||||||
|
t.Run("Valid with appId and privateKey", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
ObjectMeta: jenkinsObjectMeta,
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "example",
|
||||||
|
CredentialID: "deploy-keys",
|
||||||
|
JenkinsCredentialType: v1alpha2.GithubAppCredentialType,
|
||||||
|
Targets: "cicd/jobs/*.jenkins",
|
||||||
|
RepositoryBranch: "master",
|
||||||
|
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
TypeMeta: secretTypeMeta,
|
||||||
|
ObjectMeta: secretObjectMeta,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
AppIDSecretKey: []byte("some-id"),
|
||||||
|
PrivateKeySecretKey: []byte("some-key"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewClientBuilder().Build()
|
||||||
|
err := fakeClient.Create(context.TODO(), secret)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Client: fakeClient,
|
||||||
|
ClientSet: kubernetes.Clientset{},
|
||||||
|
Notifications: nil,
|
||||||
|
Jenkins: &v1alpha2.Jenkins{},
|
||||||
|
}
|
||||||
|
|
||||||
|
seedJobs := New(nil, config)
|
||||||
|
result, err := seedJobs.ValidateSeedJobs(jenkins)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, result)
|
||||||
|
})
|
||||||
|
t.Run("Invalid with empty app id", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
ObjectMeta: jenkinsObjectMeta,
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "example",
|
||||||
|
CredentialID: "deploy-keys",
|
||||||
|
JenkinsCredentialType: v1alpha2.GithubAppCredentialType,
|
||||||
|
Targets: "cicd/jobs/*.jenkins",
|
||||||
|
RepositoryBranch: "master",
|
||||||
|
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
TypeMeta: secretTypeMeta,
|
||||||
|
ObjectMeta: secretObjectMeta,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
AppIDSecretKey: []byte(""),
|
||||||
|
PrivateKeySecretKey: []byte("some-key"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewClientBuilder().Build()
|
||||||
|
err := fakeClient.Create(context.TODO(), secret)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Client: fakeClient,
|
||||||
|
ClientSet: kubernetes.Clientset{},
|
||||||
|
Notifications: nil,
|
||||||
|
Jenkins: &v1alpha2.Jenkins{},
|
||||||
|
}
|
||||||
|
|
||||||
|
seedJobs := New(nil, config)
|
||||||
|
result, err := seedJobs.ValidateSeedJobs(jenkins)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, result, []string{"seedJob `example` required data 'appId' is empty in secret 'deploy-keys'"})
|
||||||
|
})
|
||||||
|
t.Run("Invalid with empty private key", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
ObjectMeta: jenkinsObjectMeta,
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "example",
|
||||||
|
CredentialID: "deploy-keys",
|
||||||
|
JenkinsCredentialType: v1alpha2.GithubAppCredentialType,
|
||||||
|
Targets: "cicd/jobs/*.jenkins",
|
||||||
|
RepositoryBranch: "master",
|
||||||
|
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
TypeMeta: secretTypeMeta,
|
||||||
|
ObjectMeta: secretObjectMeta,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
AppIDSecretKey: []byte("some-id"),
|
||||||
|
PrivateKeySecretKey: []byte(""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewClientBuilder().Build()
|
||||||
|
err := fakeClient.Create(context.TODO(), secret)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Client: fakeClient,
|
||||||
|
ClientSet: kubernetes.Clientset{},
|
||||||
|
Notifications: nil,
|
||||||
|
Jenkins: &v1alpha2.Jenkins{},
|
||||||
|
}
|
||||||
|
|
||||||
|
seedJobs := New(nil, config)
|
||||||
|
result, err := seedJobs.ValidateSeedJobs(jenkins)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, result, []string{"seedJob `example` required data 'privateKey' is empty in secret 'deploy-keys'"})
|
||||||
|
})
|
||||||
|
t.Run("Invalid without app id", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
ObjectMeta: jenkinsObjectMeta,
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "example",
|
||||||
|
CredentialID: "deploy-keys",
|
||||||
|
JenkinsCredentialType: v1alpha2.GithubAppCredentialType,
|
||||||
|
Targets: "cicd/jobs/*.jenkins",
|
||||||
|
RepositoryBranch: "master",
|
||||||
|
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
TypeMeta: secretTypeMeta,
|
||||||
|
ObjectMeta: secretObjectMeta,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
PrivateKeySecretKey: []byte("some-key"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewClientBuilder().Build()
|
||||||
|
err := fakeClient.Create(context.TODO(), secret)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Client: fakeClient,
|
||||||
|
ClientSet: kubernetes.Clientset{},
|
||||||
|
Notifications: nil,
|
||||||
|
Jenkins: &v1alpha2.Jenkins{},
|
||||||
|
}
|
||||||
|
|
||||||
|
seedJobs := New(nil, config)
|
||||||
|
result, err := seedJobs.ValidateSeedJobs(jenkins)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, result, []string{"seedJob `example` required data 'appId' not found in secret 'deploy-keys'", "seedJob `example` required data 'appId' is empty in secret 'deploy-keys'"})
|
||||||
|
})
|
||||||
|
t.Run("Invalid without private key", func(t *testing.T) {
|
||||||
|
jenkins := v1alpha2.Jenkins{
|
||||||
|
ObjectMeta: jenkinsObjectMeta,
|
||||||
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
SeedJobs: []v1alpha2.SeedJob{
|
||||||
|
{
|
||||||
|
ID: "example",
|
||||||
|
CredentialID: "deploy-keys",
|
||||||
|
JenkinsCredentialType: v1alpha2.GithubAppCredentialType,
|
||||||
|
Targets: "cicd/jobs/*.jenkins",
|
||||||
|
RepositoryBranch: "master",
|
||||||
|
RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
TypeMeta: secretTypeMeta,
|
||||||
|
ObjectMeta: secretObjectMeta,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
AppIDSecretKey: []byte("some-username"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewClientBuilder().Build()
|
||||||
|
err := fakeClient.Create(context.TODO(), secret)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Client: fakeClient,
|
||||||
|
ClientSet: kubernetes.Clientset{},
|
||||||
|
Notifications: nil,
|
||||||
|
Jenkins: &v1alpha2.Jenkins{},
|
||||||
|
}
|
||||||
|
|
||||||
|
seedJobs := New(nil, config)
|
||||||
|
result, err := seedJobs.ValidateSeedJobs(jenkins)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, result, []string{"seedJob `example` required data 'privateKey' not found in secret 'deploy-keys'", "seedJob `example` required data 'privateKey' is empty in secret 'deploy-keys'"})
|
||||||
|
})
|
||||||
t.Run("Invalid with wrong cron spec", func(t *testing.T) {
|
t.Run("Invalid with wrong cron spec", func(t *testing.T) {
|
||||||
jenkins := v1alpha2.Jenkins{
|
jenkins := v1alpha2.Jenkins{
|
||||||
Spec: v1alpha2.JenkinsSpec{
|
Spec: v1alpha2.JenkinsSpec{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue