initial ephemeral home implementation
This commit is contained in:
		
							parent
							
								
									a690c7cc6c
								
							
						
					
					
						commit
						2e8fd0ea7a
					
				|  | @ -343,6 +343,11 @@ type JenkinsMaster struct { | |||
| 	// +optional
 | ||||
| 	Volumes []corev1.Volume `json:"volumes,omitempty"` | ||||
| 
 | ||||
| 	// Storage settings for the jenkins home directory
 | ||||
| 	// Can be tempDir or ephemeral
 | ||||
| 	// +optional
 | ||||
| 	StorageSettings StorageSettings `json:"storage,omitempty"` | ||||
| 
 | ||||
| 	// If specified, the pod's tolerations.
 | ||||
| 	// +optional
 | ||||
| 	Tolerations []corev1.Toleration `json:"tolerations,omitempty"` | ||||
|  | @ -387,6 +392,14 @@ type JenkinsMaster struct { | |||
| 	HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // StorageSettings defines Jenkins master pod persistance attributes.
 | ||||
| type StorageSettings struct { | ||||
| 	UseTempDir bool `json:"useTempDir"` | ||||
| 	UseEphemeralStorage bool `json:"useEphemeralStorage"` | ||||
| 	StorageClassName string `json:"storageClassName"` | ||||
| 	StorageRequest string `json:"storageRequest"` | ||||
| } | ||||
| 
 | ||||
| // Service defines Kubernetes service attributes
 | ||||
| type Service struct { | ||||
| 	// Annotations is an unstructured key value map stored with a resource that may be
 | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import ( | |||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/constants" | ||||
| 
 | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/api/resource" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| ) | ||||
| 
 | ||||
|  | @ -88,18 +89,67 @@ func getJenkinsHomePath(jenkins *v1alpha2.Jenkins) string { | |||
| 	return defaultJenkinsHomePath | ||||
| } | ||||
| 
 | ||||
| func validateStorageSize(storageSize string) bool { | ||||
| 	if strings.TrimSpace(storageSize) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	_, err := resource.ParseQuantity(storageSize) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // get Jenkins home storage settings from the CRD
 | ||||
| func getJenkinsHomeStorageSettings(jenkins *v1alpha2.Jenkins) corev1.Volume { | ||||
| 	JenkinsHomeVolume := corev1.Volume{} | ||||
| 	emptyDirVol := corev1.Volume{ | ||||
| 		Name: JenkinsHomeVolumeName, | ||||
| 		VolumeSource: corev1.VolumeSource{ | ||||
| 			// Create empty dir for Jenkins home
 | ||||
| 			EmptyDir: &corev1.EmptyDirVolumeSource{}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if jenkins.Spec.Master.StorageSettings.UseEphemeralStorage { | ||||
| 		if !validateStorageSize(jenkins.Spec.Master.StorageSettings.StorageRequest) { | ||||
| 			fmt.Println("Invalid storage size %s, falling back to empty dir" + jenkins.Spec.Master.StorageSettings.StorageRequest) | ||||
| 			JenkinsHomeVolume = emptyDirVol | ||||
| 			return JenkinsHomeVolume | ||||
| 		} | ||||
| 		JenkinsHomeVolume = corev1.Volume{ | ||||
| 			Name: JenkinsHomeVolumeName, | ||||
| 			VolumeSource: corev1.VolumeSource{ | ||||
| 				// Create ephemeral storage for Jenkins home
 | ||||
| 				Ephemeral: &corev1.EphemeralVolumeSource{ | ||||
| 					VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{ | ||||
| 						Spec: corev1.PersistentVolumeClaimSpec{ | ||||
| 							StorageClassName: &jenkins.Spec.Master.StorageSettings.StorageClassName, | ||||
| 							AccessModes:      []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, | ||||
| 							Resources: corev1.ResourceRequirements{ | ||||
| 								Requests: corev1.ResourceList{ | ||||
| 									corev1.ResourceStorage: resource.MustParse(jenkins.Spec.Master.StorageSettings.StorageRequest), | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 	} else { | ||||
| 		JenkinsHomeVolume = emptyDirVol | ||||
| 	} | ||||
| 	return JenkinsHomeVolume | ||||
| } | ||||
| 
 | ||||
| // GetJenkinsMasterPodBaseVolumes returns Jenkins master pod volumes required by operator
 | ||||
| func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { | ||||
| 	configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode | ||||
| 	secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode | ||||
| 	var scriptsVolumeDefaultMode int32 = 0777 | ||||
| 	volumes := []corev1.Volume{ | ||||
| 		{ | ||||
| 			Name: JenkinsHomeVolumeName, | ||||
| 			VolumeSource: corev1.VolumeSource{ | ||||
| 				EmptyDir: &corev1.EmptyDirVolumeSource{}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		getJenkinsHomeStorageSettings(jenkins), | ||||
| 		{ | ||||
| 			Name: jenkinsScriptsVolumeName, | ||||
| 			VolumeSource: corev1.VolumeSource{ | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ import ( | |||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/jenkinsci/kubernetes-operator/api/v1alpha2" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
|  | @ -144,6 +143,42 @@ func TestGetJenkinsMasterPodBaseVolumes(t *testing.T) { | |||
| 		assert.True(t, groovyExists) | ||||
| 		assert.True(t, cascExists) | ||||
| 	}) | ||||
| 	t.Run("home volume is present and is Tempdir", func(t *testing.T) { | ||||
| 		jenkins := &v1alpha2.Jenkins{ | ||||
| 			Spec: v1alpha2.JenkinsSpec{ | ||||
| 				Master: v1alpha2.JenkinsMaster{ | ||||
| 					StorageSettings: v1alpha2.StorageSettings{ | ||||
| 						UseTempDir: true, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 
 | ||||
| 		HomeExist, HomeTempdirExist, HomeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) | ||||
| 
 | ||||
| 		assert.True(t, HomeExist) | ||||
| 		assert.True(t, HomeTempdirExist) | ||||
| 		assert.False(t, HomeEphemeralStorageExist) | ||||
| 	}) | ||||
| 	t.Run("home volume is present and it's ephemeral", func(t *testing.T) { | ||||
| 		jenkins := &v1alpha2.Jenkins{ | ||||
| 			Spec: v1alpha2.JenkinsSpec{ | ||||
| 				Master: v1alpha2.JenkinsMaster{ | ||||
| 					StorageSettings: v1alpha2.StorageSettings{ | ||||
| 						UseEphemeralStorage: true, | ||||
| 						StorageClassName:    "", | ||||
| 						StorageRequest:      "1Gi", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 
 | ||||
| 		HomeExist, HomeTempdirExist, HomeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) | ||||
| 
 | ||||
| 		assert.True(t, HomeExist) | ||||
| 		assert.False(t, HomeTempdirExist) | ||||
| 		assert.True(t, HomeEphemeralStorageExist) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, cascExists bool) { | ||||
|  | @ -156,3 +191,55 @@ func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, c | |||
| 	} | ||||
| 	return groovyExists, cascExists | ||||
| } | ||||
| 
 | ||||
| func checkHomeVolumesPresence(jenkins *v1alpha2.Jenkins) (HomeExist bool, HomeTempdirExist bool, HomeEphemeralStorageExist bool) { | ||||
| 	for _, volume := range GetJenkinsMasterPodBaseVolumes(jenkins) { | ||||
| 		if volume.Name == ("jenkins-home") { | ||||
| 			HomeExist = true | ||||
| 			// check if the volume is an emptyDir
 | ||||
| 			if volume.VolumeSource.EmptyDir != nil { | ||||
| 				HomeTempdirExist = true | ||||
| 			} else if volume.VolumeSource.Ephemeral != nil { | ||||
| 				HomeEphemeralStorageExist = true | ||||
| 			} | ||||
| 		} else { | ||||
| 			HomeExist = false | ||||
| 			HomeTempdirExist = false | ||||
| 			HomeEphemeralStorageExist = false | ||||
| 		} | ||||
| 	} | ||||
| 	return HomeExist, HomeTempdirExist, HomeEphemeralStorageExist | ||||
| } | ||||
| 
 | ||||
| func Test_validateStorageSize(t *testing.T) { | ||||
| 	type args struct { | ||||
| 		storageSize string | ||||
| 	} | ||||
| 	tests := []struct { | ||||
| 		name string | ||||
| 		args args | ||||
| 		want bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "1Gi", | ||||
| 			args: args{ | ||||
| 				storageSize: "1Gi", | ||||
| 			}, | ||||
| 			want: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "1Gi1", | ||||
| 			args: args{ | ||||
| 				storageSize: "1Gi1", | ||||
| 			}, | ||||
| 			want: false, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			if got := validateStorageSize(tt.args.storageSize); got != tt.want { | ||||
| 				t.Errorf("validateStorageSize() = %v, want %v", got, tt.want) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue