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:
Andras Szerdahelyi 2022-05-31 07:04:03 -07:00 committed by GitHub
parent 13f871b1e4
commit 46f64fea6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 240 additions and 1 deletions

View File

@ -527,6 +527,7 @@ const (
BasicSSHCredentialType JenkinsCredentialType = "basicSSHUserPrivateKey"
// UsernamePasswordCredentialType define username & password Jenkins credential type
UsernamePasswordCredentialType JenkinsCredentialType = "usernamePassword"
GithubAppCredentialType JenkinsCredentialType = "githubApp"
// ExternalCredentialType defines other credential type
ExternalCredentialType JenkinsCredentialType = "external"
)
@ -536,6 +537,7 @@ var AllowedJenkinsCredentialMap = map[string]string{
string(NoJenkinsCredentialCredentialType): "",
string(BasicSSHCredentialType): "",
string(UsernamePasswordCredentialType): "",
string(GithubAppCredentialType): "",
string(ExternalCredentialType): "",
}

View File

@ -36,6 +36,8 @@ const (
// PrivateKeySecretKey is private key data key in Kubernetes secret used to create Jenkins SSH credential
PrivateKeySecretKey = "privateKey"
AppIDSecretKey = "appId"
// JenkinsCredentialTypeLabelName is label for kubernetes-credentials-provider-plugin which determine Jenkins
// credential type
JenkinsCredentialTypeLabelName = "jenkins.io/credentials-type"

View File

@ -55,7 +55,8 @@ func (s *seedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) ([]string, error)
}
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType ||
seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType {
seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType ||
seedJob.JenkinsCredentialType == v1alpha2.GithubAppCredentialType {
secret := &v1.Secret{}
namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID}
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 {
@ -219,6 +227,26 @@ func validateUsernamePasswordSecret(secret v1.Secret) []string {
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 {
_, err := ssh.ParseRawPrivateKey([]byte(privateKey))
if err != nil {

View File

@ -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'"})
})
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) {
jenkins := v1alpha2.Jenkins{
Spec: v1alpha2.JenkinsSpec{