Remove backup mechanism
This commit is contained in:
parent
ab87922bd1
commit
dd04c0cf5b
|
|
@ -33,49 +33,6 @@
|
|||
pruneopts = "NT"
|
||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d6d8c5e82a2ac7f2d44a9cd37412f72c4c0342b61ed5fa57e0c6c53bf3f1a4cc"
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
packages = [
|
||||
"aws",
|
||||
"aws/awserr",
|
||||
"aws/awsutil",
|
||||
"aws/client",
|
||||
"aws/client/metadata",
|
||||
"aws/corehandlers",
|
||||
"aws/credentials",
|
||||
"aws/credentials/ec2rolecreds",
|
||||
"aws/credentials/endpointcreds",
|
||||
"aws/credentials/processcreds",
|
||||
"aws/credentials/stscreds",
|
||||
"aws/csm",
|
||||
"aws/defaults",
|
||||
"aws/ec2metadata",
|
||||
"aws/endpoints",
|
||||
"aws/request",
|
||||
"aws/session",
|
||||
"aws/signer/v4",
|
||||
"internal/ini",
|
||||
"internal/s3err",
|
||||
"internal/sdkio",
|
||||
"internal/sdkrand",
|
||||
"internal/sdkuri",
|
||||
"internal/shareddefaults",
|
||||
"private/protocol",
|
||||
"private/protocol/eventstream",
|
||||
"private/protocol/eventstream/eventstreamapi",
|
||||
"private/protocol/query",
|
||||
"private/protocol/query/queryutil",
|
||||
"private/protocol/rest",
|
||||
"private/protocol/restxml",
|
||||
"private/protocol/xml/xmlutil",
|
||||
"service/s3",
|
||||
"service/sts",
|
||||
]
|
||||
pruneopts = "NT"
|
||||
revision = "a2484497433aa110263c045df2f1d49336b38cfd"
|
||||
version = "v1.16.22"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8d13c70d5898b091728540686c696baee0d64013b8e43089da80621a49410391"
|
||||
name = "github.com/bndr/gojenkins"
|
||||
|
|
@ -292,13 +249,6 @@
|
|||
revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"
|
||||
version = "v0.3.6"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1234e31f3de67447e344dabcdf72c4588d31b8eed2d28f1889377ec006a086a9"
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
pruneopts = "NT"
|
||||
revision = "c2b33e84"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f5b9328966ccea0970b1d15075698eff0ddb3e75889560aad2e9f76b289b536a"
|
||||
name = "github.com/joho/godotenv"
|
||||
|
|
@ -468,10 +418,7 @@
|
|||
[[projects]]
|
||||
digest = "1:4af061277c04a7660e082acc2020f4c66d2c21dfc62e0242ffa1d2120cdfb4ec"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require",
|
||||
]
|
||||
packages = ["assert"]
|
||||
pruneopts = "NT"
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
version = "v1.2.2"
|
||||
|
|
@ -517,7 +464,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a7fcf4f3e5247a06ad4c28108f0bc1d4ab980a1a0567e7790260cf2d3d77f37d"
|
||||
digest = "1:922c0bb9dc59af35400f6725ed08582f99f710ffc1a1075e8914c73515bb269e"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
|
|
@ -530,7 +477,7 @@
|
|||
"idna",
|
||||
]
|
||||
pruneopts = "NT"
|
||||
revision = "c10e9556a7bc0e7c942242b606f0acf024ad5d6a"
|
||||
revision = "3a22650c66bd7f4fb6d1e8072ffd7b75c8a27898"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
@ -914,10 +861,6 @@
|
|||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/aws/aws-sdk-go/aws",
|
||||
"github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"github.com/aws/aws-sdk-go/aws/session",
|
||||
"github.com/aws/aws-sdk-go/service/s3",
|
||||
"github.com/bndr/gojenkins",
|
||||
"github.com/docker/distribution/reference",
|
||||
"github.com/go-logr/logr",
|
||||
|
|
@ -930,7 +873,6 @@
|
|||
"github.com/operator-framework/operator-sdk/version",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/require",
|
||||
"k8s.io/api/core/v1",
|
||||
"k8s.io/api/rbac/v1",
|
||||
"k8s.io/apimachinery/pkg/api/errors",
|
||||
|
|
|
|||
|
|
@ -12,30 +12,8 @@ import (
|
|||
type JenkinsSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
|
||||
Backup JenkinsBackup `json:"backup,omitempty"`
|
||||
BackupAmazonS3 JenkinsBackupAmazonS3 `json:"backupAmazonS3,omitempty"`
|
||||
Master JenkinsMaster `json:"master,omitempty"`
|
||||
SeedJobs []SeedJob `json:"seedJobs,omitempty"`
|
||||
}
|
||||
|
||||
// JenkinsBackup defines type of Jenkins backup
|
||||
type JenkinsBackup string
|
||||
|
||||
const (
|
||||
// JenkinsBackupTypeNoBackup tells that Jenkins won't backup jobs
|
||||
JenkinsBackupTypeNoBackup = "NoBackup"
|
||||
// JenkinsBackupTypeAmazonS3 tells that Jenkins will backup jobs into AWS S3 bucket
|
||||
JenkinsBackupTypeAmazonS3 = "AmazonS3"
|
||||
)
|
||||
|
||||
// AllowedJenkinsBackups consists allowed Jenkins backup types
|
||||
var AllowedJenkinsBackups = []JenkinsBackup{JenkinsBackupTypeNoBackup, JenkinsBackupTypeAmazonS3}
|
||||
|
||||
// JenkinsBackupAmazonS3 defines backup configuration to AWS S3 bucket
|
||||
type JenkinsBackupAmazonS3 struct {
|
||||
BucketName string `json:"bucketName,omitempty"`
|
||||
BucketPath string `json:"bucketPath,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
Master JenkinsMaster `json:"master,omitempty"`
|
||||
SeedJobs []SeedJob `json:"seedJobs,omitempty"`
|
||||
}
|
||||
|
||||
// JenkinsMaster defines the Jenkins master pod attributes and plugins,
|
||||
|
|
@ -51,7 +29,6 @@ type JenkinsMaster struct {
|
|||
type JenkinsStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
|
||||
BackupRestored bool `json:"backupRestored,omitempty"`
|
||||
BaseConfigurationCompletedTime *metav1.Time `json:"baseConfigurationCompletedTime,omitempty"`
|
||||
UserConfigurationCompletedTime *metav1.Time `json:"userConfigurationCompletedTime,omitempty"`
|
||||
Builds []Build `json:"builds,omitempty"`
|
||||
|
|
|
|||
|
|
@ -77,22 +77,6 @@ func (in *Jenkins) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JenkinsBackupAmazonS3) DeepCopyInto(out *JenkinsBackupAmazonS3) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsBackupAmazonS3.
|
||||
func (in *JenkinsBackupAmazonS3) DeepCopy() *JenkinsBackupAmazonS3 {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JenkinsBackupAmazonS3)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JenkinsList) DeepCopyInto(out *JenkinsList) {
|
||||
*out = *in
|
||||
|
|
@ -168,7 +152,6 @@ func (in *JenkinsMaster) DeepCopy() *JenkinsMaster {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) {
|
||||
*out = *in
|
||||
out.BackupAmazonS3 = in.BackupAmazonS3
|
||||
in.Master.DeepCopyInto(&out.Master)
|
||||
if in.SeedJobs != nil {
|
||||
in, out := &in.SeedJobs, &out.SeedJobs
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
k8s "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// AmazonS3Backup is a backup strategy where backup is stored in AWS S3 bucket
|
||||
// credentials required to make calls to AWS API are provided by user in backup credentials Kubernetes secret
|
||||
type AmazonS3Backup struct{}
|
||||
|
||||
// GetRestoreJobXML returns Jenkins restore backup job config XML
|
||||
func (b *AmazonS3Backup) GetRestoreJobXML(jenkins v1alpha1.Jenkins) (string, error) {
|
||||
return `<?xml version='1.1' encoding='UTF-8'?>
|
||||
<flow-definition plugin="workflow-job@2.31">
|
||||
<actions/>
|
||||
<description></description>
|
||||
<keepDependencies>false</keepDependencies>
|
||||
<properties>
|
||||
<org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty/>
|
||||
<org.jenkinsci.plugins.workflow.job.properties.DisableResumeJobProperty/>
|
||||
</properties>
|
||||
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.61.1">
|
||||
<script>import com.amazonaws.auth.PropertiesFileCredentialsProvider
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder
|
||||
import com.amazonaws.services.s3.model.AmazonS3Exception
|
||||
import com.amazonaws.services.s3.model.S3Object
|
||||
|
||||
node('master') {
|
||||
def accessKeyFilePath = "` + resources.JenkinsBackupCredentialsVolumePath + `/` + constants.BackupAmazonS3SecretAccessKey + `"
|
||||
def secretKeyFilePath = "` + resources.JenkinsBackupCredentialsVolumePath + `/` + constants.BackupAmazonS3SecretSecretKey + `"
|
||||
def credentialsFileName = "backup-credentials"
|
||||
def bucketName = "` + jenkins.Spec.BackupAmazonS3.BucketName + `"
|
||||
def bucketKey = "` + jenkins.Spec.BackupAmazonS3.BucketPath + `"
|
||||
def region = "` + jenkins.Spec.BackupAmazonS3.Region + `"
|
||||
def latestBackupFile = "` + constants.BackupLatestFileName + `"
|
||||
|
||||
def jenkinsHome = env.JENKINS_HOME
|
||||
def latestBackupKey = "${bucketKey}/${latestBackupFile}"
|
||||
def tmpBackupPath = "/tmp/restore.tar.gz"
|
||||
boolean backupExists = true
|
||||
|
||||
def accessKey = new java.io.File(accessKeyFilePath).text
|
||||
def secretKey = new java.io.File(secretKeyFilePath).text
|
||||
sh "touch ${env.WORKSPACE}/${credentialsFileName}"
|
||||
new java.io.File("${env.WORKSPACE}/${credentialsFileName}").write("accessKey=${accessKey}\nsecretKey=${secretKey}\n")
|
||||
|
||||
stage('Check if backup exists') {
|
||||
def s3 = AmazonS3ClientBuilder
|
||||
.standard()
|
||||
.withCredentials(new PropertiesFileCredentialsProvider("${env.WORKSPACE}/${credentialsFileName}"))
|
||||
.withRegion(region)
|
||||
.build()
|
||||
try {
|
||||
println s3.getObjectMetadata(bucketName, latestBackupKey)
|
||||
} catch (AmazonS3Exception e) {
|
||||
if (e.getStatusCode() == 404) {
|
||||
println "There is no backup ${bucketName}/${latestBackupKey}"
|
||||
backupExists = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (backupExists) {
|
||||
stage('Download backup') {
|
||||
def s3 = AmazonS3ClientBuilder
|
||||
.standard()
|
||||
.withCredentials(new PropertiesFileCredentialsProvider("${env.WORKSPACE}/${credentialsFileName}"))
|
||||
.withRegion(region)
|
||||
.build()
|
||||
S3Object backup = s3.getObject(bucketName, latestBackupKey)
|
||||
java.nio.file.Files.copy(
|
||||
backup.getObjectContent(),
|
||||
new java.io.File(tmpBackupPath).toPath(),
|
||||
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
stage('Unpack backup') {
|
||||
sh "tar -C ${jenkinsHome} -zxf ${tmpBackupPath}"
|
||||
}
|
||||
|
||||
stage('Reload Jenkins') {
|
||||
jenkins.model.Jenkins.getInstance().reload()
|
||||
}
|
||||
|
||||
sh "rm ${tmpBackupPath}"
|
||||
sh "rm ${env.WORKSPACE}/${credentialsFileName}"
|
||||
}
|
||||
}</script>
|
||||
<sandbox>false</sandbox>
|
||||
</definition>
|
||||
<triggers/>
|
||||
<disabled>false</disabled>
|
||||
</flow-definition>`, nil
|
||||
}
|
||||
|
||||
// GetBackupJobXML returns Jenkins backup job config XML
|
||||
func (b *AmazonS3Backup) GetBackupJobXML(jenkins v1alpha1.Jenkins) (string, error) {
|
||||
return `<?xml version='1.1' encoding='UTF-8'?>
|
||||
<flow-definition plugin="workflow-job@2.31">
|
||||
<actions/>
|
||||
<description></description>
|
||||
<keepDependencies>false</keepDependencies>
|
||||
<properties>
|
||||
<org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty/>
|
||||
<org.jenkinsci.plugins.workflow.job.properties.DisableResumeJobProperty/>
|
||||
<org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
|
||||
<triggers>
|
||||
<hudson.triggers.TimerTrigger>
|
||||
<spec>H/60 * * * *</spec>
|
||||
</hudson.triggers.TimerTrigger>
|
||||
</triggers>
|
||||
</org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
|
||||
</properties>
|
||||
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.61">
|
||||
<script>import com.amazonaws.auth.PropertiesFileCredentialsProvider
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder
|
||||
|
||||
import java.io.File
|
||||
|
||||
node('master') {
|
||||
def accessKeyFilePath = "` + resources.JenkinsBackupCredentialsVolumePath + `/` + constants.BackupAmazonS3SecretAccessKey + `"
|
||||
def secretKeyFilePath = "` + resources.JenkinsBackupCredentialsVolumePath + `/` + constants.BackupAmazonS3SecretSecretKey + `"
|
||||
def credentialsFileName = "backup-credentials"
|
||||
def bucketName = "` + jenkins.Spec.BackupAmazonS3.BucketName + `"
|
||||
def bucketKey = "` + jenkins.Spec.BackupAmazonS3.BucketPath + `"
|
||||
def region = "` + jenkins.Spec.BackupAmazonS3.Region + `"
|
||||
def latestBackupFile = "` + constants.BackupLatestFileName + `"
|
||||
|
||||
def jenkinsHome = env.JENKINS_HOME
|
||||
def backupTime = sh(script: "date '+%Y-%m-%d-%H-%M'", returnStdout: true).trim()
|
||||
def tmpBackupPath = "/tmp/backup.tar.gz"
|
||||
|
||||
def backupKey = "${bucketKey}/build-history-${backupTime}.tar.gz"
|
||||
def latestBackupKey = "${bucketKey}/${latestBackupFile}"
|
||||
|
||||
def accessKey = new java.io.File(accessKeyFilePath).text
|
||||
def secretKey = new java.io.File(secretKeyFilePath).text
|
||||
sh "touch ${env.WORKSPACE}/${credentialsFileName}"
|
||||
new java.io.File("${env.WORKSPACE}/${credentialsFileName}").write("accessKey=${accessKey}\nsecretKey=${secretKey}\n")
|
||||
|
||||
stage('Create backup archive') {
|
||||
println "Creating backup archive to ${tmpBackupPath}"
|
||||
sh "tar -C ${jenkinsHome} -z --exclude jobs/*/config.xml --exclude jobs/*/workspace* --exclude jobs/*/simulation.log -c config-history jobs -f ${tmpBackupPath}"
|
||||
}
|
||||
|
||||
stage('Upload backup') {
|
||||
def s3 = AmazonS3ClientBuilder
|
||||
.standard()
|
||||
.withCredentials(new PropertiesFileCredentialsProvider("${env.WORKSPACE}/${credentialsFileName}"))
|
||||
.withRegion(region)
|
||||
.build()
|
||||
println "Uploading backup to ${bucketName}/${backupKey}"
|
||||
s3.putObject(bucketName, backupKey, new File(tmpBackupPath))
|
||||
println s3.getObjectMetadata(bucketName, backupKey)
|
||||
}
|
||||
|
||||
stage('Copy backup') {
|
||||
def s3 = AmazonS3ClientBuilder
|
||||
.standard()
|
||||
.withCredentials(new PropertiesFileCredentialsProvider("${env.WORKSPACE}/${credentialsFileName}"))
|
||||
.withRegion(region)
|
||||
.build()
|
||||
println "Coping backup ${bucketName}${backupKey} to ${bucketName}/${latestBackupKey}"
|
||||
s3.copyObject(bucketName, backupKey, bucketName, latestBackupKey)
|
||||
println s3.getObjectMetadata(bucketName, latestBackupKey)
|
||||
}
|
||||
|
||||
sh "rm ${tmpBackupPath}"
|
||||
sh "rm ${env.WORKSPACE}/${credentialsFileName}"
|
||||
}</script>
|
||||
<sandbox>false</sandbox>
|
||||
</definition>
|
||||
<triggers/>
|
||||
<disabled>false</disabled>
|
||||
</flow-definition>`, nil
|
||||
}
|
||||
|
||||
// IsConfigurationValidForBasePhase validates if user provided valid configuration of backup for base phase
|
||||
func (b *AmazonS3Backup) IsConfigurationValidForBasePhase(jenkins v1alpha1.Jenkins, logger logr.Logger) bool {
|
||||
if len(jenkins.Spec.BackupAmazonS3.BucketName) == 0 {
|
||||
logger.V(log.VWarn).Info("Bucket name not set in 'spec.backupAmazonS3.bucketName'")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(jenkins.Spec.BackupAmazonS3.BucketPath) == 0 {
|
||||
logger.V(log.VWarn).Info("Bucket path not set in 'spec.backupAmazonS3.bucketPath'")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(jenkins.Spec.BackupAmazonS3.Region) == 0 {
|
||||
logger.V(log.VWarn).Info("Region not set in 'spec.backupAmazonS3.region'")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsConfigurationValidForUserPhase validates if user provided valid configuration of backup for user phase
|
||||
func (b *AmazonS3Backup) IsConfigurationValidForUserPhase(k8sClient k8s.Client, jenkins v1alpha1.Jenkins, logger logr.Logger) (bool, error) {
|
||||
backupSecretName := resources.GetBackupCredentialsSecretName(&jenkins)
|
||||
backupSecret := &corev1.Secret{}
|
||||
err := k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: jenkins.Namespace, Name: backupSecretName}, backupSecret)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(backupSecret.Data[constants.BackupAmazonS3SecretSecretKey]) == 0 {
|
||||
logger.V(log.VWarn).Info(fmt.Sprintf("Secret '%s' doesn't contains key: %s", backupSecretName, constants.BackupAmazonS3SecretSecretKey))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if len(backupSecret.Data[constants.BackupAmazonS3SecretAccessKey]) == 0 {
|
||||
logger.V(log.VWarn).Info(fmt.Sprintf("Secret '%s' doesn't contains key: %s", backupSecretName, constants.BackupAmazonS3SecretAccessKey))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetRequiredPlugins returns all required Jenkins plugins by this backup strategy
|
||||
func (b *AmazonS3Backup) GetRequiredPlugins() map[string][]plugins.Plugin {
|
||||
return map[string][]plugins.Plugin{
|
||||
"aws-java-sdk:1.11.457": {
|
||||
plugins.Must(plugins.New(plugins.ApacheComponentsClientPlugin)),
|
||||
plugins.Must(plugins.New(plugins.Jackson2ADIPlugin)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestAmazonS3Backup_IsConfigurationValidForBasePhase(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jenkins v1alpha1.Jenkins
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
jenkins: v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
BackupAmazonS3: v1alpha1.JenkinsBackupAmazonS3{
|
||||
BucketName: "some-value",
|
||||
BucketPath: "some-value",
|
||||
Region: "some-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "fail, no bucket name",
|
||||
jenkins: v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
BackupAmazonS3: v1alpha1.JenkinsBackupAmazonS3{
|
||||
BucketName: "",
|
||||
BucketPath: "some-value",
|
||||
Region: "some-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "fail, no bucket path",
|
||||
jenkins: v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
BackupAmazonS3: v1alpha1.JenkinsBackupAmazonS3{
|
||||
BucketName: "some-value",
|
||||
BucketPath: "",
|
||||
Region: "some-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "fail, no region",
|
||||
jenkins: v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
BackupAmazonS3: v1alpha1.JenkinsBackupAmazonS3{
|
||||
BucketName: "some-value",
|
||||
BucketPath: "some-value",
|
||||
Region: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &AmazonS3Backup{}
|
||||
got := r.IsConfigurationValidForBasePhase(tt.jenkins, logf.ZapLogger(false))
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAmazonS3Backup_IsConfigurationValidForUserPhase(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jenkins *v1alpha1.Jenkins
|
||||
secret *corev1.Secret
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
},
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-operator-backup-credentials-jenkins-cr-name"},
|
||||
Data: map[string][]byte{
|
||||
constants.BackupAmazonS3SecretSecretKey: []byte("some-value"),
|
||||
constants.BackupAmazonS3SecretAccessKey: []byte("some-value"),
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "fail, no secret",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
},
|
||||
want: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "fail, no secret key in secret",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
},
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-operator-backup-credentials-jenkins-cr-name"},
|
||||
Data: map[string][]byte{
|
||||
constants.BackupAmazonS3SecretSecretKey: []byte(""),
|
||||
constants.BackupAmazonS3SecretAccessKey: []byte("some-value"),
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "fail, no access key in secret",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
},
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-operator-backup-credentials-jenkins-cr-name"},
|
||||
Data: map[string][]byte{
|
||||
constants.BackupAmazonS3SecretSecretKey: []byte("some-value"),
|
||||
constants.BackupAmazonS3SecretAccessKey: []byte(""),
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
k8sClient := fake.NewFakeClient()
|
||||
logger := logf.ZapLogger(false)
|
||||
b := &AmazonS3Backup{}
|
||||
if tt.secret != nil {
|
||||
e := k8sClient.Create(context.TODO(), tt.secret)
|
||||
assert.NoError(t, e)
|
||||
}
|
||||
got, err := b.IsConfigurationValidForUserPhase(k8sClient, *tt.jenkins, logger)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup/aws"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup/nobackup"
|
||||
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/constants"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/pkg/errors"
|
||||
k8s "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const (
|
||||
restoreJobName = constants.OperatorName + "-restore-backup"
|
||||
)
|
||||
|
||||
// Provider defines API of backup providers
|
||||
type Provider interface {
|
||||
GetRestoreJobXML(jenkins v1alpha1.Jenkins) (string, error)
|
||||
GetBackupJobXML(jenkins v1alpha1.Jenkins) (string, error)
|
||||
IsConfigurationValidForBasePhase(jenkins v1alpha1.Jenkins, logger logr.Logger) bool
|
||||
IsConfigurationValidForUserPhase(k8sClient k8s.Client, jenkins v1alpha1.Jenkins, logger logr.Logger) (bool, error)
|
||||
GetRequiredPlugins() map[string][]plugins.Plugin
|
||||
}
|
||||
|
||||
// Backup defines backup manager which is responsible of backup of jobs history
|
||||
type Backup struct {
|
||||
jenkins *v1alpha1.Jenkins
|
||||
k8sClient k8s.Client
|
||||
logger logr.Logger
|
||||
jenkinsClient jenkinsclient.Jenkins
|
||||
}
|
||||
|
||||
// New returns instance of backup manager
|
||||
func New(jenkins *v1alpha1.Jenkins, k8sClient k8s.Client, logger logr.Logger, jenkinsClient jenkinsclient.Jenkins) *Backup {
|
||||
return &Backup{jenkins: jenkins, k8sClient: k8sClient, logger: logger, jenkinsClient: jenkinsClient}
|
||||
}
|
||||
|
||||
// EnsureRestoreJob creates and updates Jenkins job used to restore backup
|
||||
func (b *Backup) EnsureRestoreJob() error {
|
||||
if b.jenkins.Status.UserConfigurationCompletedTime == nil {
|
||||
provider, err := GetBackupProvider(b.jenkins.Spec.Backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
restoreJobXML, err := provider.GetRestoreJobXML(*b.jenkins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, created, err := b.jenkinsClient.CreateOrUpdateJob(restoreJobXML, restoreJobName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if created {
|
||||
b.logger.Info(fmt.Sprintf("'%s' job has been created", restoreJobName))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreBackup restores backup
|
||||
func (b *Backup) RestoreBackup() (reconcile.Result, error) {
|
||||
if !b.jenkins.Status.BackupRestored && b.jenkins.Status.UserConfigurationCompletedTime == nil {
|
||||
jobsClient := jobs.New(b.jenkinsClient, b.k8sClient, b.logger)
|
||||
|
||||
hash := "hash-restore" // it can be hardcoded because restore job can be run only once
|
||||
done, err := jobsClient.EnsureBuildJob(restoreJobName, hash, map[string]string{}, b.jenkins, true)
|
||||
if err != nil {
|
||||
// build failed and can be recovered - retry build and requeue reconciliation loop with timeout
|
||||
if err == jobs.ErrorBuildFailed {
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil
|
||||
}
|
||||
// build failed and cannot be recovered
|
||||
if err == jobs.ErrorUnrecoverableBuildFailed {
|
||||
b.logger.Info(fmt.Sprintf("Restore backup can not be performed. Please check backup configuration in CR and credentials in secret '%s'.", resources.GetBackupCredentialsSecretName(b.jenkins)))
|
||||
b.logger.Info(fmt.Sprintf("You can also check '%s' job logs in Jenkins", constants.BackupJobName))
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
// unexpected error - requeue reconciliation loop
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
// build not finished yet - requeue reconciliation loop with timeout
|
||||
if !done {
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil
|
||||
}
|
||||
|
||||
b.jenkins.Status.BackupRestored = true
|
||||
err = b.k8sClient.Update(context.TODO(), b.jenkins)
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
// EnsureBackupJob creates and updates Jenkins job used to backup
|
||||
func (b *Backup) EnsureBackupJob() error {
|
||||
provider, err := GetBackupProvider(b.jenkins.Spec.Backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupJobXML, err := provider.GetBackupJobXML(*b.jenkins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, created, err := b.jenkinsClient.CreateOrUpdateJob(backupJobXML, constants.BackupJobName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if created {
|
||||
b.logger.Info(fmt.Sprintf("'%s' job has been created", constants.BackupJobName))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBackupProvider returns backup provider by type
|
||||
func GetBackupProvider(backupType v1alpha1.JenkinsBackup) (Provider, error) {
|
||||
switch backupType {
|
||||
case v1alpha1.JenkinsBackupTypeNoBackup:
|
||||
return &nobackup.NoBackup{}, nil
|
||||
case v1alpha1.JenkinsBackupTypeAmazonS3:
|
||||
return &aws.AmazonS3Backup{}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("Invalid BackupManager type '%s'", backupType)
|
||||
}
|
||||
}
|
||||
|
||||
// GetPluginsRequiredByAllBackupProviders returns plugins required by all backup providers
|
||||
func GetPluginsRequiredByAllBackupProviders() map[string][]plugins.Plugin {
|
||||
allPlugins := map[string][]plugins.Plugin{}
|
||||
for _, provider := range getAllProviders() {
|
||||
for key, value := range provider.GetRequiredPlugins() {
|
||||
allPlugins[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
func getAllProviders() []Provider {
|
||||
return []Provider{
|
||||
&nobackup.NoBackup{}, &aws.AmazonS3Backup{},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package nobackup
|
||||
|
||||
import (
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
k8s "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// NoBackup is a backup strategy where there is no backup
|
||||
type NoBackup struct{}
|
||||
|
||||
var emptyJob = `<?xml version='1.1' encoding='UTF-8'?>
|
||||
<flow-definition plugin="workflow-job@2.31">
|
||||
<actions/>
|
||||
<description></description>
|
||||
<keepDependencies>false</keepDependencies>
|
||||
<properties></properties>
|
||||
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.61">
|
||||
<script></script>
|
||||
<sandbox>false</sandbox>
|
||||
</definition>
|
||||
<triggers/>
|
||||
<disabled>false</disabled>
|
||||
</flow-definition>
|
||||
`
|
||||
|
||||
// GetRestoreJobXML returns Jenkins restore backup job config XML
|
||||
func (b *NoBackup) GetRestoreJobXML(jenkins v1alpha1.Jenkins) (string, error) {
|
||||
return emptyJob, nil
|
||||
}
|
||||
|
||||
// GetBackupJobXML returns Jenkins backup job config XML
|
||||
func (b *NoBackup) GetBackupJobXML(jenkins v1alpha1.Jenkins) (string, error) {
|
||||
return emptyJob, nil
|
||||
}
|
||||
|
||||
// IsConfigurationValidForBasePhase validates if user provided valid configuration of backup for base phase
|
||||
func (b *NoBackup) IsConfigurationValidForBasePhase(jenkins v1alpha1.Jenkins, logger logr.Logger) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsConfigurationValidForUserPhase validates if user provided valid configuration of backup for user phase
|
||||
func (b *NoBackup) IsConfigurationValidForUserPhase(k8sClient k8s.Client, jenkins v1alpha1.Jenkins, logger logr.Logger) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetRequiredPlugins returns all required Jenkins plugins by this backup strategy
|
||||
func (b *NoBackup) GetRequiredPlugins() map[string][]plugins.Plugin {
|
||||
return map[string][]plugins.Plugin{}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup"
|
||||
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/constants"
|
||||
|
|
@ -62,16 +61,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki
|
|||
return reconcile.Result{}, nil, err
|
||||
}
|
||||
|
||||
pluginsRequiredByAllBackupProviders := backup.GetPluginsRequiredByAllBackupProviders()
|
||||
result, err := r.ensurePluginsRequiredByAllBackupProviders(pluginsRequiredByAllBackupProviders)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, nil, err
|
||||
}
|
||||
if result.Requeue {
|
||||
return result, nil, nil
|
||||
}
|
||||
|
||||
result, err = r.ensureJenkinsMasterPod(metaObject)
|
||||
result, err := r.ensureJenkinsMasterPod(metaObject)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, nil, err
|
||||
}
|
||||
|
|
@ -95,7 +85,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenki
|
|||
}
|
||||
r.logger.V(log.VDebug).Info("Jenkins API client set")
|
||||
|
||||
ok, err := r.verifyPlugins(jenkinsClient, plugins.BasePluginsMap, pluginsRequiredByAllBackupProviders)
|
||||
ok, err := r.verifyPlugins(jenkinsClient, plugins.BasePluginsMap)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, nil, err
|
||||
}
|
||||
|
|
@ -144,11 +134,6 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod
|
|||
}
|
||||
r.logger.V(log.VDebug).Info("Service is present")
|
||||
|
||||
if err := r.createBackupCredentialsSecret(metaObject); err != nil {
|
||||
return err
|
||||
}
|
||||
r.logger.V(log.VDebug).Info("Backup credentials secret is present")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -473,23 +458,6 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClien
|
|||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) createBackupCredentialsSecret(meta metav1.ObjectMeta) error {
|
||||
currentSecret := &corev1.Secret{}
|
||||
err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetBackupCredentialsSecretName(r.jenkins), Namespace: r.jenkins.Namespace}, currentSecret)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
return r.k8sClient.Create(context.TODO(), resources.NewBackupCredentialsSecret(r.jenkins))
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
valid := r.verifyLabelsForWatchedResource(currentSecret)
|
||||
if !valid {
|
||||
currentSecret.ObjectMeta.Labels = resources.BuildLabelsForWatchedResources(r.jenkins)
|
||||
return r.k8sClient.Update(context.TODO(), currentSecret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) verifyLabelsForWatchedResource(object metav1.Object) bool {
|
||||
requiredLabels := resources.BuildLabelsForWatchedResources(r.jenkins)
|
||||
for key, value := range requiredLabels {
|
||||
|
|
@ -500,28 +468,3 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyLabelsForWatchedResource(objec
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) ensurePluginsRequiredByAllBackupProviders(requiredPlugins map[string][]plugins.Plugin) (reconcile.Result, error) {
|
||||
copiedPlugins := map[string][]string{}
|
||||
for key, value := range r.jenkins.Spec.Master.Plugins {
|
||||
copiedPlugins[key] = value
|
||||
}
|
||||
for key, value := range requiredPlugins {
|
||||
copiedPlugins[key] = func() []string {
|
||||
var pluginsWithVersion []string
|
||||
for _, plugin := range value {
|
||||
pluginsWithVersion = append(pluginsWithVersion, plugin.String())
|
||||
}
|
||||
return pluginsWithVersion
|
||||
}()
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.jenkins.Spec.Master.Plugins, copiedPlugins) {
|
||||
r.logger.Info("Adding plugins required by backup providers to '.spec.master.plugins'")
|
||||
r.jenkins.Spec.Master.Plugins = copiedPlugins
|
||||
err := r.k8sClient.Update(context.TODO(), r.jenkins)
|
||||
return reconcile.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestReconcileJenkinsBaseConfiguration_ensurePluginsRequiredByAllBackupProviders(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jenkins *v1alpha1.Jenkins
|
||||
requiredPlugins map[string][]plugins.Plugin
|
||||
want reconcile.Result
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy, no required plugins",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Master: v1alpha1.JenkinsMaster{
|
||||
Plugins: map[string][]string{
|
||||
"first-plugin:0.0.1": {"second-plugin:0.0.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: reconcile.Result{Requeue: false},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "happy, required plugins are set",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Master: v1alpha1.JenkinsMaster{
|
||||
Plugins: map[string][]string{
|
||||
"first-plugin:0.0.1": {"second-plugin:0.0.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
requiredPlugins: map[string][]plugins.Plugin{
|
||||
"first-plugin:0.0.1": {plugins.Must(plugins.New("second-plugin:0.0.1"))},
|
||||
},
|
||||
want: reconcile.Result{Requeue: false},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "happy, jenkins CR must be updated",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Master: v1alpha1.JenkinsMaster{
|
||||
Plugins: map[string][]string{
|
||||
"first-plugin:0.0.1": {"second-plugin:0.0.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
requiredPlugins: map[string][]plugins.Plugin{
|
||||
"first-plugin:0.0.1": {plugins.Must(plugins.New("second-plugin:0.0.1"))},
|
||||
"third-plugin:0.0.1": {},
|
||||
},
|
||||
want: reconcile.Result{Requeue: true},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "happy, jenkins CR must be updated",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Master: v1alpha1.JenkinsMaster{
|
||||
Plugins: map[string][]string{
|
||||
"first-plugin:0.0.1": {"second-plugin:0.0.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
requiredPlugins: map[string][]plugins.Plugin{
|
||||
"first-plugin:0.0.1": {plugins.Must(plugins.New("second-plugin:0.0.1"))},
|
||||
"third-plugin:0.0.1": {plugins.Must(plugins.New("fourth-plugin:0.0.1"))},
|
||||
},
|
||||
want: reconcile.Result{Requeue: true},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v1alpha1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
assert.NoError(t, err)
|
||||
r := &ReconcileJenkinsBaseConfiguration{
|
||||
k8sClient: fake.NewFakeClient(),
|
||||
scheme: nil,
|
||||
logger: logf.ZapLogger(false),
|
||||
jenkins: tt.jenkins,
|
||||
local: false,
|
||||
minikube: false,
|
||||
}
|
||||
err = r.k8sClient.Create(context.TODO(), tt.jenkins)
|
||||
assert.NoError(t, err)
|
||||
got, err := r.ensurePluginsRequiredByAllBackupProviders(tt.requiredPlugins)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package resources
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// GetBackupCredentialsSecretName returns name of Kubernetes secret used to store backup credentials
|
||||
func GetBackupCredentialsSecretName(jenkins *v1alpha1.Jenkins) string {
|
||||
return fmt.Sprintf("%s-backup-credentials-%s", constants.OperatorName, jenkins.Name)
|
||||
}
|
||||
|
||||
// NewBackupCredentialsSecret builds the Kubernetes secret used to store backup credentials
|
||||
func NewBackupCredentialsSecret(jenkins *v1alpha1.Jenkins) *corev1.Secret {
|
||||
meta := metav1.ObjectMeta{
|
||||
Name: GetBackupCredentialsSecretName(jenkins),
|
||||
Namespace: jenkins.ObjectMeta.Namespace,
|
||||
Labels: BuildLabelsForWatchedResources(jenkins),
|
||||
}
|
||||
|
||||
return &corev1.Secret{
|
||||
TypeMeta: buildSecretTypeMeta(),
|
||||
ObjectMeta: meta,
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import jenkins.model.JenkinsLocationConfiguration
|
|||
import hudson.model.Node.Mode
|
||||
|
||||
def jenkins = Jenkins.instance
|
||||
//Number of jobs that run simultaneously on master, currently only backup and SeedJob.
|
||||
//Number of jobs that run simultaneously on master, currently only SeedJob.
|
||||
jenkins.setNumExecutors(%d)
|
||||
//Jobs must specify that they want to run on master
|
||||
jenkins.setMode(Mode.EXCLUSIVE)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ const (
|
|||
jenkinsScriptsVolumeName = "scripts"
|
||||
jenkinsScriptsVolumePath = "/var/jenkins/scripts"
|
||||
initScriptName = "init.sh"
|
||||
backupScriptName = "backup.sh"
|
||||
|
||||
jenkinsOperatorCredentialsVolumeName = "operator-credentials"
|
||||
jenkinsOperatorCredentialsVolumePath = "/var/jenkins/operator-credentials"
|
||||
|
|
@ -34,11 +33,6 @@ const (
|
|||
// this scripts are provided by user
|
||||
JenkinsUserConfigurationVolumePath = "/var/jenkins/user-configuration"
|
||||
|
||||
jenkinsBackupCredentialsVolumeName = "backup-credentials"
|
||||
// JenkinsBackupCredentialsVolumePath is a path where are credentials used for backup/restore
|
||||
// credentials are provided by user
|
||||
JenkinsBackupCredentialsVolumePath = "/var/jenkins/backup-credentials"
|
||||
|
||||
httpPortName = "http"
|
||||
slavePortName = "slavelistener"
|
||||
// HTTPPortInt defines Jenkins master HTTP port
|
||||
|
|
@ -84,16 +78,6 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins
|
|||
"bash",
|
||||
fmt.Sprintf("%s/%s", jenkinsScriptsVolumePath, initScriptName),
|
||||
},
|
||||
Lifecycle: &corev1.Lifecycle{
|
||||
PreStop: &corev1.Handler{
|
||||
Exec: &corev1.ExecAction{
|
||||
Command: []string{
|
||||
"bash",
|
||||
fmt.Sprintf("%s/%s", jenkinsScriptsVolumePath, backupScriptName),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
LivenessProbe: &corev1.Probe{
|
||||
Handler: corev1.Handler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
|
|
@ -168,11 +152,6 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins
|
|||
MountPath: jenkinsOperatorCredentialsVolumePath,
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: jenkinsBackupCredentialsVolumeName,
|
||||
MountPath: JenkinsBackupCredentialsVolumePath,
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -231,14 +210,6 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: jenkinsBackupCredentialsVolumeName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: GetBackupCredentialsSecretName(jenkins),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,70 +264,6 @@ echo "Installing plugins - end"
|
|||
/sbin/tini -s -- /usr/local/bin/jenkins.sh
|
||||
`))
|
||||
|
||||
const backupBashFmt = `#!/usr/bin/env bash
|
||||
|
||||
# don't add set -e
|
||||
|
||||
JENKINS_SERVER="http://%s:$(cat %s/%s)@localhost:%d"
|
||||
JOB="%s"
|
||||
JOB_QUERY=/job/${JOB}
|
||||
|
||||
echo 'Starting the build'
|
||||
curl -f -v -X POST "${JENKINS_SERVER}${JOB_QUERY}/build?delay=0sec" || exit -1
|
||||
sleep 3 # give some time for Jenkins to update builds numbers
|
||||
|
||||
BUILD_STATUS_QUERY=/lastBuild/api/json
|
||||
|
||||
CURRENT_BUILD_NUMBER_QUERY=/lastBuild/buildNumber
|
||||
CURRENT_BUILD_JSON=$(curl -s -f "${JENKINS_SERVER}${JOB_QUERY}${CURRENT_BUILD_NUMBER_QUERY}")
|
||||
LAST_STABLE_BUILD_NUMBER_QUERY=/lastStableBuild/buildNumber
|
||||
|
||||
check_build()
|
||||
{
|
||||
GOOD_BUILD="Last build successful. "
|
||||
BAD_BUILD="Last build failed. "
|
||||
JOB_STATUS_JSON=$(curl -s -f "${JENKINS_SERVER}${JOB_QUERY}${BUILD_STATUS_QUERY}")
|
||||
RESULT=$(echo "${JOB_STATUS_JSON}" | sed -n 's/.*"result":\([\"A-Za-z]*\),.*/\1/p')
|
||||
CURRENT_BUILD_NUMBER=${CURRENT_BUILD_JSON}
|
||||
LAST_STABLE_BUILD_JSON=$(curl --silent "${JENKINS_SERVER}${JOB_QUERY}${LAST_STABLE_BUILD_NUMBER_QUERY}")
|
||||
LAST_STABLE_BUILD_NUMBER=${LAST_STABLE_BUILD_JSON}
|
||||
LAST_BUILD_STATUS=${GOOD_BUILD}
|
||||
echo "${LAST_STABLE_BUILD_NUMBER}" | grep "is not available" > /dev/null
|
||||
GREP_RETURN_CODE=$?
|
||||
if [[ ${GREP_RETURN_CODE} -ne 0 ]]
|
||||
then
|
||||
if [[ $(expr ${CURRENT_BUILD_NUMBER} - 1) -gt ${LAST_STABLE_BUILD_NUMBER} ]]
|
||||
then
|
||||
LAST_BUILD_STATUS=${BAD_BUILD}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${RESULT}" = "null" ]]
|
||||
then
|
||||
echo "${LAST_BUILD_STATUS}Building ${JOB} ${CURRENT_BUILD_NUMBER}... last stable was ${LAST_STABLE_BUILD_NUMBER}"
|
||||
elif [[ "${RESULT}" = "\"SUCCESS\"" ]]
|
||||
then
|
||||
echo "${LAST_BUILD_STATUS}${JOB} ${CURRENT_BUILD_NUMBER} completed successfully."
|
||||
exit 0
|
||||
elif [[ "${RESULT}" = "\"FAILURE\"" ]]
|
||||
then
|
||||
LAST_BUILD_STATUS=${BAD_BUILD}
|
||||
echo "${LAST_BUILD_STATUS}${JOB} ${CURRENT_BUILD_NUMBER} failed"
|
||||
exit -1
|
||||
else
|
||||
LAST_BUILD_STATUS=${BAD_BUILD}
|
||||
echo "${LAST_BUILD_STATUS}${JOB} ${CURRENT_BUILD_NUMBER} status unknown - '${RESULT}'"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ true ]]
|
||||
do
|
||||
check_build
|
||||
sleep 1
|
||||
done
|
||||
`
|
||||
|
||||
func buildConfigMapTypeMeta() metav1.TypeMeta {
|
||||
return metav1.TypeMeta{
|
||||
Kind: "ConfigMap",
|
||||
|
|
@ -377,8 +313,6 @@ func NewScriptsConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) (*co
|
|||
Data: map[string]string{
|
||||
initScriptName: *initBashScript,
|
||||
installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath),
|
||||
backupScriptName: fmt.Sprintf(backupBashFmt,
|
||||
OperatorUserName, jenkinsOperatorCredentialsVolumePath, OperatorCredentialsSecretTokenKey, HTTPPortInt, constants.BackupJobName),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,14 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
docker "github.com/docker/distribution/reference"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -38,20 +32,6 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha1.Jenkins)
|
|||
return false, nil
|
||||
}
|
||||
|
||||
valid, err := r.verifyBackup()
|
||||
if !valid || err != nil {
|
||||
return valid, err
|
||||
}
|
||||
|
||||
backupProvider, err := backup.GetBackupProvider(r.jenkins.Spec.Backup)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !backupProvider.IsConfigurationValidForBasePhase(*r.jenkins, r.logger) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
@ -84,39 +64,3 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(pluginsWithVersions
|
|||
|
||||
return valid
|
||||
}
|
||||
|
||||
func (r *ReconcileJenkinsBaseConfiguration) verifyBackup() (bool, error) {
|
||||
if r.jenkins.Spec.Backup == "" {
|
||||
r.logger.V(log.VWarn).Info("Backup strategy not set in 'spec.backup'")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
valid := false
|
||||
for _, backupType := range v1alpha1.AllowedJenkinsBackups {
|
||||
if r.jenkins.Spec.Backup == backupType {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Invalid backup strategy '%s'", r.jenkins.Spec.Backup))
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Allowed backups '%+v'", v1alpha1.AllowedJenkinsBackups))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if r.jenkins.Spec.Backup == v1alpha1.JenkinsBackupTypeNoBackup {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
backupSecretName := resources.GetBackupCredentialsSecretName(r.jenkins)
|
||||
backupSecret := &corev1.Secret{}
|
||||
err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: r.jenkins.Namespace, Name: backupSecretName}, backupSecret)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
r.logger.V(log.VWarn).Info(fmt.Sprintf("Please create secret '%s' in namespace '%s'", backupSecretName, r.jenkins.Namespace))
|
||||
return false, nil
|
||||
} else if err != nil && !errors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
|
|
@ -62,87 +56,3 @@ func TestValidatePlugins(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcileJenkinsBaseConfiguration_verifyBackup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jenkins *v1alpha1.Jenkins
|
||||
secret *corev1.Secret
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy, no backup",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Backup: v1alpha1.JenkinsBackupTypeNoBackup,
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "happy",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Backup: v1alpha1.JenkinsBackupTypeAmazonS3,
|
||||
},
|
||||
},
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-operator-backup-credentials-jenkins-cr-name"},
|
||||
},
|
||||
want: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "fail, no secret",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Backup: v1alpha1.JenkinsBackupTypeAmazonS3,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "fail, empty backup type",
|
||||
jenkins: &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-cr-name"},
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Backup: "",
|
||||
},
|
||||
},
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace-name", Name: "jenkins-operator-backup-credentials-jenkins-cr-name"},
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &ReconcileJenkinsBaseConfiguration{
|
||||
k8sClient: fake.NewFakeClient(),
|
||||
scheme: nil,
|
||||
logger: logf.ZapLogger(false),
|
||||
jenkins: tt.jenkins,
|
||||
local: false,
|
||||
minikube: false,
|
||||
}
|
||||
if tt.secret != nil {
|
||||
e := r.k8sClient.Create(context.TODO(), tt.secret)
|
||||
assert.NoError(t, e)
|
||||
}
|
||||
got, err := r.verifyBackup()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup"
|
||||
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"
|
||||
|
|
@ -41,21 +40,8 @@ func New(k8sClient k8s.Client, jenkinsClient jenkinsclient.Jenkins, logger logr.
|
|||
|
||||
// Reconcile it's a main reconciliation loop for user supplied configuration
|
||||
func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) {
|
||||
backupManager := backup.New(r.jenkins, r.k8sClient, r.logger, r.jenkinsClient)
|
||||
if err := backupManager.EnsureRestoreJob(); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
result, err := backupManager.RestoreBackup()
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
if result.Requeue {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// reconcile seed jobs
|
||||
result, err = r.ensureSeedJobs()
|
||||
result, err := r.ensureSeedJobs()
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
|
@ -71,11 +57,6 @@ func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
err = backupManager.EnsureBackupJob()
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/backup"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
|
|
@ -24,12 +23,7 @@ func (r *ReconcileUserConfiguration) Validate(jenkins *v1alpha1.Jenkins) (bool,
|
|||
return valid, err
|
||||
}
|
||||
|
||||
backupProvider, err := backup.GetBackupProvider(r.jenkins.Spec.Backup)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return backupProvider.IsConfigurationValidForUserPhase(r.k8sClient, *r.jenkins, r.logger)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileUserConfiguration) validateSeedJobs(jenkins *v1alpha1.Jenkins) (bool, error) {
|
||||
|
|
|
|||
|
|
@ -9,14 +9,6 @@ const (
|
|||
SeedJobSuffix = "job-dsl-seed"
|
||||
// DefaultJenkinsMasterImage is the default Jenkins master docker image
|
||||
DefaultJenkinsMasterImage = "jenkins/jenkins:lts"
|
||||
// BackupAmazonS3SecretAccessKey is the Amazon user access key used to Amazon S3 backup
|
||||
BackupAmazonS3SecretAccessKey = "access-key"
|
||||
// BackupAmazonS3SecretSecretKey is the Amazon user secret key used to Amazon S3 backup
|
||||
BackupAmazonS3SecretSecretKey = "secret-key"
|
||||
// BackupJobName is the Jenkins job name used to backup jobs history
|
||||
BackupJobName = OperatorName + "-backup"
|
||||
// UserConfigurationJobName is the Jenkins job name used to configure Jenkins by groovy scripts provided by user
|
||||
UserConfigurationJobName = OperatorName + "-user-configuration"
|
||||
// BackupLatestFileName is the latest backup file name
|
||||
BackupLatestFileName = "build-history-latest.tar.gz"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -212,12 +212,6 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha1.Jenkins, logger logr.Lo
|
|||
changed = true
|
||||
jenkins.Spec.Master.Image = constants.DefaultJenkinsMasterImage
|
||||
}
|
||||
if len(jenkins.Spec.Backup) == 0 {
|
||||
logger.Info("Setting default backup strategy: " + v1alpha1.JenkinsBackupTypeNoBackup)
|
||||
logger.V(log.VWarn).Info("Backup is disable !!! Please configure backup in '.spec.backup'")
|
||||
changed = true
|
||||
jenkins.Spec.Backup = v1alpha1.JenkinsBackupTypeNoBackup
|
||||
}
|
||||
if len(jenkins.Spec.Master.Plugins) == 0 {
|
||||
logger.Info("Setting default base plugins")
|
||||
changed = true
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/bndr/gojenkins"
|
||||
framework "github.com/operator-framework/operator-sdk/pkg/test"
|
||||
assert "github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type amazonS3BackupConfiguration struct {
|
||||
BucketName string `json:"bucketName,omitempty"`
|
||||
BucketPath string `json:"bucketPath,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
AccessKey string `json:"accessKey,omitempty"`
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
}
|
||||
|
||||
func TestAmazonS3Backup(t *testing.T) {
|
||||
t.Parallel()
|
||||
if amazonS3BackupConfigurationFile == nil || len(*amazonS3BackupConfigurationFile) == 0 {
|
||||
t.Skipf("Skipping testing because flag '%s' is not set", amazonS3BackupConfigurationParameterName)
|
||||
}
|
||||
backupConfig := loadAmazonS3BackupConfig(t)
|
||||
|
||||
s3Client := createS3Client(t, backupConfig)
|
||||
deleteAllBackupsInS3(t, backupConfig, s3Client)
|
||||
namespace, ctx := setupTest(t)
|
||||
defer ctx.Cleanup() // Deletes test namespace
|
||||
|
||||
jenkins := createJenkinsCRWithAmazonS3Backup(t, namespace, backupConfig)
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
waitForJenkinsUserConfigurationToComplete(t, jenkins)
|
||||
|
||||
restartJenkinsMasterPod(t, jenkins)
|
||||
waitForRecreateJenkinsMasterPod(t, jenkins)
|
||||
|
||||
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
|
||||
waitForJenkinsUserConfigurationToComplete(t, jenkins)
|
||||
jenkinsClient := verifyJenkinsAPIConnection(t, jenkins)
|
||||
verifyIfBackupAndRestoreWasSuccessfull(t, jenkinsClient, backupConfig, s3Client)
|
||||
}
|
||||
|
||||
func createS3Client(t *testing.T, backupConfig amazonS3BackupConfiguration) *s3.S3 {
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
Region: aws.String(backupConfig.Region),
|
||||
Credentials: credentials.NewStaticCredentials(backupConfig.AccessKey, backupConfig.SecretKey, ""),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
return s3.New(sess)
|
||||
}
|
||||
|
||||
func deleteAllBackupsInS3(t *testing.T, backupConfig amazonS3BackupConfiguration, s3Client *s3.S3) {
|
||||
input := &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(backupConfig.BucketName),
|
||||
Key: aws.String(backupConfig.BucketPath),
|
||||
}
|
||||
|
||||
_, err := s3Client.DeleteObject(input)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func verifyIfBackupAndRestoreWasSuccessfull(t *testing.T, jenkinsClient *gojenkins.Jenkins, backupConfig amazonS3BackupConfiguration, s3Client *s3.S3) {
|
||||
job, err := jenkinsClient.GetJob(constants.UserConfigurationJobName)
|
||||
assert.NoError(t, err)
|
||||
// jenkins runs twice(2) + 1 as next build number
|
||||
assert.Equal(t, int64(3), job.Raw.NextBuildNumber)
|
||||
|
||||
listObjects, err := s3Client.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: aws.String(backupConfig.BucketName),
|
||||
Marker: aws.String(backupConfig.BucketPath),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
t.Logf("Backups in S3:%+v", listObjects.Contents)
|
||||
assert.Equal(t, len(listObjects.Contents), 2)
|
||||
latestBackupFound := false
|
||||
for _, backup := range listObjects.Contents {
|
||||
if *backup.Key == fmt.Sprintf("%s/%s", backupConfig.BucketPath, constants.BackupLatestFileName) {
|
||||
latestBackupFound = true
|
||||
}
|
||||
}
|
||||
assert.True(t, latestBackupFound)
|
||||
}
|
||||
|
||||
func createJenkinsCRWithAmazonS3Backup(t *testing.T, namespace string, backupConfig amazonS3BackupConfiguration) *v1alpha1.Jenkins {
|
||||
jenkins := &v1alpha1.Jenkins{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha1.JenkinsSpec{
|
||||
Backup: v1alpha1.JenkinsBackupTypeAmazonS3,
|
||||
BackupAmazonS3: v1alpha1.JenkinsBackupAmazonS3{
|
||||
Region: backupConfig.Region,
|
||||
BucketPath: backupConfig.BucketPath,
|
||||
BucketName: backupConfig.BucketName,
|
||||
},
|
||||
Master: v1alpha1.JenkinsMaster{
|
||||
Image: "jenkins/jenkins",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Jenkins CR %+v", *jenkins)
|
||||
err := framework.Global.Client.Create(context.TODO(), jenkins, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
backupCredentialsSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resources.GetBackupCredentialsSecretName(jenkins),
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
constants.BackupAmazonS3SecretAccessKey: []byte(backupConfig.AccessKey),
|
||||
constants.BackupAmazonS3SecretSecretKey: []byte(backupConfig.SecretKey),
|
||||
},
|
||||
}
|
||||
err = framework.Global.Client.Create(context.TODO(), backupCredentialsSecret, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return jenkins
|
||||
}
|
||||
|
||||
func loadAmazonS3BackupConfig(t *testing.T) amazonS3BackupConfiguration {
|
||||
jsonFile, err := os.Open(*amazonS3BackupConfigurationFile)
|
||||
assert.NoError(t, err)
|
||||
defer func() { _ = jsonFile.Close() }()
|
||||
|
||||
byteValue, err := ioutil.ReadAll(jsonFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var result amazonS3BackupConfiguration
|
||||
err = json.Unmarshal([]byte(byteValue), &result)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, result.AccessKey)
|
||||
assert.NotEmpty(t, result.BucketName)
|
||||
assert.NotEmpty(t, result.Region)
|
||||
assert.NotEmpty(t, result.SecretKey)
|
||||
result.BucketPath = t.Name()
|
||||
return result
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/pkg/apis"
|
||||
|
|
@ -15,16 +14,10 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
jenkinsOperatorDeploymentName = constants.OperatorName
|
||||
amazonS3BackupConfigurationParameterName = "s3BackupConfig"
|
||||
)
|
||||
|
||||
var (
|
||||
amazonS3BackupConfigurationFile *string
|
||||
jenkinsOperatorDeploymentName = constants.OperatorName
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
amazonS3BackupConfigurationFile = flag.String(amazonS3BackupConfigurationParameterName, "", "path to AWS S3 backup config")
|
||||
f.MainEntry(m)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue