initial ephemeral home implementation
This commit is contained in:
		
							parent
							
								
									a690c7cc6c
								
							
						
					
					
						commit
						2e8fd0ea7a
					
				|  | @ -343,6 +343,11 @@ type JenkinsMaster struct { | ||||||
| 	// +optional
 | 	// +optional
 | ||||||
| 	Volumes []corev1.Volume `json:"volumes,omitempty"` | 	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.
 | 	// If specified, the pod's tolerations.
 | ||||||
| 	// +optional
 | 	// +optional
 | ||||||
| 	Tolerations []corev1.Toleration `json:"tolerations,omitempty"` | 	Tolerations []corev1.Toleration `json:"tolerations,omitempty"` | ||||||
|  | @ -387,6 +392,14 @@ type JenkinsMaster struct { | ||||||
| 	HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"` | 	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
 | // Service defines Kubernetes service attributes
 | ||||||
| type Service struct { | type Service struct { | ||||||
| 	// Annotations is an unstructured key value map stored with a resource that may be
 | 	// 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" | 	"github.com/jenkinsci/kubernetes-operator/pkg/constants" | ||||||
| 
 | 
 | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/api/resource" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -88,18 +89,67 @@ func getJenkinsHomePath(jenkins *v1alpha2.Jenkins) string { | ||||||
| 	return defaultJenkinsHomePath | 	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
 | // GetJenkinsMasterPodBaseVolumes returns Jenkins master pod volumes required by operator
 | ||||||
| func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { | func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { | ||||||
| 	configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode | 	configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode | ||||||
| 	secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode | 	secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode | ||||||
| 	var scriptsVolumeDefaultMode int32 = 0777 | 	var scriptsVolumeDefaultMode int32 = 0777 | ||||||
| 	volumes := []corev1.Volume{ | 	volumes := []corev1.Volume{ | ||||||
| 		{ | 		getJenkinsHomeStorageSettings(jenkins), | ||||||
| 			Name: JenkinsHomeVolumeName, |  | ||||||
| 			VolumeSource: corev1.VolumeSource{ |  | ||||||
| 				EmptyDir: &corev1.EmptyDirVolumeSource{}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		{ | 		{ | ||||||
| 			Name: jenkinsScriptsVolumeName, | 			Name: jenkinsScriptsVolumeName, | ||||||
| 			VolumeSource: corev1.VolumeSource{ | 			VolumeSource: corev1.VolumeSource{ | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/api/v1alpha2" | 	"github.com/jenkinsci/kubernetes-operator/api/v1alpha2" | ||||||
| 
 |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -144,6 +143,42 @@ func TestGetJenkinsMasterPodBaseVolumes(t *testing.T) { | ||||||
| 		assert.True(t, groovyExists) | 		assert.True(t, groovyExists) | ||||||
| 		assert.True(t, cascExists) | 		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) { | func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, cascExists bool) { | ||||||
|  | @ -156,3 +191,55 @@ func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, c | ||||||
| 	} | 	} | ||||||
| 	return groovyExists, cascExists | 	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