diff --git a/Gopkg.lock b/Gopkg.lock index 98726a16..b7e480ed 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -609,6 +609,20 @@ revision = "298182f68c66c05229eb03ac171abe6e309ee79a" version = "v1.0.3" +[[projects]] + digest = "1:0b30d1cfbc7b4a319c1dbfdc81388257ef36e044b583450798511d1a3d8f9330" + name = "github.com/tektoncd/pipeline" + packages = [ + "pkg/apis/config", + "pkg/apis/pipeline", + "pkg/apis/pipeline/v1alpha1", + "pkg/list", + "pkg/names", + ] + pruneopts = "NT" + revision = "c105b6835a899ad1cccadc3556c8a260e812d2cf" + version = "v0.6.0" + [[projects]] digest = "1:48127e583ca365f24dd779c4bd74347c78be147b72afed547d45f3b6a65c32c0" name = "go.opencensus.io" @@ -782,6 +796,17 @@ pruneopts = "NT" revision = "9c57229d8a1bf332c4c51b3c55f6d5b924f41f1e" +[[projects]] + branch = "master" + digest = "1:da4baf48e12c5766130e8ec8ae5058cb3b5e8b533606c75340f7fb59604a4018" + name = "golang.org/x/xerrors" + packages = [ + ".", + "internal", + ] + pruneopts = "NT" + revision = "a985d3407aa71f30cf86696ee0a2f409709f22e1" + [[projects]] branch = "master" digest = "1:b03f1529de9a7039cead8074d2b9c527769f5a67c1ce3dd3ee03a38867328633" @@ -999,6 +1024,7 @@ "pkg/util/mergepatch", "pkg/util/naming", "pkg/util/net", + "pkg/util/rand", "pkg/util/runtime", "pkg/util/sets", "pkg/util/strategicpatch", @@ -1359,6 +1385,7 @@ "github.com/operator-framework/operator-sdk/pkg/restmapper", "github.com/operator-framework/operator-sdk/version", "github.com/spf13/pflag", + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1", "k8s.io/api/core/v1", "k8s.io/apimachinery/pkg/api/errors", "k8s.io/apimachinery/pkg/apis/meta/v1", diff --git a/cmd/manager/main.go b/cmd/manager/main.go index fe76c456..13771c0e 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -12,6 +12,9 @@ import ( knv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1" knv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1" + // Import tekton types + pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -123,6 +126,11 @@ func main() { os.Exit(1) } + if err := pipeline.AddToScheme(mgr.GetScheme()); err != nil { + log.Error(err, "Can't register the tekton pipeline scheme") + os.Exit(1) + } + // Setup all Controllers if err := controller.AddToManager(mgr); err != nil { log.Error(err, "") diff --git a/deploy/build/js-function-build-task.yaml b/deploy/build/js-function-build-task.yaml new file mode 100644 index 00000000..cc1d0b98 --- /dev/null +++ b/deploy/build/js-function-build-task.yaml @@ -0,0 +1,69 @@ +apiVersion: tekton.dev/v1alpha1 +kind: Task +metadata: + name: js-function-build-runtime +spec: + inputs: + params: + - name: FUNCTION_NAME + description: The name of the function being built + default: 'user-function' + - name: TLSVERIFY + description: Verify the TLS on the registry endpoint (default false) + default: 'false' + outputs: + resources: + - name: image + type: image + steps: + - name: copy-source + image: docker.io/alpine + command: ['cp', '-fpv', '/fn-source/index.js', '/fn-source/package.json', '/home/node/usr/'] + volumeMounts: + - name: sourcedir + mountPath: /fn-source + - name: gen-source + mountPath: /home/node/usr + securityContext: + privileged: true + - name: generate + image: quay.io/openshift-pipeline/s2i + workingdir: '/home/node/usr' + command: ['s2i', 'build', '.', 'quay.io/lanceball/js-runtime', '--as-dockerfile', '/home/node/build/Dockerfile.gen'] + volumeMounts: + - name: gen-source + mountPath: /home/node/usr + - name: buildpath + mountPath: /home/node/build + securityContext: + privileged: true + - name: build + image: quay.io/buildah/stable + workingdir: /home/node/build + command: ['buildah', 'bud', '--tls-verify=${inputs.params.TLSVERIFY}', '--layers', '-f', '/Dockerfile.gen', '-t', '${outputs.resources.image.url}', '.'] + volumeMounts: + - name: varlibcontainers + mountPath: /var/lib/containers + - name: buildpath + mountPath: /home/node/build + securityContext: + privileged: true + - name: push + image: quay.io/buildah/stable + command: ['buildah', 'push', '--tls-verify=${inputs.params.TLSVERIFY}', '${outputs.resources.image.url}', 'docker://${outputs.resources.image.url}'] + volumeMounts: + - name: varlibcontainers + mountPath: /var/lib/containers + securityContext: + privileged: true + + volumes: + - name: varlibcontainers + emptyDir: {} + - name: gen-source + emptyDir: {} + - name: buildpath + emptyDir: {} + - name: sourcedir + configMap: + name: ${inputs.params.FUNCTION_NAME} diff --git a/deploy/operator.yaml b/deploy/operator.yaml index fcc2d928..e1e5e4e6 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -16,7 +16,7 @@ spec: containers: - name: js-function-operator # Replace this with the built image name - image: docker.io/lanceball/js-function-operator:v0.0.2 + image: quay.io/lanceball/js-function-operator:v0.0.2 command: - js-function-operator imagePullPolicy: Always diff --git a/deploy/role.yaml b/deploy/role.yaml index 96233e5e..2f5b89f7 100644 --- a/deploy/role.yaml +++ b/deploy/role.yaml @@ -31,6 +31,12 @@ rules: - services verbs: - '*' +- apiGroups: + - tekton.dev + resources: + - tasks + verbs: + - '*' - apiGroups: - monitoring.coreos.com resources: diff --git a/pkg/controller/jsfunction/jsfunction_controller.go b/pkg/controller/jsfunction/jsfunction_controller.go index 7dfd01ad..f49da825 100644 --- a/pkg/controller/jsfunction/jsfunction_controller.go +++ b/pkg/controller/jsfunction/jsfunction_controller.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" kneventing "knative.dev/eventing/pkg/apis/eventing/v1alpha1" knv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1" knv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1" @@ -134,8 +135,19 @@ func (r *ReconcileJSFunction) Reconcile(request reconcile.Request) (reconcile.Re return reconcile.Result{}, err } + reqLogger.Info("Creating TaskRun for function build.") + build, err := r.buildForFunction(function) + if err != nil { + return reconcile.Result{}, err + } + err = r.client.Create(context.TODO(), build) + if err != nil { + reqLogger.Error(err, "Failed to create TaskRun for function build.", "Service.Namespace", build.Namespace, "ConfigMap.Name", build.Name) + return reconcile.Result{}, err + } + // Create service, mounting the config map - service, err := r.serviceForFunction(function, configMap.Name) + service, err := r.serviceForFunction(function, configMap.Name, runtimeImageForFunction(function)) if err != nil { return reconcile.Result{}, err } @@ -216,6 +228,49 @@ func (r *ReconcileJSFunction) Reconcile(request reconcile.Request) (reconcile.Re return reconcile.Result{}, nil } +func (r *ReconcileJSFunction) buildForFunction(f *faasv1alpha1.JSFunction) (*pipeline.TaskRun, error) { + imageName := runtimeImageForFunction(f) + taskRun := &pipeline.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-build", f.Name), + Namespace: f.Namespace, + }, + Spec: pipeline.TaskRunSpec{ + ServiceAccount: "js-function-operator", + TaskRef: &pipeline.TaskRef{ + Name: "js-function-build-runtime", + }, + Inputs: pipeline.TaskRunInputs{ + Params: []pipeline.Param{{ + Name: "FUNCTION_NAME", + Value: pipeline.ArrayOrString{ + Type: "string", + StringVal: f.Name, + }, + }}, + }, + Outputs: pipeline.TaskRunOutputs{ + Resources: []pipeline.TaskResourceBinding{ + { + Name: "image", + ResourceSpec: &pipeline.PipelineResourceSpec{ + Type: "image", + Params: []pipeline.ResourceParam{{ + Name: "url", + Value: imageName, + }}, + }, + }, + }, + }, + }, + } + if err := controllerutil.SetControllerReference(f, taskRun, r.scheme); err != nil { + return nil, err + } + return taskRun, nil +} + func (r *ReconcileJSFunction) configMapWithFunction(f *faasv1alpha1.JSFunction) (*corev1.ConfigMap, error) { data := map[string]string{"index.js": f.Spec.Func} @@ -238,7 +293,7 @@ func (r *ReconcileJSFunction) configMapWithFunction(f *faasv1alpha1.JSFunction) return configMap, nil } -func (r *ReconcileJSFunction) serviceForFunction(f *faasv1alpha1.JSFunction, configMapName string) (*knv1alpha1.Service, error) { +func (r *ReconcileJSFunction) serviceForFunction(f *faasv1alpha1.JSFunction, configMapName string, imageName string) (*knv1alpha1.Service, error) { service := &knv1alpha1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: f.Name, @@ -252,7 +307,7 @@ func (r *ReconcileJSFunction) serviceForFunction(f *faasv1alpha1.JSFunction, con }, Spec: knv1alpha1.RevisionSpec{ RevisionSpec: knv1beta1.RevisionSpec{ - PodSpec: createPodSpec(f.Name, configMapName), + PodSpec: createPodSpec(f.Name, imageName), }, }, }, @@ -269,25 +324,15 @@ func (r *ReconcileJSFunction) serviceForFunction(f *faasv1alpha1.JSFunction, con return service, nil } -func createPodSpec(functionName, configMapName string) corev1.PodSpec { - volumeName := fmt.Sprintf("%s-source", functionName) +func createPodSpec(functionName, imageName string) corev1.PodSpec { return corev1.PodSpec{ Containers: []corev1.Container{{ - Image: "docker.io/zroubalik/js-runtime", + Image: imageName, Name: fmt.Sprintf("nodejs-%s", functionName), Ports: []corev1.ContainerPort{{ ContainerPort: 8080, }}, - VolumeMounts: []corev1.VolumeMount{ - { - Name: volumeName, - MountPath: "/home/node/usr", - }, - }, }}, - Volumes: []corev1.Volume{ - createConfigMapVolume(volumeName, configMapName), - }, } } @@ -357,3 +402,7 @@ func (r *ReconcileJSFunction) subscriptionForFunction(f *faasv1alpha1.JSFunction return subscription, nil } + +func runtimeImageForFunction(f *faasv1alpha1.JSFunction) string { + return fmt.Sprintf("image-registry.openshift-image-registry.svc:5000/%s/%s-runtime", f.Namespace, f.Name) +}