JenkinsImage controller based on kaniko (#394)
This commit is contained in:
		
							parent
							
								
									3ecf280efa
								
							
						
					
					
						commit
						ee01c4c028
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -197,7 +197,7 @@ ifeq ($(KUBERNETES_PROVIDER),minikube) | |||
| endif | ||||
| endif | ||||
| 
 | ||||
| 	@RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \
 | ||||
| 	RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \
 | ||||
| 		-root=$(CURRENT_DIRECTORY) -kubeconfig=$(HOME)/.kube/config -globalMan deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml \
 | ||||
| 		-namespacedMan deploy/namespace-init.yaml $(TEST_ARGS) | ||||
| 
 | ||||
|  | @ -251,6 +251,7 @@ ifeq ($(KUBERNETES_PROVIDER),crc) | |||
| 	oc project $(CRC_OC_PROJECT) | ||||
| endif | ||||
| 	kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml | ||||
| 	kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkinsimage_crd.yaml | ||||
| 	@echo "Watching '$(WATCH_NAMESPACE)' namespace" | ||||
| 	build/_output/bin/jenkins-operator $(OPERATOR_ARGS) | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ import ( | |||
| 	"os" | ||||
| 	"runtime" | ||||
| 
 | ||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkinsimage" | ||||
| 
 | ||||
| 	// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
 | ||||
| 	_ "k8s.io/client-go/plugin/pkg/client/auth" | ||||
| 
 | ||||
|  | @ -135,6 +137,10 @@ func main() { | |||
| 	if err := jenkins.Add(mgr, jenkinsAPIConnectionSettings, *clientSet, *cfg, &c); err != nil { | ||||
| 		fatal(errors.Wrap(err, "failed to setup controllers"), *debug) | ||||
| 	} | ||||
| 	// setup JenkinsImage controller
 | ||||
| 	if err = jenkinsimage.Add(mgr); err != nil { | ||||
| 		fatal(errors.Wrap(err, "failed to setup controllers"), *debug) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = serveCRMetrics(cfg); err != nil { | ||||
| 		log.Log.V(log.VWarn).Info("Could not generate and serve custom resource metrics", "error", err.Error()) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ DOCKER_ORGANIZATION=virtuslab | |||
| DOCKER_REGISTRY=jenkins-operator | ||||
| NAMESPACE=default | ||||
| API_VERSION=v1alpha2 | ||||
| API_VERSION_NEXT=v1alpha3 | ||||
| ALL_IN_ONE_DEPLOY_FILE_PREFIX=all-in-one | ||||
| GEN_CRD_API=gen-crd-api-reference-docs | ||||
| IMAGE_PULL_MODE=local | ||||
|  |  | |||
|  | @ -0,0 +1,85 @@ | |||
| apiVersion: apiextensions.k8s.io/v1beta1 | ||||
| kind: CustomResourceDefinition | ||||
| metadata: | ||||
|   name: jenkinsimages.jenkins.io | ||||
| spec: | ||||
|   group: jenkins.io | ||||
|   names: | ||||
|     kind: JenkinsImage | ||||
|     listKind: JenkinsImageList | ||||
|     plural: jenkinsimages | ||||
|     singular: jenkinsimage | ||||
|   scope: Namespaced | ||||
|   subresources: | ||||
|     status: {} | ||||
|   validation: | ||||
|     openAPIV3Schema: | ||||
|       description: JenkinsImage is the Schema for the jenkinsimages API | ||||
|       properties: | ||||
|         apiVersion: | ||||
|           description: 'APIVersion defines the versioned schema of this representation | ||||
|             of an object. Servers should convert recognized schemas to the latest | ||||
|             internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' | ||||
|           type: string | ||||
|         kind: | ||||
|           description: 'Kind is a string value representing the REST resource this | ||||
|             object represents. Servers may infer this from the endpoint the client | ||||
|             submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' | ||||
|           type: string | ||||
|         metadata: | ||||
|           type: object | ||||
|         spec: | ||||
|           description: JenkinsImageSpec defines the desired state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               description: Defines Jenkins Plugin structure | ||||
|               properties: | ||||
|                 name: | ||||
|                   type: string | ||||
|                 version: | ||||
|                   type: string | ||||
|               required: | ||||
|               - name | ||||
|               type: object | ||||
|             plugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                 - name | ||||
|                 type: object | ||||
|               type: array | ||||
|           required: | ||||
|           - image | ||||
|           - plugins | ||||
|           type: object | ||||
|         status: | ||||
|           description: JenkinsImageStatus defines the observed state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               type: string | ||||
|             installedPlugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                 - name | ||||
|                 type: object | ||||
|               type: array | ||||
|             md5sum: | ||||
|               type: string | ||||
|           type: object | ||||
|       type: object | ||||
|   version: v1alpha2 | ||||
|   versions: | ||||
|   - name: v1alpha2 | ||||
|     served: true | ||||
|     storage: true | ||||
|  | @ -17,3 +17,90 @@ spec: | |||
|     - name : v1alpha1 | ||||
|       served: true | ||||
|       storage: false | ||||
| --- | ||||
| apiVersion: apiextensions.k8s.io/v1beta1 | ||||
| kind: CustomResourceDefinition | ||||
| metadata: | ||||
|   name: jenkinsimages.jenkins.io | ||||
| spec: | ||||
|   group: jenkins.io | ||||
|   names: | ||||
|     kind: JenkinsImage | ||||
|     listKind: JenkinsImageList | ||||
|     plural: jenkinsimages | ||||
|     singular: jenkinsimage | ||||
|   scope: Namespaced | ||||
|   subresources: | ||||
|     status: {} | ||||
|   validation: | ||||
|     openAPIV3Schema: | ||||
|       description: JenkinsImage is the Schema for the jenkinsimages API | ||||
|       properties: | ||||
|         apiVersion: | ||||
|           description: 'APIVersion defines the versioned schema of this representation | ||||
|             of an object. Servers should convert recognized schemas to the latest | ||||
|             internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' | ||||
|           type: string | ||||
|         kind: | ||||
|           description: 'Kind is a string value representing the REST resource this | ||||
|             object represents. Servers may infer this from the endpoint the client | ||||
|             submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' | ||||
|           type: string | ||||
|         metadata: | ||||
|           type: object | ||||
|         spec: | ||||
|           description: JenkinsImageSpec defines the desired state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               description: Defines Jenkins Plugin structure | ||||
|               properties: | ||||
|                 name: | ||||
|                   type: string | ||||
|                 version: | ||||
|                   type: string | ||||
|               required: | ||||
|                 - name | ||||
|               type: object | ||||
|             plugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                   - name | ||||
|                 type: object | ||||
|               type: array | ||||
|           required: | ||||
|             - image | ||||
|             - plugins | ||||
|           type: object | ||||
|         status: | ||||
|           description: JenkinsImageStatus defines the observed state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               type: string | ||||
|             installedPlugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                   - name | ||||
|                 type: object | ||||
|               type: array | ||||
|             md5sum: | ||||
|               type: string | ||||
|           type: object | ||||
|       type: object | ||||
|   version: v1alpha2 | ||||
|   versions: | ||||
|     - name: v1alpha2 | ||||
|       served: true | ||||
|       storage: true | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,24 @@ | |||
| apiVersion: jenkins.io/v1alpha2 | ||||
| kind: JenkinsImage | ||||
| metadata: | ||||
|   name: simple-jenkinsimage | ||||
| spec: | ||||
|   image: | ||||
|     name: jenkins/jenkins | ||||
|     tag: lts | ||||
|   plugins: | ||||
|   - name: kubernetes | ||||
|     version: "1.15.7" | ||||
|   - name: workflow-job  | ||||
|     version: "2.32" | ||||
|   - name: workflow-aggregator | ||||
|     version: "2.6" | ||||
|   - name: git | ||||
|     version: "3.10.0" | ||||
|   - name: job-dsl | ||||
|     version: "1.74" | ||||
|   - name: configuration-as-code | ||||
|     version: "1.19" | ||||
|   - name: kubernetes-credentials-provider | ||||
|     version: "0.12.1" | ||||
| 
 | ||||
|  | @ -0,0 +1,85 @@ | |||
| apiVersion: apiextensions.k8s.io/v1beta1 | ||||
| kind: CustomResourceDefinition | ||||
| metadata: | ||||
|   name: jenkinsimages.jenkins.io | ||||
| spec: | ||||
|   group: jenkins.io | ||||
|   names: | ||||
|     kind: JenkinsImage | ||||
|     listKind: JenkinsImageList | ||||
|     plural: jenkinsimages | ||||
|     singular: jenkinsimage | ||||
|   scope: Namespaced | ||||
|   subresources: | ||||
|     status: {} | ||||
|   validation: | ||||
|     openAPIV3Schema: | ||||
|       description: JenkinsImage is the Schema for the jenkinsimages API | ||||
|       properties: | ||||
|         apiVersion: | ||||
|           description: 'APIVersion defines the versioned schema of this representation | ||||
|             of an object. Servers should convert recognized schemas to the latest | ||||
|             internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' | ||||
|           type: string | ||||
|         kind: | ||||
|           description: 'Kind is a string value representing the REST resource this | ||||
|             object represents. Servers may infer this from the endpoint the client | ||||
|             submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' | ||||
|           type: string | ||||
|         metadata: | ||||
|           type: object | ||||
|         spec: | ||||
|           description: JenkinsImageSpec defines the desired state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               description: Defines Jenkins Plugin structure | ||||
|               properties: | ||||
|                 name: | ||||
|                   type: string | ||||
|                 version: | ||||
|                   type: string | ||||
|               required: | ||||
|               - name | ||||
|               type: object | ||||
|             plugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                 - name | ||||
|                 type: object | ||||
|               type: array | ||||
|           required: | ||||
|           - image | ||||
|           - plugins | ||||
|           type: object | ||||
|         status: | ||||
|           description: JenkinsImageStatus defines the observed state of JenkinsImage | ||||
|           properties: | ||||
|             image: | ||||
|               type: string | ||||
|             installedPlugins: | ||||
|               items: | ||||
|                 description: Defines Jenkins Plugin structure | ||||
|                 properties: | ||||
|                   name: | ||||
|                     type: string | ||||
|                   version: | ||||
|                     type: string | ||||
|                 required: | ||||
|                 - name | ||||
|                 type: object | ||||
|               type: array | ||||
|             md5sum: | ||||
|               type: string | ||||
|           type: object | ||||
|       type: object | ||||
|   version: v1alpha2 | ||||
|   versions: | ||||
|   - name: v1alpha2 | ||||
|     served: true | ||||
|     storage: true | ||||
|  | @ -0,0 +1,58 @@ | |||
| package v1alpha2 | ||||
| 
 | ||||
| import ( | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| ) | ||||
| 
 | ||||
| // EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
 | ||||
| // NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
 | ||||
| 
 | ||||
| // JenkinsImageSpec defines the desired state of JenkinsImage
 | ||||
| type JenkinsImageSpec struct { | ||||
| 	BaseImage Image           `json:"image"` | ||||
| 	Plugins   []JenkinsPlugin `json:"plugins"` // Plugins list
 | ||||
| } | ||||
| 
 | ||||
| // Defines Jenkins Plugin structure
 | ||||
| type JenkinsPlugin struct { | ||||
| 	Name    string `json:"name"` | ||||
| 	Version string `json:"version,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Defines Jenkins Plugin structure
 | ||||
| type Image struct { | ||||
| 	Name string `json:"name"` | ||||
| 	Tag  string `json:"version,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JenkinsImageStatus defines the observed state of JenkinsImage
 | ||||
| type JenkinsImageStatus struct { | ||||
| 	Image            string          `json:"image,omitempty"` | ||||
| 	MD5Sum           string          `json:"md5sum,omitempty"` | ||||
| 	InstalledPlugins []JenkinsPlugin `json:"installedPlugins,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 | ||||
| 
 | ||||
| // JenkinsImage is the Schema for the jenkinsimages API
 | ||||
| // +kubebuilder:subresource:status
 | ||||
| // +kubebuilder:resource:path=jenkinsimages,scope=Namespaced
 | ||||
| type JenkinsImage struct { | ||||
| 	metav1.TypeMeta   `json:",inline"` | ||||
| 	metav1.ObjectMeta `json:"metadata,omitempty"` | ||||
| 	Spec              JenkinsImageSpec   `json:"spec,omitempty"` | ||||
| 	Status            JenkinsImageStatus `json:"status,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 | ||||
| 
 | ||||
| // JenkinsImageList contains a list of JenkinsImage
 | ||||
| type JenkinsImageList struct { | ||||
| 	metav1.TypeMeta `json:",inline"` | ||||
| 	metav1.ListMeta `json:"metadata,omitempty"` | ||||
| 	Items           []JenkinsImage `json:"items"` | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	SchemeBuilder.Register(&JenkinsImage{}, &JenkinsImageList{}) | ||||
| } | ||||
|  | @ -47,4 +47,5 @@ func JenkinsTypeMeta() metav1.TypeMeta { | |||
| 
 | ||||
| func init() { | ||||
| 	SchemeBuilder.Register(&Jenkins{}, &JenkinsList{}) | ||||
| 	SchemeBuilder.Register(&JenkinsImage{}, &JenkinsImageList{}) | ||||
| } | ||||
|  |  | |||
|  | @ -209,6 +209,22 @@ func (in *Handler) DeepCopy() *Handler { | |||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *Image) DeepCopyInto(out *Image) { | ||||
| 	*out = *in | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image.
 | ||||
| func (in *Image) DeepCopy() *Image { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(Image) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *Jenkins) DeepCopyInto(out *Jenkins) { | ||||
| 	*out = *in | ||||
|  | @ -253,6 +269,110 @@ func (in *JenkinsAPISettings) DeepCopy() *JenkinsAPISettings { | |||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsImage) DeepCopyInto(out *JenkinsImage) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) | ||||
| 	in.Spec.DeepCopyInto(&out.Spec) | ||||
| 	in.Status.DeepCopyInto(&out.Status) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImage.
 | ||||
| func (in *JenkinsImage) DeepCopy() *JenkinsImage { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(JenkinsImage) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
 | ||||
| func (in *JenkinsImage) DeepCopyObject() runtime.Object { | ||||
| 	if c := in.DeepCopy(); c != nil { | ||||
| 		return c | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsImageList) DeepCopyInto(out *JenkinsImageList) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	in.ListMeta.DeepCopyInto(&out.ListMeta) | ||||
| 	if in.Items != nil { | ||||
| 		in, out := &in.Items, &out.Items | ||||
| 		*out = make([]JenkinsImage, len(*in)) | ||||
| 		for i := range *in { | ||||
| 			(*in)[i].DeepCopyInto(&(*out)[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageList.
 | ||||
| func (in *JenkinsImageList) DeepCopy() *JenkinsImageList { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(JenkinsImageList) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
 | ||||
| func (in *JenkinsImageList) DeepCopyObject() runtime.Object { | ||||
| 	if c := in.DeepCopy(); c != nil { | ||||
| 		return c | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsImageSpec) DeepCopyInto(out *JenkinsImageSpec) { | ||||
| 	*out = *in | ||||
| 	out.BaseImage = in.BaseImage | ||||
| 	if in.Plugins != nil { | ||||
| 		in, out := &in.Plugins, &out.Plugins | ||||
| 		*out = make([]JenkinsPlugin, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageSpec.
 | ||||
| func (in *JenkinsImageSpec) DeepCopy() *JenkinsImageSpec { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(JenkinsImageSpec) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsImageStatus) DeepCopyInto(out *JenkinsImageStatus) { | ||||
| 	*out = *in | ||||
| 	if in.InstalledPlugins != nil { | ||||
| 		in, out := &in.InstalledPlugins, &out.InstalledPlugins | ||||
| 		*out = make([]JenkinsPlugin, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageStatus.
 | ||||
| func (in *JenkinsImageStatus) DeepCopy() *JenkinsImageStatus { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(JenkinsImageStatus) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsList) DeepCopyInto(out *JenkinsList) { | ||||
| 	*out = *in | ||||
|  | @ -371,6 +491,22 @@ func (in *JenkinsMaster) DeepCopy() *JenkinsMaster { | |||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsPlugin) DeepCopyInto(out *JenkinsPlugin) { | ||||
| 	*out = *in | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsPlugin.
 | ||||
| func (in *JenkinsPlugin) DeepCopy() *JenkinsPlugin { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(JenkinsPlugin) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) { | ||||
| 	*out = *in | ||||
|  |  | |||
|  | @ -0,0 +1,128 @@ | |||
| package resources | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	jenkinsv1alpha2 "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/log" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	NameWithSuffixFormat         = "%s-%s" | ||||
| 	PluginDefinitionFormat       = "%s:%s" | ||||
| 	BuilderDockerfileArg         = "--dockerfile=/workspace/dockerfile/Dockerfile" | ||||
| 	BuilderContextDirArg         = "--context=dir://workspace/" | ||||
| 	BuilderPushArg               = "--no-push" | ||||
| 	BuilderDigestFileArg         = "--digest-file=/dev/termination-log" | ||||
| 	BuilderSuffix                = "builder" | ||||
| 	DockerfileStorageSuffix      = "dockerfile-storage" | ||||
| 	DockerfileNameSuffix         = "dockerfile" | ||||
| 	JenkinsImageBuilderImage     = "gcr.io/kaniko-project/executor:latest" | ||||
| 	JenkinsImageBuilderName      = "jenkins-image-builder" | ||||
| 	JenkinsImageDefaultBaseImage = "jenkins/jenkins:lts" | ||||
| 	DockerfileName               = "Dockerfile" | ||||
| 	DockerfileTemplate           = `FROM %s | ||||
| RUN curl -o /tmp/install-plugins.sh https://raw.githubusercontent.com/jenkinsci/docker/master/install-plugins.sh
 | ||||
| RUN chmod +x /tmp/install-plugins.sh | ||||
| RUN install-plugins.sh %s ` | ||||
| ) | ||||
| 
 | ||||
| var log = logf.Log.WithName("controller_jenkinsimage") | ||||
| 
 | ||||
| // NewBuilderPod returns a busybox pod with the same name/namespace as the cr.
 | ||||
| func NewBuilderPod(cr *jenkinsv1alpha2.JenkinsImage) *corev1.Pod { | ||||
| 	name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, BuilderSuffix) | ||||
| 	args := []string{BuilderDockerfileArg, BuilderContextDirArg, BuilderPushArg, BuilderDigestFileArg} | ||||
| 	volumes := getVolumes(cr) | ||||
| 	volumeMounts := getVolumesMounts(cr) | ||||
| 	p := &corev1.Pod{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      name, | ||||
| 			Namespace: cr.Namespace, | ||||
| 		}, | ||||
| 		Spec: corev1.PodSpec{ | ||||
| 			RestartPolicy: corev1.RestartPolicyNever, | ||||
| 			Containers: []corev1.Container{ | ||||
| 				{ | ||||
| 					Name:         JenkinsImageBuilderName, | ||||
| 					Image:        JenkinsImageBuilderImage, | ||||
| 					Args:         args, | ||||
| 					VolumeMounts: volumeMounts, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Volumes: volumes, | ||||
| 		}, | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| // NewDockerfileConfigMap returns a busybox pod with the same name/namespace as the cr.
 | ||||
| func NewDockerfileConfigMap(cr *jenkinsv1alpha2.JenkinsImage) *corev1.ConfigMap { | ||||
| 	dockerfileContent := fmt.Sprintf(DockerfileTemplate, getDefaultedBaseImage(cr), getPluginsList(cr)) | ||||
| 	name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix) | ||||
| 	data := map[string]string{DockerfileName: dockerfileContent} | ||||
| 	dockerfile := &corev1.ConfigMap{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      name, | ||||
| 			Namespace: cr.Namespace, | ||||
| 		}, | ||||
| 		Data: data, | ||||
| 	} | ||||
| 	return dockerfile | ||||
| } | ||||
| 
 | ||||
| func getPluginsList(cr *jenkinsv1alpha2.JenkinsImage) string { | ||||
| 	logger := log.WithName("jenkinsimage_getPluginsList") | ||||
| 	plugins := "" | ||||
| 	for _, v := range cr.Spec.Plugins { | ||||
| 		plugins += fmt.Sprintf(PluginDefinitionFormat, v.Name, v.Version) + " " | ||||
| 		logger.Info(fmt.Sprintf("Adding plugin %s:%s ", v.Name, v.Version)) | ||||
| 	} | ||||
| 	return plugins | ||||
| } | ||||
| 
 | ||||
| func getDefaultedBaseImage(cr *jenkinsv1alpha2.JenkinsImage) string { | ||||
| 	if len(cr.Spec.BaseImage.Name) != 0 { | ||||
| 		return cr.Spec.BaseImage.Name | ||||
| 	} | ||||
| 	return JenkinsImageDefaultBaseImage | ||||
| } | ||||
| 
 | ||||
| func getVolumes(cr *jenkinsv1alpha2.JenkinsImage) []corev1.Volume { | ||||
| 	name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileStorageSuffix) | ||||
| 	storage := corev1.Volume{ | ||||
| 		Name: name, | ||||
| 		VolumeSource: corev1.VolumeSource{ | ||||
| 			EmptyDir: &corev1.EmptyDirVolumeSource{}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	name = fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix) | ||||
| 	config := corev1.Volume{ | ||||
| 		Name: name, | ||||
| 		VolumeSource: corev1.VolumeSource{ | ||||
| 			ConfigMap: &corev1.ConfigMapVolumeSource{ | ||||
| 				LocalObjectReference: corev1.LocalObjectReference{Name: name}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	volumes := []corev1.Volume{storage, config} | ||||
| 	return volumes | ||||
| } | ||||
| 
 | ||||
| func getVolumesMounts(cr *jenkinsv1alpha2.JenkinsImage) []corev1.VolumeMount { | ||||
| 	name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileStorageSuffix) | ||||
| 	storage := corev1.VolumeMount{ | ||||
| 		Name:      name, | ||||
| 		MountPath: "/workspace", | ||||
| 	} | ||||
| 	name = fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix) | ||||
| 	config := corev1.VolumeMount{ | ||||
| 		Name:      name, | ||||
| 		MountPath: "/workspace/dockerfile", | ||||
| 	} | ||||
| 	volumeMounts := []corev1.VolumeMount{storage, config} | ||||
| 	return volumeMounts | ||||
| } | ||||
|  | @ -0,0 +1,150 @@ | |||
| package jenkinsimage | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	jenkinsv1alpha2 "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | ||||
| 
 | ||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	apierrors "k8s.io/apimachinery/pkg/api/errors" | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/client" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/controller" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/handler" | ||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/log" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/manager" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/source" | ||||
| ) | ||||
| 
 | ||||
| var log = logf.Log.WithName("controller_jenkinsimage") | ||||
| 
 | ||||
| // Add creates a new JenkinsImage Controller and adds it to the Manager. The Manager will set fields on the Controller
 | ||||
| // and Start it when the Manager is Started.
 | ||||
| func Add(mgr manager.Manager) error { | ||||
| 	r := &ReconcileJenkinsImage{client: mgr.GetClient(), scheme: mgr.GetScheme()} | ||||
| 	return add(mgr, r) | ||||
| } | ||||
| 
 | ||||
| // add adds a new Controller to mgr with r as the reconcile.Reconciler
 | ||||
| func add(mgr manager.Manager, r reconcile.Reconciler) error { | ||||
| 	// Create a new controller
 | ||||
| 	c, err := controller.New("jenkinsimage-controller", mgr, controller.Options{Reconciler: r}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Watch for changes to primary resource JenkinsImage
 | ||||
| 	eventHandlerForObject := &handler.EnqueueRequestForObject{} | ||||
| 	src := &source.Kind{Type: &jenkinsv1alpha2.JenkinsImage{}} | ||||
| 	err = c.Watch(src, eventHandlerForObject) | ||||
| 	if err != nil { | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Watch for changes to secondary resource Pods and requeue the owner JenkinsImage
 | ||||
| 	eventHandlerForOwner := &handler.EnqueueRequestForOwner{ | ||||
| 		IsController: true, | ||||
| 		OwnerType:    &jenkinsv1alpha2.JenkinsImage{}, | ||||
| 	} | ||||
| 	err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, eventHandlerForOwner) | ||||
| 	if err != nil { | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 	// Watch for changes to secondary ConfigMap and requeue the owner JenkinsImage
 | ||||
| 	err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, eventHandlerForOwner) | ||||
| 	if err != nil { | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // blank assignment to verify that ReconcileJenkinsImage implements reconcile.Reconciler
 | ||||
| var _ reconcile.Reconciler = &ReconcileJenkinsImage{} | ||||
| 
 | ||||
| // ReconcileJenkinsImage reconciles a JenkinsImage object
 | ||||
| type ReconcileJenkinsImage struct { | ||||
| 	// This client, initialized using mgr.Client() above, is a split client
 | ||||
| 	// that reads objects from the cache and writes to the apiserver
 | ||||
| 	client client.Client | ||||
| 	scheme *runtime.Scheme | ||||
| } | ||||
| 
 | ||||
| // Reconcile reads that state of the cluster for a JenkinsImage object and makes changes based on the state read
 | ||||
| // and what is in the JenkinsImage.Spec
 | ||||
| // The Controller will requeue the Request to be processed again if the returned error is non-nil or
 | ||||
| // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
 | ||||
| func (r *ReconcileJenkinsImage) Reconcile(request reconcile.Request) (reconcile.Result, error) { | ||||
| 	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) | ||||
| 	reqLogger.Info("Reconciling JenkinsImage") | ||||
| 
 | ||||
| 	// Fetch the JenkinsImage instance
 | ||||
| 	instance := &jenkinsv1alpha2.JenkinsImage{} | ||||
| 	err := r.client.Get(context.TODO(), request.NamespacedName, instance) | ||||
| 	if err != nil { | ||||
| 		if apierrors.IsNotFound(err) { | ||||
| 			// Request object not found, could have been deleted after reconcile request.
 | ||||
| 			// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
 | ||||
| 			// Return and don't requeue
 | ||||
| 			return reconcile.Result{}, nil | ||||
| 		} | ||||
| 		// Error reading the object - requeue the request.
 | ||||
| 		return reconcile.Result{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Define a new ConfigMap containing the Dockerfile used to build the image
 | ||||
| 	dockerfile := resources.NewDockerfileConfigMap(instance) | ||||
| 	// Set JenkinsImage instance as the owner and controller
 | ||||
| 	if err := controllerutil.SetControllerReference(instance, dockerfile, r.scheme); err != nil { | ||||
| 		return reconcile.Result{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if this ConfigMap already exists
 | ||||
| 	foundConfigMap := &corev1.ConfigMap{} | ||||
| 	err = r.client.Get(context.TODO(), types.NamespacedName{Name: dockerfile.Name, Namespace: dockerfile.Namespace}, foundConfigMap) | ||||
| 	if err != nil && apierrors.IsNotFound(err) { | ||||
| 		reqLogger.Info("Creating a new ConfigMap", "ConfigMap.Namespace", dockerfile.Namespace, "ConfigMap.Name", dockerfile.Name) | ||||
| 		err = r.client.Create(context.TODO(), dockerfile) | ||||
| 		if err != nil { | ||||
| 			return reconcile.Result{}, err | ||||
| 		} | ||||
| 		// ConfigMap created successfully - don't requeue
 | ||||
| 		return reconcile.Result{}, nil | ||||
| 	} else if err != nil { | ||||
| 		return reconcile.Result{}, err | ||||
| 	} | ||||
| 	// ConfigMap already exists - don't requeue
 | ||||
| 	reqLogger.Info("Skip reconcile: ConfigMap already exists", "ConfigMap.Namespace", foundConfigMap.Namespace, "ConfigMap.Name", foundConfigMap.Name) | ||||
| 
 | ||||
| 	// Define a new Pod object
 | ||||
| 	pod := resources.NewBuilderPod(instance) | ||||
| 	// Set JenkinsImage instance as the owner and controller
 | ||||
| 	if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil { | ||||
| 		return reconcile.Result{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if this Pod already exists
 | ||||
| 	foundPod := &corev1.Pod{} | ||||
| 	err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, foundPod) | ||||
| 	if err != nil && apierrors.IsNotFound(err) { | ||||
| 		reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name) | ||||
| 		err = r.client.Create(context.TODO(), pod) | ||||
| 		if err != nil { | ||||
| 			return reconcile.Result{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		// Pod created successfully - don't requeue
 | ||||
| 		return reconcile.Result{}, nil | ||||
| 	} else if err != nil { | ||||
| 		return reconcile.Result{}, err | ||||
| 	} | ||||
| 	// Pod already exists - don't requeue
 | ||||
| 	reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", foundPod.Namespace, "Pod.Name", foundPod.Name) | ||||
| 
 | ||||
| 	return reconcile.Result{}, nil | ||||
| } | ||||
		Loading…
	
		Reference in New Issue