mirror of https://github.com/knative/func.git
feat: S2I strategy for on cluster build (#1191)
* feat: S2I strategy for on cluster build Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com> * use upstream `s2i` task only Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com> * use custom func-s2i task Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com> Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com>
This commit is contained in:
parent
e6ec11b0e5
commit
1112aaa2fe
|
@ -57,8 +57,9 @@ func NewClientFactory(n func() *fn.Client) ClientFactory {
|
|||
// the currently configured is used.
|
||||
// 'Verbose' indicates the system should write out a higher amount of logging.
|
||||
// Example:
|
||||
// client, done := NewClient("",false)
|
||||
// defer done()
|
||||
//
|
||||
// client, done := NewClient("",false)
|
||||
// defer done()
|
||||
func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) {
|
||||
var (
|
||||
p = progress.New(cfg.Verbose) // updates the CLI
|
||||
|
@ -143,7 +144,6 @@ func newTektonPipelinesProvider(namespace string, progress *progress.Bar, creds
|
|||
}
|
||||
|
||||
return tekton.NewPipelinesProvider(options...)
|
||||
|
||||
}
|
||||
|
||||
func newKnativeDeployer(namespace string, verbose bool) fn.Deployer {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Building Functions on Cluster with Tekton Pipelines
|
||||
|
||||
This guide describes how you can build a Function on Cluster with Tekton Pipelines. The on cluster build is enabled by fetching Function source code from a remote Git repository.
|
||||
This guide describes how you can build a Function on Cluster with Tekton Pipelines. The on cluster build is enabled by fetching Function source code from a remote Git repository. Buildpacks or S2I builder strategy can be used to build the Function image.
|
||||
|
||||
## Prerequisite
|
||||
1. Install Tekton Pipelines on the cluster. Please refer to [Tekton Pipelines documentation](https://github.com/tektoncd/pipeline/blob/main/docs/install.md) or run the following command:
|
||||
|
@ -14,10 +14,15 @@ In each namespace that you would like to run Pipelines and deploy a Function you
|
|||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-clone/0.4/git-clone.yaml
|
||||
```
|
||||
2. Install the Functions Buildpacks Tekton Task to be able to build the Function image:
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml
|
||||
```
|
||||
2. Install a Tekton Task responsible for building the Function, based on the builder preference (Buildpacks or S2I)
|
||||
1. For Buildpacks builder install the Functions Buildpacks Tekton Task:
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml
|
||||
```
|
||||
2. For S2I builder install the S2I task:
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml
|
||||
```
|
||||
3. Install the `kn func` Deploy Tekton Task to be able to deploy the Function on in the Pipeline:
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml
|
||||
|
@ -82,6 +87,7 @@ export NAMESPACE=<INSERT_YOUR_NAMESPACE>
|
|||
kubectl delete clusterrolebinding $NAMESPACE:knative-serving-namespaced-admin
|
||||
kubectl delete task.tekton.dev git-clone
|
||||
kubectl delete task.tekton.dev func-buildpacks
|
||||
kubectl delete task.tekton.dev func-s2i
|
||||
kubectl delete task.tekton.dev func-deploy
|
||||
```
|
||||
2. Uninstall Tekton Pipelines
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
apiVersion: tekton.dev/v1beta1
|
||||
kind: Task
|
||||
metadata:
|
||||
name: func-s2i
|
||||
labels:
|
||||
app.kubernetes.io/version: "0.1"
|
||||
annotations:
|
||||
tekton.dev/pipelines.minVersion: "0.17.0"
|
||||
tekton.dev/categories: Image Build
|
||||
tekton.dev/tags: image-build
|
||||
tekton.dev/platforms: "linux/amd64"
|
||||
spec:
|
||||
description: >-
|
||||
Knative Functions Source-to-Image (S2I) is a toolkit and workflow for building reproducible
|
||||
container images from source code
|
||||
|
||||
S2I produces images by injecting source code into a base S2I container image
|
||||
and letting the container prepare that source code for execution. The base
|
||||
S2I container images contains the language runtime and build tools needed for
|
||||
building and running the source code.
|
||||
|
||||
params:
|
||||
- name: BUILDER_IMAGE
|
||||
description: The location of the s2i builder image.
|
||||
- name: IMAGE
|
||||
description: Reference of the image S2I will produce.
|
||||
- name: PATH_CONTEXT
|
||||
description: The location of the path to run s2i from.
|
||||
default: .
|
||||
- name: TLSVERIFY
|
||||
description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry)
|
||||
default: "true"
|
||||
- name: LOGLEVEL
|
||||
description: Log level when running the S2I binary
|
||||
default: "0"
|
||||
- name: ENV_VARS
|
||||
type: array
|
||||
description: Environment variables to set during _build-time_.
|
||||
default: []
|
||||
workspaces:
|
||||
- name: source
|
||||
- name: sslcertdir
|
||||
optional: true
|
||||
- name: dockerconfig
|
||||
description: >-
|
||||
An optional workspace that allows providing a .docker/config.json file
|
||||
for Buildah to access the container registry.
|
||||
The file should be placed at the root of the Workspace with name config.json.
|
||||
optional: true
|
||||
results:
|
||||
- name: IMAGE_DIGEST
|
||||
description: Digest of the image just built.
|
||||
steps:
|
||||
- name: generate
|
||||
image: quay.io/openshift-pipeline/s2i:nightly
|
||||
workingDir: $(workspaces.source.path)
|
||||
args: ["$(params.ENV_VARS[*])"]
|
||||
script: |
|
||||
echo "Processing Build Environment Variables"
|
||||
echo "" > /env-vars/env-file
|
||||
for var in "$@"
|
||||
do
|
||||
echo "$var" >> /env-vars/env-file
|
||||
done
|
||||
|
||||
echo "Generated Build Env Var file"
|
||||
echo "------------------------------"
|
||||
cat /env-vars/env-file
|
||||
echo "------------------------------"
|
||||
|
||||
/usr/local/bin/s2i --loglevel=$(params.LOGLEVEL) build $(params.PATH_CONTEXT) $(params.BUILDER_IMAGE) \
|
||||
--as-dockerfile /gen-source/Dockerfile.gen --environment-file /env-vars/env-file
|
||||
volumeMounts:
|
||||
- mountPath: /gen-source
|
||||
name: gen-source
|
||||
- mountPath: /env-vars
|
||||
name: env-vars
|
||||
- name: build
|
||||
image: quay.io/buildah/stable:v1.17.0
|
||||
workingDir: /gen-source
|
||||
script: |
|
||||
[[ "$(workspaces.sslcertdir.bound)" == "true" ]] && CERT_DIR_FLAG="--cert-dir $(workspaces.sslcertdir.path)"
|
||||
buildah ${CERT_DIR_FLAG} bud --storage-driver=vfs --tls-verify=$(params.TLSVERIFY) --layers \
|
||||
-f /gen-source/Dockerfile.gen -t $(params.IMAGE) .
|
||||
|
||||
[[ "$(workspaces.dockerconfig.bound)" == "true" ]] && export DOCKER_CONFIG="$(workspaces.dockerconfig.path)"
|
||||
buildah ${CERT_DIR_FLAG} push --storage-driver=vfs --tls-verify=$(params.TLSVERIFY) --digestfile $(workspaces.source.path)/image-digest \
|
||||
$(params.IMAGE) docker://$(params.IMAGE)
|
||||
|
||||
cat $(workspaces.source.path)/image-digest | tee /tekton/results/IMAGE_DIGEST
|
||||
volumeMounts:
|
||||
- name: varlibcontainers
|
||||
mountPath: /var/lib/containers
|
||||
- mountPath: /gen-source
|
||||
name: gen-source
|
||||
securityContext:
|
||||
capabilities:
|
||||
add: ["SETFCAP"]
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: varlibcontainers
|
||||
- emptyDir: {}
|
||||
name: gen-source
|
||||
- emptyDir: {}
|
||||
name: env-vars
|
|
@ -12,6 +12,7 @@ import (
|
|||
fn "knative.dev/kn-plugin-func"
|
||||
"knative.dev/kn-plugin-func/builders"
|
||||
"knative.dev/kn-plugin-func/buildpacks"
|
||||
"knative.dev/kn-plugin-func/s2i"
|
||||
)
|
||||
|
||||
func deletePipelines(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
|
||||
|
@ -33,6 +34,8 @@ func deletePipelineRuns(ctx context.Context, namespaceOverride string, listOptio
|
|||
}
|
||||
|
||||
func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline {
|
||||
|
||||
// ----- General properties
|
||||
pipelineName := getPipelineName(f)
|
||||
|
||||
params := []pplnv1beta1.ParamSpec{
|
||||
|
@ -56,7 +59,7 @@ func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipe
|
|||
},
|
||||
{
|
||||
Name: "builderImage",
|
||||
Description: "Buildpacks builder image to be used",
|
||||
Description: "Builder image to be used",
|
||||
},
|
||||
{
|
||||
Name: "buildEnvs",
|
||||
|
@ -67,14 +70,30 @@ func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipe
|
|||
|
||||
workspaces := []pplnv1beta1.PipelineWorkspaceDeclaration{
|
||||
{Name: "source-workspace", Description: "Directory where function source is located."},
|
||||
{Name: "cache-workspace", Description: "Directory where Buildpacks cache is stored."},
|
||||
{Name: "dockerconfig-workspace", Description: "Directory containing image registry credentials stored in `config.json` file.", Optional: true},
|
||||
}
|
||||
|
||||
var taskBuild pplnv1beta1.PipelineTask
|
||||
|
||||
// Deploy step that uses an image produced by S2I builds needs explicit reference to the image
|
||||
referenceImageFromPreviousTaskResults := false
|
||||
|
||||
if f.Builder == builders.Pack {
|
||||
// ----- Buildpacks related properties
|
||||
workspaces = append(workspaces, pplnv1beta1.PipelineWorkspaceDeclaration{Name: "cache-workspace", Description: "Directory where Buildpacks cache is stored."})
|
||||
taskBuild = taskBuildpacks(taskNameFetchSources)
|
||||
|
||||
} else if f.Builder == builders.S2I {
|
||||
// ----- S2I build related properties
|
||||
taskBuild = taskS2iBuild(taskNameFetchSources)
|
||||
referenceImageFromPreviousTaskResults = true
|
||||
}
|
||||
|
||||
// ----- Pipeline definition
|
||||
tasks := pplnv1beta1.PipelineTaskList{
|
||||
taskFetchSources(),
|
||||
taskBuild(taskNameFetchSources),
|
||||
taskDeploy(taskNameBuild),
|
||||
taskBuild,
|
||||
taskDeploy(taskNameBuild, referenceImageFromPreviousTaskResults),
|
||||
}
|
||||
|
||||
return &pplnv1beta1.Pipeline{
|
||||
|
@ -92,11 +111,15 @@ func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipe
|
|||
|
||||
func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.PipelineRun {
|
||||
|
||||
// ----- General properties
|
||||
revision := ""
|
||||
if f.Git.Revision != nil {
|
||||
revision = *f.Git.Revision
|
||||
}
|
||||
contextDir := ""
|
||||
if f.Builder == builders.S2I {
|
||||
contextDir = "."
|
||||
}
|
||||
if f.Git.ContextDir != nil {
|
||||
contextDir = *f.Git.ContextDir
|
||||
}
|
||||
|
@ -113,66 +136,73 @@ func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.P
|
|||
buildEnvs = pplnv1beta1.NewArrayOrString(envs[0], envs[1:]...)
|
||||
}
|
||||
|
||||
params := []pplnv1beta1.Param{
|
||||
{
|
||||
Name: "gitRepository",
|
||||
Value: *pplnv1beta1.NewArrayOrString(*f.Git.URL),
|
||||
},
|
||||
{
|
||||
Name: "gitRevision",
|
||||
Value: *pplnv1beta1.NewArrayOrString(revision),
|
||||
},
|
||||
{
|
||||
Name: "contextDir",
|
||||
Value: *pplnv1beta1.NewArrayOrString(contextDir),
|
||||
},
|
||||
{
|
||||
Name: "imageName",
|
||||
Value: *pplnv1beta1.NewArrayOrString(f.Image),
|
||||
},
|
||||
{
|
||||
Name: "builderImage",
|
||||
Value: *pplnv1beta1.NewArrayOrString(getBuilderImage(f)),
|
||||
},
|
||||
{
|
||||
Name: "buildEnvs",
|
||||
Value: *buildEnvs,
|
||||
},
|
||||
}
|
||||
|
||||
workspaces := []pplnv1beta1.WorkspaceBinding{
|
||||
{
|
||||
Name: "source-workspace",
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: getPipelinePvcName(f),
|
||||
},
|
||||
SubPath: "source",
|
||||
},
|
||||
{
|
||||
Name: "dockerconfig-workspace",
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: getPipelineSecretName(f),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if f.Builder == builders.Pack {
|
||||
// ----- Buildpacks related properties
|
||||
|
||||
workspaces = append(workspaces, pplnv1beta1.WorkspaceBinding{
|
||||
Name: "cache-workspace",
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: getPipelinePvcName(f),
|
||||
},
|
||||
SubPath: "cache",
|
||||
})
|
||||
}
|
||||
|
||||
// ----- PipelineRun definition
|
||||
return &pplnv1beta1.PipelineRun{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)),
|
||||
Labels: labels,
|
||||
},
|
||||
|
||||
Spec: pplnv1beta1.PipelineRunSpec{
|
||||
PipelineRef: &pplnv1beta1.PipelineRef{
|
||||
Name: getPipelineName(f),
|
||||
},
|
||||
|
||||
Params: []pplnv1beta1.Param{
|
||||
{
|
||||
Name: "gitRepository",
|
||||
Value: *pplnv1beta1.NewArrayOrString(*f.Git.URL),
|
||||
},
|
||||
{
|
||||
Name: "gitRevision",
|
||||
Value: *pplnv1beta1.NewArrayOrString(revision),
|
||||
},
|
||||
{
|
||||
Name: "contextDir",
|
||||
Value: *pplnv1beta1.NewArrayOrString(contextDir),
|
||||
},
|
||||
{
|
||||
Name: "imageName",
|
||||
Value: *pplnv1beta1.NewArrayOrString(f.Image),
|
||||
},
|
||||
{
|
||||
Name: "builderImage",
|
||||
Value: *pplnv1beta1.NewArrayOrString(getBuilderImage(f)),
|
||||
},
|
||||
{
|
||||
Name: "buildEnvs",
|
||||
Value: *buildEnvs,
|
||||
},
|
||||
},
|
||||
|
||||
Workspaces: []pplnv1beta1.WorkspaceBinding{
|
||||
{
|
||||
Name: "source-workspace",
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: getPipelinePvcName(f),
|
||||
},
|
||||
SubPath: "source",
|
||||
},
|
||||
{
|
||||
Name: "cache-workspace",
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: getPipelinePvcName(f),
|
||||
},
|
||||
SubPath: "cache",
|
||||
},
|
||||
{
|
||||
Name: "dockerconfig-workspace",
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: getPipelineSecretName(f),
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: params,
|
||||
Workspaces: workspaces,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -182,12 +212,16 @@ func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.P
|
|||
// language runtime. Errors are checked elsewhere, so at this level they
|
||||
// manifest as an inability to get a builder image = empty string.
|
||||
func getBuilderImage(f fn.Function) (name string) {
|
||||
name, _ = buildpacks.BuilderImage(f, builders.Pack)
|
||||
if f.Builder == builders.S2I {
|
||||
name, _ = s2i.BuilderImage(f, builders.S2I)
|
||||
} else {
|
||||
name, _ = buildpacks.BuilderImage(f, builders.Pack)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getPipelineName(f fn.Function) string {
|
||||
return fmt.Sprintf("%s-%s-pipeline", f.Name, f.BuildType)
|
||||
return fmt.Sprintf("%s-%s-%s-pipeline", f.Name, f.BuildType, f.Builder)
|
||||
}
|
||||
|
||||
func getPipelineSecretName(f fn.Function) string {
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package tekton
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fn "knative.dev/kn-plugin-func"
|
||||
"knative.dev/kn-plugin-func/builders"
|
||||
)
|
||||
|
||||
func Test_generatePipeline(t *testing.T) {
|
||||
testGitRepo := "http://git-repo/git.git"
|
||||
testGit := fn.Git{
|
||||
URL: &testGitRepo,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
function fn.Function
|
||||
taskBuildName string
|
||||
}{
|
||||
{
|
||||
name: "Pack builder - use buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Git: testGit},
|
||||
taskBuildName: "func-buildpacks",
|
||||
},
|
||||
{
|
||||
name: "s2i builder - use",
|
||||
function: fn.Function{Builder: builders.S2I, Git: testGit},
|
||||
taskBuildName: "func-s2i",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ppl := generatePipeline(tt.function, map[string]string{})
|
||||
|
||||
for _, task := range ppl.Spec.Tasks {
|
||||
// let's check what is the Task used for build task
|
||||
if task.Name == taskNameBuild {
|
||||
if task.TaskRef.Name != tt.taskBuildName {
|
||||
t.Errorf("generatePipeline(), for builder = %q: wanted build Task = %q, got = %q", tt.function.Builder, tt.taskBuildName, task.TaskRef.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// we haven't found the build Task -> fail
|
||||
t.Errorf("generatePipeline(), wasn't able to find build related task named = %q", taskNameBuild)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package tekton
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
|
||||
)
|
||||
|
||||
|
@ -27,7 +29,7 @@ func taskFetchSources() pplnv1beta1.PipelineTask {
|
|||
}
|
||||
}
|
||||
|
||||
func taskBuild(runAfter string) pplnv1beta1.PipelineTask {
|
||||
func taskBuildpacks(runAfter string) pplnv1beta1.PipelineTask {
|
||||
return pplnv1beta1.PipelineTask{
|
||||
Name: taskNameBuild,
|
||||
TaskRef: &pplnv1beta1.TaskRef{
|
||||
|
@ -57,9 +59,47 @@ func taskBuild(runAfter string) pplnv1beta1.PipelineTask {
|
|||
}},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
func taskS2iBuild(runAfter string) pplnv1beta1.PipelineTask {
|
||||
params := []pplnv1beta1.Param{
|
||||
{Name: "IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.imageName)")},
|
||||
{Name: "PATH_CONTEXT", Value: *pplnv1beta1.NewArrayOrString("$(params.contextDir)")},
|
||||
{Name: "BUILDER_IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.builderImage)")},
|
||||
{Name: "ENV_VARS", Value: pplnv1beta1.ArrayOrString{
|
||||
Type: pplnv1beta1.ParamTypeArray,
|
||||
ArrayVal: []string{"$(params.buildEnvs[*])"},
|
||||
}},
|
||||
}
|
||||
return pplnv1beta1.PipelineTask{
|
||||
Name: taskNameBuild,
|
||||
TaskRef: &pplnv1beta1.TaskRef{
|
||||
Name: "func-s2i",
|
||||
},
|
||||
RunAfter: []string{runAfter},
|
||||
Workspaces: []pplnv1beta1.WorkspacePipelineTaskBinding{
|
||||
{
|
||||
Name: "source",
|
||||
Workspace: "source-workspace",
|
||||
},
|
||||
{
|
||||
Name: "dockerconfig",
|
||||
Workspace: "dockerconfig-workspace",
|
||||
}},
|
||||
Params: params,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func taskDeploy(runAfter string) pplnv1beta1.PipelineTask {
|
||||
func taskDeploy(runAfter string, referenceImageFromPreviousTaskResults bool) pplnv1beta1.PipelineTask {
|
||||
|
||||
params := []pplnv1beta1.Param{{Name: "path", Value: *pplnv1beta1.NewArrayOrString("$(workspaces.source.path)/$(params.contextDir)")}}
|
||||
|
||||
// Deploy step that uses an image produced by S2I builds needs explicit reference to the image
|
||||
if referenceImageFromPreviousTaskResults {
|
||||
params = append(params, pplnv1beta1.Param{Name: "image", Value: *pplnv1beta1.NewArrayOrString(fmt.Sprintf("$(params.imageName)@$(tasks.%s.results.IMAGE_DIGEST)", runAfter))})
|
||||
}
|
||||
|
||||
return pplnv1beta1.PipelineTask{
|
||||
Name: taskNameDeploy,
|
||||
TaskRef: &pplnv1beta1.TaskRef{
|
||||
|
@ -70,8 +110,6 @@ func taskDeploy(runAfter string) pplnv1beta1.PipelineTask {
|
|||
Name: "source",
|
||||
Workspace: "source-workspace",
|
||||
}},
|
||||
Params: []pplnv1beta1.Param{
|
||||
{Name: "path", Value: *pplnv1beta1.NewArrayOrString("$(workspaces.source.path)/$(params.contextDir)")},
|
||||
},
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
fn "knative.dev/kn-plugin-func"
|
||||
"knative.dev/kn-plugin-func/builders"
|
||||
"knative.dev/kn-plugin-func/s2i"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -23,16 +25,23 @@ func (e ErrRuntimeNotSupported) Error() string {
|
|||
}
|
||||
|
||||
func validatePipeline(f fn.Function) error {
|
||||
if f.Runtime == "" {
|
||||
return ErrRuntimeRequired
|
||||
}
|
||||
if f.Builder == builders.Pack {
|
||||
if f.Runtime == "" {
|
||||
return ErrRuntimeRequired
|
||||
}
|
||||
|
||||
if f.Runtime == "go" || f.Runtime == "rust" {
|
||||
return ErrRuntimeNotSupported{f.Runtime}
|
||||
}
|
||||
if f.Runtime == "go" || f.Runtime == "rust" {
|
||||
return ErrRuntimeNotSupported{f.Runtime}
|
||||
}
|
||||
|
||||
if len(f.Buildpacks) > 0 {
|
||||
return ErrBuilpacksNotSupported
|
||||
if len(f.Buildpacks) > 0 {
|
||||
return ErrBuilpacksNotSupported
|
||||
}
|
||||
} else if f.Builder == builders.S2I {
|
||||
_, err := s2i.BuilderImage(f, builders.S2I)
|
||||
return err
|
||||
} else {
|
||||
return builders.ErrUnknownBuilder{Name: f.Builder}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
fn "knative.dev/kn-plugin-func"
|
||||
"knative.dev/kn-plugin-func/builders"
|
||||
)
|
||||
|
||||
func Test_validatePipeline(t *testing.T) {
|
||||
|
@ -19,45 +20,90 @@ func Test_validatePipeline(t *testing.T) {
|
|||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Without runtime - without additional Buildpacks",
|
||||
name: "Without runtime - without builder - without additional Buildpacks",
|
||||
function: fn.Function{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without runtime - with additional Buildpacks",
|
||||
name: "Without runtime - pack builder - without additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without runtime - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without runtime - without builder - with additional Buildpacks",
|
||||
function: fn.Function{Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - without additional Buildpacks",
|
||||
name: "Without runtime - pack builder - with additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without runtime - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I, Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - without builder - without additional Buildpacks",
|
||||
function: fn.Function{Runtime: "node"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - pack builder - without additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Runtime: "node"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - with additional Buildpacks",
|
||||
function: fn.Function{Runtime: "node", Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Go - without additional Buildpacks",
|
||||
function: fn.Function{Runtime: "go"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - Quarkus - without additional Buildpacks",
|
||||
function: fn.Function{Runtime: "quarkus"},
|
||||
name: "Supported runtime - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I, Runtime: "node"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Rust - without additional Buildpacks",
|
||||
function: fn.Function{Runtime: "rust"},
|
||||
name: "Supported runtime - pack builder - with additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Runtime: "node", Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Go - with additional Buildpacks",
|
||||
name: "Unsupported runtime - Go - pack builder - without additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Runtime: "go"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Go - pack builder - with additional Buildpacks",
|
||||
function: fn.Function{Runtime: "go", Buildpacks: testBuildpacks},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Go - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I, Runtime: "go"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - Quarkus - pack builder - without additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Runtime: "quarkus"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Supported runtime - Quarkus - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I, Runtime: "quarkus"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Rust - pack builder - without additional Buildpacks",
|
||||
function: fn.Function{Builder: builders.Pack, Runtime: "rust"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported runtime - Rust - s2i builder",
|
||||
function: fn.Function{Builder: builders.S2I, Runtime: "rust"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -31,6 +31,62 @@ import (
|
|||
. "knative.dev/kn-plugin-func/testing"
|
||||
)
|
||||
|
||||
// Test_BuildImages ensures that supported runtimes returns builder image
|
||||
func Test_BuildImages(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
function fn.Function
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Without builder - without runtime",
|
||||
function: fn.Function{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without builder - supported runtime - node",
|
||||
function: fn.Function{Runtime: "node"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Without builder - supported runtime - typescript",
|
||||
function: fn.Function{Runtime: "typescript"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Without builder - supported runtime - quarkus",
|
||||
function: fn.Function{Runtime: "quarkus"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Without builder - unsupported runtime - go",
|
||||
function: fn.Function{Runtime: "go"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without builder - unsupported runtime - python",
|
||||
function: fn.Function{Runtime: "python"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Without builder - unsupported runtime - rust",
|
||||
function: fn.Function{Runtime: "rust"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := s2i.BuilderImage(tt.function, builders.S2I)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("BuilderImage() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test_BuilderImageDefault ensures that a function being built which does not
|
||||
// define a Builder Image will default.
|
||||
func Test_BuilderImageDefault(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue