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
|
||||||
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 \
|
-root=$(CURRENT_DIRECTORY) -kubeconfig=$(HOME)/.kube/config -globalMan deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml \
|
||||||
-namespacedMan deploy/namespace-init.yaml $(TEST_ARGS)
|
-namespacedMan deploy/namespace-init.yaml $(TEST_ARGS)
|
||||||
|
|
||||||
|
|
@ -251,6 +251,7 @@ ifeq ($(KUBERNETES_PROVIDER),crc)
|
||||||
oc project $(CRC_OC_PROJECT)
|
oc project $(CRC_OC_PROJECT)
|
||||||
endif
|
endif
|
||||||
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
|
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"
|
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
|
||||||
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
|
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkinsimage"
|
||||||
|
|
||||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "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 {
|
if err := jenkins.Add(mgr, jenkinsAPIConnectionSettings, *clientSet, *cfg, &c); err != nil {
|
||||||
fatal(errors.Wrap(err, "failed to setup controllers"), *debug)
|
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 {
|
if err = serveCRMetrics(cfg); err != nil {
|
||||||
log.Log.V(log.VWarn).Info("Could not generate and serve custom resource metrics", "error", err.Error())
|
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
|
DOCKER_REGISTRY=jenkins-operator
|
||||||
NAMESPACE=default
|
NAMESPACE=default
|
||||||
API_VERSION=v1alpha2
|
API_VERSION=v1alpha2
|
||||||
|
API_VERSION_NEXT=v1alpha3
|
||||||
ALL_IN_ONE_DEPLOY_FILE_PREFIX=all-in-one
|
ALL_IN_ONE_DEPLOY_FILE_PREFIX=all-in-one
|
||||||
GEN_CRD_API=gen-crd-api-reference-docs
|
GEN_CRD_API=gen-crd-api-reference-docs
|
||||||
IMAGE_PULL_MODE=local
|
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
|
- name : v1alpha1
|
||||||
served: true
|
served: true
|
||||||
storage: false
|
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() {
|
func init() {
|
||||||
SchemeBuilder.Register(&Jenkins{}, &JenkinsList{})
|
SchemeBuilder.Register(&Jenkins{}, &JenkinsList{})
|
||||||
|
SchemeBuilder.Register(&JenkinsImage{}, &JenkinsImageList{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,22 @@ func (in *Handler) DeepCopy() *Handler {
|
||||||
return out
|
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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Jenkins) DeepCopyInto(out *Jenkins) {
|
func (in *Jenkins) DeepCopyInto(out *Jenkins) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
@ -253,6 +269,110 @@ func (in *JenkinsAPISettings) DeepCopy() *JenkinsAPISettings {
|
||||||
return out
|
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.
|
// 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
|
||||||
|
|
@ -371,6 +491,22 @@ func (in *JenkinsMaster) DeepCopy() *JenkinsMaster {
|
||||||
return out
|
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.
|
// 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
|
||||||
|
|
|
||||||
|
|
@ -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