Remove backup mechanism
This commit is contained in:
		
							parent
							
								
									ab87922bd1
								
							
						
					
					
						commit
						dd04c0cf5b
					
				|  | @ -33,49 +33,6 @@ | ||||||
|   pruneopts = "NT" |   pruneopts = "NT" | ||||||
|   revision = "de5bf2ad457846296e2031421a34e2568e304e35" |   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]] | [[projects]] | ||||||
|   digest = "1:8d13c70d5898b091728540686c696baee0d64013b8e43089da80621a49410391" |   digest = "1:8d13c70d5898b091728540686c696baee0d64013b8e43089da80621a49410391" | ||||||
|   name = "github.com/bndr/gojenkins" |   name = "github.com/bndr/gojenkins" | ||||||
|  | @ -292,13 +249,6 @@ | ||||||
|   revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" |   revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" | ||||||
|   version = "v0.3.6" |   version = "v0.3.6" | ||||||
| 
 | 
 | ||||||
| [[projects]] |  | ||||||
|   digest = "1:1234e31f3de67447e344dabcdf72c4588d31b8eed2d28f1889377ec006a086a9" |  | ||||||
|   name = "github.com/jmespath/go-jmespath" |  | ||||||
|   packages = ["."] |  | ||||||
|   pruneopts = "NT" |  | ||||||
|   revision = "c2b33e84" |  | ||||||
| 
 |  | ||||||
| [[projects]] | [[projects]] | ||||||
|   digest = "1:f5b9328966ccea0970b1d15075698eff0ddb3e75889560aad2e9f76b289b536a" |   digest = "1:f5b9328966ccea0970b1d15075698eff0ddb3e75889560aad2e9f76b289b536a" | ||||||
|   name = "github.com/joho/godotenv" |   name = "github.com/joho/godotenv" | ||||||
|  | @ -468,10 +418,7 @@ | ||||||
| [[projects]] | [[projects]] | ||||||
|   digest = "1:4af061277c04a7660e082acc2020f4c66d2c21dfc62e0242ffa1d2120cdfb4ec" |   digest = "1:4af061277c04a7660e082acc2020f4c66d2c21dfc62e0242ffa1d2120cdfb4ec" | ||||||
|   name = "github.com/stretchr/testify" |   name = "github.com/stretchr/testify" | ||||||
|   packages = [ |   packages = ["assert"] | ||||||
|     "assert", |  | ||||||
|     "require", |  | ||||||
|   ] |  | ||||||
|   pruneopts = "NT" |   pruneopts = "NT" | ||||||
|   revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" |   revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" | ||||||
|   version = "v1.2.2" |   version = "v1.2.2" | ||||||
|  | @ -517,7 +464,7 @@ | ||||||
| 
 | 
 | ||||||
| [[projects]] | [[projects]] | ||||||
|   branch = "master" |   branch = "master" | ||||||
|   digest = "1:a7fcf4f3e5247a06ad4c28108f0bc1d4ab980a1a0567e7790260cf2d3d77f37d" |   digest = "1:922c0bb9dc59af35400f6725ed08582f99f710ffc1a1075e8914c73515bb269e" | ||||||
|   name = "golang.org/x/net" |   name = "golang.org/x/net" | ||||||
|   packages = [ |   packages = [ | ||||||
|     "context", |     "context", | ||||||
|  | @ -530,7 +477,7 @@ | ||||||
|     "idna", |     "idna", | ||||||
|   ] |   ] | ||||||
|   pruneopts = "NT" |   pruneopts = "NT" | ||||||
|   revision = "c10e9556a7bc0e7c942242b606f0acf024ad5d6a" |   revision = "3a22650c66bd7f4fb6d1e8072ffd7b75c8a27898" | ||||||
| 
 | 
 | ||||||
| [[projects]] | [[projects]] | ||||||
|   branch = "master" |   branch = "master" | ||||||
|  | @ -914,10 +861,6 @@ | ||||||
|   analyzer-name = "dep" |   analyzer-name = "dep" | ||||||
|   analyzer-version = 1 |   analyzer-version = 1 | ||||||
|   input-imports = [ |   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/bndr/gojenkins", | ||||||
|     "github.com/docker/distribution/reference", |     "github.com/docker/distribution/reference", | ||||||
|     "github.com/go-logr/logr", |     "github.com/go-logr/logr", | ||||||
|  | @ -930,7 +873,6 @@ | ||||||
|     "github.com/operator-framework/operator-sdk/version", |     "github.com/operator-framework/operator-sdk/version", | ||||||
|     "github.com/pkg/errors", |     "github.com/pkg/errors", | ||||||
|     "github.com/stretchr/testify/assert", |     "github.com/stretchr/testify/assert", | ||||||
|     "github.com/stretchr/testify/require", |  | ||||||
|     "k8s.io/api/core/v1", |     "k8s.io/api/core/v1", | ||||||
|     "k8s.io/api/rbac/v1", |     "k8s.io/api/rbac/v1", | ||||||
|     "k8s.io/apimachinery/pkg/api/errors", |     "k8s.io/apimachinery/pkg/api/errors", | ||||||
|  |  | ||||||
|  | @ -12,32 +12,10 @@ import ( | ||||||
| type JenkinsSpec struct { | type JenkinsSpec struct { | ||||||
| 	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
 | 	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
 | ||||||
| 	// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
 | 	// 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"` | 	Master   JenkinsMaster `json:"master,omitempty"` | ||||||
| 	SeedJobs []SeedJob     `json:"seedJobs,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"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JenkinsMaster defines the Jenkins master pod attributes and plugins,
 | // JenkinsMaster defines the Jenkins master pod attributes and plugins,
 | ||||||
| // every single change requires Jenkins master pod restart
 | // every single change requires Jenkins master pod restart
 | ||||||
| type JenkinsMaster struct { | type JenkinsMaster struct { | ||||||
|  | @ -51,7 +29,6 @@ type JenkinsMaster struct { | ||||||
| type JenkinsStatus struct { | type JenkinsStatus struct { | ||||||
| 	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
 | 	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
 | ||||||
| 	// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
 | 	// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
 | ||||||
| 	BackupRestored                 bool         `json:"backupRestored,omitempty"` |  | ||||||
| 	BaseConfigurationCompletedTime *metav1.Time `json:"baseConfigurationCompletedTime,omitempty"` | 	BaseConfigurationCompletedTime *metav1.Time `json:"baseConfigurationCompletedTime,omitempty"` | ||||||
| 	UserConfigurationCompletedTime *metav1.Time `json:"userConfigurationCompletedTime,omitempty"` | 	UserConfigurationCompletedTime *metav1.Time `json:"userConfigurationCompletedTime,omitempty"` | ||||||
| 	Builds                         []Build      `json:"builds,omitempty"` | 	Builds                         []Build      `json:"builds,omitempty"` | ||||||
|  |  | ||||||
|  | @ -77,22 +77,6 @@ func (in *Jenkins) DeepCopyObject() runtime.Object { | ||||||
| 	return nil | 	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.
 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||||
| func (in *JenkinsList) DeepCopyInto(out *JenkinsList) { | func (in *JenkinsList) DeepCopyInto(out *JenkinsList) { | ||||||
| 	*out = *in | 	*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.
 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||||
| func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) { | func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) { | ||||||
| 	*out = *in | 	*out = *in | ||||||
| 	out.BackupAmazonS3 = in.BackupAmazonS3 |  | ||||||
| 	in.Master.DeepCopyInto(&out.Master) | 	in.Master.DeepCopyInto(&out.Master) | ||||||
| 	if in.SeedJobs != nil { | 	if in.SeedJobs != nil { | ||||||
| 		in, out := &in.SeedJobs, &out.SeedJobs | 		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" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | 	"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" | 	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/base/resources" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | 	"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 | 		return reconcile.Result{}, nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pluginsRequiredByAllBackupProviders := backup.GetPluginsRequiredByAllBackupProviders() | 	result, err := r.ensureJenkinsMasterPod(metaObject) | ||||||
| 	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) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return reconcile.Result{}, nil, err | 		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") | 	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 { | 	if err != nil { | ||||||
| 		return reconcile.Result{}, nil, err | 		return reconcile.Result{}, nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -144,11 +134,6 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod | ||||||
| 	} | 	} | ||||||
| 	r.logger.V(log.VDebug).Info("Service is present") | 	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 | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -473,23 +458,6 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClien | ||||||
| 	return reconcile.Result{}, nil | 	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 { | func (r *ReconcileJenkinsBaseConfiguration) verifyLabelsForWatchedResource(object metav1.Object) bool { | ||||||
| 	requiredLabels := resources.BuildLabelsForWatchedResources(r.jenkins) | 	requiredLabels := resources.BuildLabelsForWatchedResources(r.jenkins) | ||||||
| 	for key, value := range requiredLabels { | 	for key, value := range requiredLabels { | ||||||
|  | @ -500,28 +468,3 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyLabelsForWatchedResource(objec | ||||||
| 
 | 
 | ||||||
| 	return true | 	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 | import hudson.model.Node.Mode | ||||||
| 
 | 
 | ||||||
| def jenkins = Jenkins.instance | 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) | jenkins.setNumExecutors(%d) | ||||||
| //Jobs must specify that they want to run on master
 | //Jobs must specify that they want to run on master
 | ||||||
| jenkins.setMode(Mode.EXCLUSIVE) | jenkins.setMode(Mode.EXCLUSIVE) | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ const ( | ||||||
| 	jenkinsScriptsVolumeName = "scripts" | 	jenkinsScriptsVolumeName = "scripts" | ||||||
| 	jenkinsScriptsVolumePath = "/var/jenkins/scripts" | 	jenkinsScriptsVolumePath = "/var/jenkins/scripts" | ||||||
| 	initScriptName           = "init.sh" | 	initScriptName           = "init.sh" | ||||||
| 	backupScriptName         = "backup.sh" |  | ||||||
| 
 | 
 | ||||||
| 	jenkinsOperatorCredentialsVolumeName = "operator-credentials" | 	jenkinsOperatorCredentialsVolumeName = "operator-credentials" | ||||||
| 	jenkinsOperatorCredentialsVolumePath = "/var/jenkins/operator-credentials" | 	jenkinsOperatorCredentialsVolumePath = "/var/jenkins/operator-credentials" | ||||||
|  | @ -34,11 +33,6 @@ const ( | ||||||
| 	// this scripts are provided by user
 | 	// this scripts are provided by user
 | ||||||
| 	JenkinsUserConfigurationVolumePath = "/var/jenkins/user-configuration" | 	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" | 	httpPortName  = "http" | ||||||
| 	slavePortName = "slavelistener" | 	slavePortName = "slavelistener" | ||||||
| 	// HTTPPortInt defines Jenkins master HTTP port
 | 	// HTTPPortInt defines Jenkins master HTTP port
 | ||||||
|  | @ -84,16 +78,6 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins | ||||||
| 						"bash", | 						"bash", | ||||||
| 						fmt.Sprintf("%s/%s", jenkinsScriptsVolumePath, initScriptName), | 						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{ | 					LivenessProbe: &corev1.Probe{ | ||||||
| 						Handler: corev1.Handler{ | 						Handler: corev1.Handler{ | ||||||
| 							HTTPGet: &corev1.HTTPGetAction{ | 							HTTPGet: &corev1.HTTPGetAction{ | ||||||
|  | @ -168,11 +152,6 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins | ||||||
| 							MountPath: jenkinsOperatorCredentialsVolumePath, | 							MountPath: jenkinsOperatorCredentialsVolumePath, | ||||||
| 							ReadOnly:  true, | 							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 | /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 { | func buildConfigMapTypeMeta() metav1.TypeMeta { | ||||||
| 	return metav1.TypeMeta{ | 	return metav1.TypeMeta{ | ||||||
| 		Kind:       "ConfigMap", | 		Kind:       "ConfigMap", | ||||||
|  | @ -377,8 +313,6 @@ func NewScriptsConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) (*co | ||||||
| 		Data: map[string]string{ | 		Data: map[string]string{ | ||||||
| 			initScriptName:        *initBashScript, | 			initScriptName:        *initBashScript, | ||||||
| 			installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath), | 			installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath), | ||||||
| 			backupScriptName: fmt.Sprintf(backupBashFmt, |  | ||||||
| 				OperatorUserName, jenkinsOperatorCredentialsVolumePath, OperatorCredentialsSecretTokenKey, HTTPPortInt, constants.BackupJobName), |  | ||||||
| 		}, | 		}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,20 +1,14 @@ | ||||||
| package base | package base | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | 	"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/controller/jenkins/plugins" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | ||||||
| 
 | 
 | ||||||
| 	docker "github.com/docker/distribution/reference" | 	docker "github.com/docker/distribution/reference" | ||||||
| 	corev1 "k8s.io/api/core/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" |  | ||||||
| 	"k8s.io/apimachinery/pkg/types" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | @ -38,20 +32,6 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha1.Jenkins) | ||||||
| 		return false, nil | 		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 | 	return true, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -84,39 +64,3 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(pluginsWithVersions | ||||||
| 
 | 
 | ||||||
| 	return valid | 	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 | package base | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" |  | ||||||
| 
 |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"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" | 	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" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | 	"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" | 	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/base/resources" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/seedjobs" | 	"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
 | // Reconcile it's a main reconciliation loop for user supplied configuration
 | ||||||
| func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) { | 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
 | 	// reconcile seed jobs
 | ||||||
| 	result, err = r.ensureSeedJobs() | 	result, err := r.ensureSeedJobs() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return reconcile.Result{}, err | 		return reconcile.Result{}, err | ||||||
| 	} | 	} | ||||||
|  | @ -71,11 +57,6 @@ func (r *ReconcileUserConfiguration) Reconcile() (reconcile.Result, error) { | ||||||
| 		return result, nil | 		return result, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = backupManager.EnsureBackupJob() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return reconcile.Result{}, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return reconcile.Result{}, nil | 	return reconcile.Result{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | 	"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" | 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | ||||||
| 
 | 
 | ||||||
| 	"k8s.io/api/core/v1" | 	"k8s.io/api/core/v1" | ||||||
|  | @ -24,12 +23,7 @@ func (r *ReconcileUserConfiguration) Validate(jenkins *v1alpha1.Jenkins) (bool, | ||||||
| 		return valid, err | 		return valid, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	backupProvider, err := backup.GetBackupProvider(r.jenkins.Spec.Backup) | 	return true, nil | ||||||
| 	if err != nil { |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return backupProvider.IsConfigurationValidForUserPhase(r.k8sClient, *r.jenkins, r.logger) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *ReconcileUserConfiguration) validateSeedJobs(jenkins *v1alpha1.Jenkins) (bool, error) { | func (r *ReconcileUserConfiguration) validateSeedJobs(jenkins *v1alpha1.Jenkins) (bool, error) { | ||||||
|  |  | ||||||
|  | @ -9,14 +9,6 @@ const ( | ||||||
| 	SeedJobSuffix = "job-dsl-seed" | 	SeedJobSuffix = "job-dsl-seed" | ||||||
| 	// DefaultJenkinsMasterImage is the default Jenkins master docker image
 | 	// DefaultJenkinsMasterImage is the default Jenkins master docker image
 | ||||||
| 	DefaultJenkinsMasterImage = "jenkins/jenkins:lts" | 	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 is the Jenkins job name used to configure Jenkins by groovy scripts provided by user
 | ||||||
| 	UserConfigurationJobName = OperatorName + "-user-configuration" | 	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 | 		changed = true | ||||||
| 		jenkins.Spec.Master.Image = constants.DefaultJenkinsMasterImage | 		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 { | 	if len(jenkins.Spec.Master.Plugins) == 0 { | ||||||
| 		logger.Info("Setting default base plugins") | 		logger.Info("Setting default base plugins") | ||||||
| 		changed = true | 		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 | package e2e | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"flag" |  | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis" | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis" | ||||||
|  | @ -16,15 +15,9 @@ import ( | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	jenkinsOperatorDeploymentName = constants.OperatorName | 	jenkinsOperatorDeploymentName = constants.OperatorName | ||||||
| 	amazonS3BackupConfigurationParameterName = "s3BackupConfig" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	amazonS3BackupConfigurationFile *string |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	amazonS3BackupConfigurationFile = flag.String(amazonS3BackupConfigurationParameterName, "", "path to AWS S3 backup config") |  | ||||||
| 	f.MainEntry(m) | 	f.MainEntry(m) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue