Repository: knative-sandbox/kn-plugin-func (go)

Issue:
On-cluster build: Apply annotations and labels from `func.yaml` to all created Pipeline resources

Pull request:
Adds labels and annotations from func.yaml to pipeline resources
# Changes

- 🎁 labels from func.yaml are added to Pipeline resources

/kind enhancement

Fixes #1192 

**Release Note**

```release-note
Labels and annotations from func.yaml are added to Pipeline resources
```


Patch:
diff --git a/docs/reference/podman.md b/docs/reference/podman.md
index c4892c29..a875f22d 100644
--- a/docs/reference/podman.md
+++ b/docs/reference/podman.md
@@ -26,6 +26,8 @@ Now you may use `func` as usual.
 
 For macOS and Windows we recommend [Podman Desktop](https://podman-desktop.io/).
 
+It's recommended to run podman machine in rootful mode (`podman machine set --rootful`).
+
 The docs will guide you trough `podman machine` setup.
 The `podman machine start` command it will output path to API socket.
 
diff --git a/function.go b/function.go
index c1558e96..9fd7a8b6 100644
--- a/function.go
+++ b/function.go
@@ -11,6 +11,8 @@ import (
 	"time"
 
 	"gopkg.in/yaml.v2"
+	fnlabels "knative.dev/kn-plugin-func/k8s/labels"
+	"knative.dev/pkg/ptr"
 )
 
 // FunctionFile is the file used for the serialized form of a function.
@@ -344,6 +346,61 @@ func (f Function) ImageWithDigest() string {
 	return part1 + strings.Split(part2, ":")[0] + "@" + f.ImageDigest
 }
 
+// LabelsMap combines default labels with the labels slice provided.
+// It will the resulting slice with ValidateLabels and return a key/value map.
+//   - key: EXAMPLE1                            # Label directly from a value
+//     value: value1
+//   - key: EXAMPLE2                            # Label from the local ENV var
+//     value: {{ env:MY_ENV }}
+func (f Function) LabelsMap() (map[string]string, error) {
+	defaultLabels := []Label{
+		{
+			Key:   ptr.String(fnlabels.FunctionKey),
+			Value: ptr.String(fnlabels.FunctionValue),
+		},
+		{
+			Key:   ptr.String(fnlabels.FunctionNameKey),
+			Value: ptr.String(f.Name),
+		},
+		{
+			Key:   ptr.String(fnlabels.FunctionRuntimeKey),
+			Value: ptr.String(f.Runtime),
+		},
+		// --- handle usage of deprecated labels (`boson.dev/function`, `boson.dev/runtime`)
+		{
+			Key:   ptr.String(fnlabels.DeprecatedFunctionKey),
+			Value: ptr.String(fnlabels.FunctionValue),
+		},
+		{
+			Key:   ptr.String(fnlabels.DeprecatedFunctionRuntimeKey),
+			Value: ptr.String(f.Runtime),
+		},
+		// --- end of handling usage of deprecated runtime labels
+	}
+
+	labels := append(defaultLabels, f.Labels...)
+	if err := ValidateLabels(labels); len(err) != 0 {
+		return nil, errors.New(strings.Join(err, " "))
+	}
+
+	l := map[string]string{}
+	for _, label := range labels {
+		if label.Value == nil {
+			l[*label.Key] = ""
+		} else {
+			if strings.HasPrefix(*label.Value, "{{") {
+				// env variable format is validated above in ValidateLabels
+				match := regLocalEnv.FindStringSubmatch(*label.Value)
+				l[*label.Key] = os.Getenv(match[1])
+			} else {
+				l[*label.Key] = *label.Value
+			}
+		}
+	}
+
+	return l, nil
+}
+
 // assertEmptyRoot ensures that the directory is empty enough to be used for
 // initializing a new function.
 func assertEmptyRoot(path string) (err error) {
diff --git a/k8s/persistent_volumes.go b/k8s/persistent_volumes.go
index c0a3a61c..cf685305 100644
--- a/k8s/persistent_volumes.go
+++ b/k8s/persistent_volumes.go
@@ -17,7 +17,7 @@ func GetPersistentVolumeClaim(ctx context.Context, name, namespaceOverride strin
 	return client.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{})
 }
 
-func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) {
+func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) {
 	client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
 	if err != nil {
 		return
@@ -25,9 +25,10 @@ func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride st
 
 	pvc := &corev1.PersistentVolumeClaim{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-			Labels:    labels,
+			Name:        name,
+			Namespace:   namespace,
+			Labels:      labels,
+			Annotations: annotations,
 		},
 		Spec: corev1.PersistentVolumeClaimSpec{
 			AccessModes: []corev1.PersistentVolumeAccessMode{accessMode},
diff --git a/k8s/secrets.go b/k8s/secrets.go
index 0fd8780b..7884234f 100644
--- a/k8s/secrets.go
+++ b/k8s/secrets.go
@@ -83,7 +83,7 @@ func DeleteSecrets(ctx context.Context, namespaceOverride string, listOptions me
 	return client.CoreV1().Secrets(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
 }
 
-func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverride string, labels map[string]string, username, password, server string) (err error) {
+func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, username, password, server string) (err error) {
 	client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
 	if err != nil {
 		return
@@ -110,9 +110,10 @@ func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverrid
 	if createSecret || !bytes.Equal(currentSecret.Data[secretKey], dockerConfigJSONContent) {
 		secret := &corev1.Secret{
 			ObjectMeta: metav1.ObjectMeta{
-				Name:      name,
-				Namespace: namespace,
-				Labels:    labels,
+				Name:        name,
+				Namespace:   namespace,
+				Labels:      labels,
+				Annotations: annotations,
 			},
 			Type: corev1.SecretTypeOpaque,
 			Data: map[string][]byte{},
diff --git a/knative/deployer.go b/knative/deployer.go
index 75996cc6..7bacf2d2 100644
--- a/knative/deployer.go
+++ b/knative/deployer.go
@@ -22,7 +22,6 @@ import (
 
 	fn "knative.dev/kn-plugin-func"
 	"knative.dev/kn-plugin-func/k8s"
-	"knative.dev/kn-plugin-func/k8s/labels"
 )
 
 const LIVENESS_ENDPOINT = "/health/liveness"
@@ -294,10 +293,13 @@ func generateNewService(f fn.Function, decorator DeployDecorator) (*v1.Service,
 	}
 	container.VolumeMounts = newVolumeMounts
 
-	labels, err := processLabels(f, decorator)
+	labels, err := f.LabelsMap()
 	if err != nil {
 		return nil, err
 	}
+	if decorator != nil {
+		labels = decorator.UpdateLabels(f, labels)
+	}
 
 	annotations := f.Annotations
 	if decorator != nil {
@@ -374,10 +376,14 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En
 			return service, err
 		}
 
-		labels, err := processLabels(f, decorator)
+		labels, err := f.LabelsMap()
 		if err != nil {
-			return service, err
+			return nil, err
 		}
+		if decorator != nil {
+			labels = decorator.UpdateLabels(f, labels)
+		}
+
 		service.ObjectMeta.Labels = labels
 		service.Spec.Template.ObjectMeta.Labels = labels
 
@@ -395,54 +401,6 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En
 	}
 }
 
-// processLabels generates a map of labels as key/value pairs from a function config
-// labels:
-//   - key: EXAMPLE1                            # Label directly from a value
-//     value: value1
-//   - key: EXAMPLE2                            # Label from the local ENV var
-//     value: {{ env:MY_ENV }}
-func processLabels(f fn.Function, decorator DeployDecorator) (map[string]string, error) {
-	labels := map[string]string{
-		labels.FunctionKey:        labels.FunctionValue,
-		labels.FunctionNameKey:    f.Name,
-		labels.FunctionRuntimeKey: f.Runtime,
-
-		// --- handle usage of deprecated labels (`boson.dev/function`, `boson.dev/runtime`)
-		labels.DeprecatedFunctionKey:        labels.FunctionValue,
-		labels.DeprecatedFunctionRuntimeKey: f.Runtime,
-		// --- end of handling usage of deprecated runtime labels
-	}
-
-	if decorator != nil {
-		labels = decorator.UpdateLabels(f, labels)
-	}
-
-	for _, label := range f.Labels {
-		if label.Key != nil && label.Value != nil {
-			if strings.HasPrefix(*label.Value, "{{") {
-				slices := strings.Split(strings.Trim(*label.Value, "{} "), ":")
-				if len(slices) == 2 {
-					// label from the local ENV var, eg. author={{ env:$USER }}
-					localValue, err := processLocalEnvValue(*label.Value)
-					if err != nil {
-						return nil, err
-					}
-					labels[*label.Key] = localValue
-					continue
-				}
-			} else {
-				// a standard label with key and value, eg. author=alice@example.com
-				labels[*label.Key] = *label.Value
-				continue
-			}
-		} else if label.Key != nil && label.Value == nil {
-			labels[*label.Key] = ""
-		}
-	}
-
-	return labels, nil
-}
-
 // processEnvs generates array of EnvVars and EnvFromSources from a function config
 // envs:
 //   - name: EXAMPLE1                            # ENV directly from a value
diff --git a/pipelines/tekton/pipeplines_provider.go b/pipelines/tekton/pipeplines_provider.go
index 03293ebd..ed12f6ec 100644
--- a/pipelines/tekton/pipeplines_provider.go
+++ b/pipelines/tekton/pipeplines_provider.go
@@ -19,7 +19,7 @@ import (
 	fn "knative.dev/kn-plugin-func"
 	"knative.dev/kn-plugin-func/docker"
 	"knative.dev/kn-plugin-func/k8s"
-	"knative.dev/kn-plugin-func/k8s/labels"
+	fnlabels "knative.dev/kn-plugin-func/k8s/labels"
 	"knative.dev/kn-plugin-func/knative"
 	"knative.dev/pkg/apis"
 )
@@ -97,12 +97,15 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
 	pp.namespace = namespace
 
 	// let's specify labels that will be applied to every resouce that is created for a Pipeline
-	labels := map[string]string{labels.FunctionNameKey: f.Name}
+	labels, err := f.LabelsMap()
+	if err != nil {
+		return err
+	}
 	if pp.decorator != nil {
 		labels = pp.decorator.UpdateLabels(f, labels)
 	}
 
-	err = k8s.CreatePersistentVolumeClaim(ctx, getPipelinePvcName(f), pp.namespace, labels, corev1.ReadWriteOnce, *resource.NewQuantity(DefaultPersistentVolumeClaimSize, resource.DecimalSI))
+	err = k8s.CreatePersistentVolumeClaim(ctx, getPipelinePvcName(f), pp.namespace, labels, f.Annotations, corev1.ReadWriteOnce, *resource.NewQuantity(DefaultPersistentVolumeClaimSize, resource.DecimalSI))
 	if err != nil {
 		if !errors.IsAlreadyExists(err) {
 			return fmt.Errorf("problem creating persistent volume claim: %v", err)
@@ -135,7 +138,7 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
 		registry = authn.DefaultAuthKey
 	}
 
-	err = k8s.EnsureDockerRegistrySecretExist(ctx, getPipelineSecretName(f), pp.namespace, labels, creds.Username, creds.Password, registry)
+	err = k8s.EnsureDockerRegistrySecretExist(ctx, getPipelineSecretName(f), pp.namespace, labels, f.Annotations, creds.Username, creds.Password, registry)
 	if err != nil {
 		return fmt.Errorf("problem in creating secret: %v", err)
 	}
@@ -182,7 +185,7 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
 
 func (pp *PipelinesProvider) Remove(ctx context.Context, f fn.Function) error {
 
-	l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{labels.FunctionNameKey: f.Name}))
+	l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{fnlabels.FunctionNameKey: f.Name}))
 	listOptions := metav1.ListOptions{
 		LabelSelector: l.String(),
 	}
diff --git a/pipelines/tekton/resources.go b/pipelines/tekton/resources.go
index ac6bb3b4..394e5423 100644
--- a/pipelines/tekton/resources.go
+++ b/pipelines/tekton/resources.go
@@ -98,8 +98,9 @@ func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipe
 
 	return &pplnv1beta1.Pipeline{
 		ObjectMeta: v1.ObjectMeta{
-			Name:   pipelineName,
-			Labels: labels,
+			Name:        pipelineName,
+			Labels:      labels,
+			Annotations: f.Annotations,
 		},
 		Spec: pplnv1beta1.PipelineSpec{
 			Params:     params,
@@ -200,6 +201,7 @@ func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.P
 		ObjectMeta: v1.ObjectMeta{
 			GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)),
 			Labels:       labels,
+			Annotations:  f.Annotations,
 		},
 		Spec: pplnv1beta1.PipelineRunSpec{
 			PipelineRef: &pplnv1beta1.PipelineRef{
diff --git a/s2i/builder.go b/s2i/builder.go
index b5ad461b..1e7d6d8e 100644
--- a/s2i/builder.go
+++ b/s2i/builder.go
@@ -238,7 +238,7 @@ func (b *Builder) Build(ctx context.Context, f fn.Function) (err error) {
 			if err != nil {
 				return fmt.Errorf("cannot create tar header: %w", err)
 			}
-			hdr.Name = p
+			hdr.Name = filepath.ToSlash(p)
 
 			err = tw.WriteHeader(hdr)
 			if err != nil {
diff --git a/templates/quarkus/cloudevents/pom.xml b/templates/quarkus/cloudevents/pom.xml
index d4181892..f2650485 100644
--- a/templates/quarkus/cloudevents/pom.xml
+++ b/templates/quarkus/cloudevents/pom.xml
@@ -12,7 +12,7 @@
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
     <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
-    <quarkus.platform.version>2.12.0.Final</quarkus.platform.version>
+    <quarkus.platform.version>2.12.1.Final</quarkus.platform.version>
     <skipITs>true</skipITs>
     <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
   </properties>
diff --git a/templates/quarkus/http/pom.xml b/templates/quarkus/http/pom.xml
index d4181892..f2650485 100644
--- a/templates/quarkus/http/pom.xml
+++ b/templates/quarkus/http/pom.xml
@@ -12,7 +12,7 @@
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
     <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
-    <quarkus.platform.version>2.12.0.Final</quarkus.platform.version>
+    <quarkus.platform.version>2.12.1.Final</quarkus.platform.version>
     <skipITs>true</skipITs>
     <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
   </properties>
diff --git a/zz_filesystem_generated.go b/zz_filesystem_generated.go
index 6e11636c..b951e662 100644
--- a/zz_filesystem_generated.go
+++ b/zz_filesystem_generated.go
@@ -3530,7 +3530,7 @@ var templatesZip = []byte{
 	0x82, 0xa7, 0xdc, 0x61, 0x72, 0xa0, 0xac, 0xc1, 0x20, 0x0d, 0x39, 0x30, 0x03, 0x74, 0xb9, 0x24, 0x78, 0xc2, 0x15, 0xe6, 0xd5, 0x45, 0x46, 0xf7, 0x95, 0xc8, 0x13, 0x64, 0x54, 0xa5, 0x39, 0xfc,
 	0x23, 0xb9, 0x4a, 0x84, 0xcc, 0xe8, 0x7f, 0x77, 0x5f, 0xe2, 0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, 0x85, 0xcc, 0x90, 0xaa, 0x6c, 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10,
 	0xee, 0x47, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b, 0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, 0xf7, 0xaa, 0x20, 0xf8, 0x45, 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17,
-	0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0xa6, 0x10, 0xe7, 0x68, 0x79, 0x8e, 0x16, 0xe8, 0x8b, 0x90, 0x2c, 0x1f, 0x09, 0xd3, 0xaf, 0x97, 0x79, 0x14, 0xe5, 0xe5, 0x9d, 0xa1, 0x56,
+	0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0xa6, 0x10, 0xe7, 0x68, 0x79, 0x8e, 0x96, 0xe8, 0x8b, 0x90, 0x2c, 0x1f, 0x09, 0xd3, 0xaf, 0x97, 0x79, 0x14, 0xe5, 0xe5, 0x9d, 0xa1, 0x56,
 	0x57, 0x40, 0x70, 0x33, 0xaa, 0x7d, 0x95, 0x86, 0x54, 0x68, 0x38, 0x14, 0x82, 0x53, 0xd3, 0xf5, 0x5f, 0x04, 0x4f, 0x21, 0x9c, 0xae, 0x70, 0x5f, 0x58, 0x24, 0x81, 0x12, 0x64, 0x02, 0x92, 0x6f,
 	0xaf, 0x99, 0x64, 0x19, 0x14, 0x20, 0x6d, 0xbd, 0x50, 0xeb, 0x6a, 0x45, 0xd8, 0xc3, 0x37, 0xa6, 0xce, 0x2f, 0x71, 0xf6, 0x3c, 0x49, 0xcf, 0xae, 0xf7, 0x9b, 0xd4, 0xf3, 0x3a, 0xff, 0xc5, 0xc8,
 	0xd4, 0x4e, 0xad, 0x76, 0xc3, 0x7f, 0xa8, 0x0e, 0xd0, 0xec, 0x6c, 0x64, 0x76, 0xed, 0xda, 0xf5, 0x7e, 0xab, 0x7a, 0x9a, 0xdd, 0x96, 0x40, 0x4b, 0x27, 0x10, 0xff, 0xb5, 0x77, 0x18, 0xae, 0x4a,
@@ -3550,7 +3550,7 @@ var templatesZip = []byte{
 	0x51, 0x70, 0x74, 0x9c, 0x8a, 0xa3, 0x69, 0x25, 0x07, 0xd7, 0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0x5e, 0x5d, 0xa9, 0x68, 0x13, 0x68, 0x86, 0x6d, 0x10, 0x91, 0xd0, 0x40,
 	0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4, 0x9a, 0xc7, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, 0x06, 0xc1, 0x87, 0x81, 0x0f, 0xde, 0xb8,
 	0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7, 0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, 0x83, 0xac, 0xf6, 0x2d, 0x38, 0xee, 0x90,
-	0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x4f, 0x51, 0x3a, 0xfb, 0x19, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xff, 0x08, 0x92, 0x77, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x50, 0x4b,
+	0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x4f, 0x51, 0x3a, 0xfb, 0x19, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x4c, 0x6d, 0xb8, 0xd5, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x50, 0x4b,
 	0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72,
 	0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65,
@@ -3939,7 +3939,7 @@ var templatesZip = []byte{
 	0x39, 0x30, 0x03, 0x74, 0xb9, 0x24, 0x78, 0xc2, 0x15, 0xe6, 0xd5, 0x45, 0x46, 0xf7, 0x95, 0xc8, 0x13, 0x64, 0x54, 0xa5, 0x39, 0xfc, 0x23, 0xb9, 0x4a, 0x84, 0xcc, 0xe8, 0x7f, 0x77, 0x5f, 0xe2,
 	0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, 0x85, 0xcc, 0x90, 0xaa, 0x6c, 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10, 0xee, 0x47, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b,
 	0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, 0xf7, 0xaa, 0x20, 0xf8, 0x45, 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17, 0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0xa6,
-	0x10, 0xe7, 0x68, 0x79, 0x8e, 0x16, 0xe8, 0x8b, 0x90, 0x2c, 0x1f, 0x09, 0xd3, 0xaf, 0x97, 0x79, 0x14, 0xe5, 0xe5, 0x9d, 0xa1, 0x56, 0x57, 0x40, 0x70, 0x33, 0xaa, 0x7d, 0x95, 0x86, 0x54, 0x68,
+	0x10, 0xe7, 0x68, 0x79, 0x8e, 0x96, 0xe8, 0x8b, 0x90, 0x2c, 0x1f, 0x09, 0xd3, 0xaf, 0x97, 0x79, 0x14, 0xe5, 0xe5, 0x9d, 0xa1, 0x56, 0x57, 0x40, 0x70, 0x33, 0xaa, 0x7d, 0x95, 0x86, 0x54, 0x68,
 	0x38, 0x14, 0x82, 0x53, 0xd3, 0xf5, 0x5f, 0x04, 0x4f, 0x21, 0x9c, 0xae, 0x70, 0x5f, 0x58, 0x24, 0x81, 0x12, 0x64, 0x02, 0x92, 0x6f, 0xaf, 0x99, 0x64, 0x19, 0x14, 0x20, 0x6d, 0xbd, 0x50, 0xeb,
 	0x6a, 0x45, 0xd8, 0xc3, 0x37, 0xa6, 0xce, 0x2f, 0x71, 0xf6, 0x3c, 0x49, 0xcf, 0xae, 0xf7, 0x9b, 0xd4, 0xf3, 0x3a, 0xff, 0xc5, 0xc8, 0xd4, 0x4e, 0xad, 0x76, 0xc3, 0x7f, 0xa8, 0x0e, 0xd0, 0xec,
 	0x6c, 0x64, 0x76, 0xed, 0xda, 0xf5, 0x7e, 0xab, 0x7a, 0x9a, 0xdd, 0x96, 0x40, 0x4b, 0x27, 0x10, 0xff, 0xb5, 0x77, 0x18, 0xae, 0x4a, 0xa0, 0xa2, 0x70, 0xf2, 0x23, 0x38, 0x8c, 0x9a, 0x9d, 0xe3,
@@ -3959,7 +3959,7 @@ var templatesZip = []byte{
 	0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0x5e, 0x5d, 0xa9, 0x68, 0x13, 0x68, 0x86, 0x6d, 0x10, 0x91, 0xd0, 0x40, 0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4,
 	0x9a, 0xc7, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, 0x06, 0xc1, 0x87, 0x81, 0x0f, 0xde, 0xb8, 0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7,
 	0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, 0x83, 0xac, 0xf6, 0x2d, 0x38, 0xee, 0x90, 0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x4f, 0x51, 0x3a, 0xfb, 0x19,
-	0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xff, 0x08, 0x92, 0x77, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x4c, 0x6d, 0xb8, 0xd5, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73,
 	0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
 	0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00,
@@ -10640,7 +10640,7 @@ var templatesZip = []byte{
 	0x00, 0x00, 0xed, 0x81, 0x42, 0xa0, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77,
 	0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x2c, 0x84, 0x18, 0xd1, 0x09, 0x00, 0x00, 0xcf, 0x19, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x3c, 0xad, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74,
-	0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x92, 0x77, 0xfe, 0x03,
+	0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6d, 0xb8, 0xd5, 0xfe, 0x03,
 	0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x57, 0xb7, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f,
 	0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x9e, 0xbb, 0x01,
@@ -10703,7 +10703,7 @@ var templatesZip = []byte{
 	0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x6d, 0xd3, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14,
 	0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x2c, 0x84, 0x18, 0xd1, 0x09, 0x00, 0x00, 0xcf, 0x19, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0xed, 0x81, 0x60, 0xe0, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b,
-	0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x92, 0x77, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6d, 0xb8, 0xd5, 0xfe, 0x03, 0x00, 0x00, 0xff, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x74, 0xea, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c,
 	0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xb4, 0xee, 0x01, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50,


Test patch:
diff --git a/filesystem_test.go b/filesystem_test.go
index 72f8c94c..a1e8f19e 100644
--- a/filesystem_test.go
+++ b/filesystem_test.go
@@ -231,7 +231,7 @@ func initGitFS(t *testing.T) Filesystem {
 
 	result, err := filesystemFromRepo(uri)
 	if err != nil {
-		t.Fatal(t)
+		t.Fatal(err)
 	}
 	return result
 }
diff --git a/function_unit_test.go b/function_unit_test.go
index b433ac1c..4729bfe3 100644
--- a/function_unit_test.go
+++ b/function_unit_test.go
@@ -4,7 +4,11 @@
 package function
 
 import (
+	"reflect"
 	"testing"
+
+	fnlabels "knative.dev/kn-plugin-func/k8s/labels"
+	. "knative.dev/kn-plugin-func/testing"
 )
 
 func TestFunction_ImageWithDigest(t *testing.T) {
@@ -82,3 +86,126 @@ func TestFunction_ImageName(t *testing.T) {
 		}
 	}
 }
+
+func Test_LabelsMap(t *testing.T) {
+	key1 := "key1"
+	key2 := "key2"
+	value1 := "value1"
+	value2 := "value2"
+
+	defer WithEnvVar(t, "BAD_EXAMPLE", ":invalid")()
+	valueLocalEnvIncorrect4 := "{{env:BAD_EXAMPLE}}"
+
+	defer WithEnvVar(t, "GOOD_EXAMPLE", "valid")()
+	valueLocalEnv4 := "{{env:GOOD_EXAMPLE}}"
+
+	tests := []struct {
+		name        string
+		labels      []Label
+		expectErr   bool
+		expectedMap map[string]string
+	}{
+		{
+			name: "invalid Labels should return err",
+			labels: []Label{
+				{
+					Value: &value1,
+				},
+			},
+			expectErr: true,
+		},
+		{
+			name: "with valid env var",
+			labels: []Label{
+				{
+					Key:   &key1,
+					Value: &valueLocalEnv4,
+				},
+			},
+			expectErr: false,
+			expectedMap: map[string]string{
+				key1: "valid",
+			},
+		},
+		{
+			name: "with invalid env var",
+			labels: []Label{
+				{
+					Key:   &key1,
+					Value: &valueLocalEnvIncorrect4,
+				},
+			},
+			expectErr: true,
+		},
+		{
+			name: "empty labels allowed. returns default labels",
+			labels: []Label{
+				{
+					Key: &key1,
+				},
+			},
+			expectErr: false,
+			expectedMap: map[string]string{
+				key1: "",
+			},
+		},
+		{
+			name: "full set of labels",
+			labels: []Label{
+				{
+					Key:   &key1,
+					Value: &value1,
+				},
+				{
+					Key:   &key2,
+					Value: &value2,
+				},
+			},
+			expectErr: false,
+			expectedMap: map[string]string{
+				key1: value1,
+				key2: value2,
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			f := Function{
+				Name:    "some-function",
+				Runtime: "golang",
+				Labels:  tt.labels,
+			}
+			got, err := f.LabelsMap()
+
+			if tt.expectErr {
+				if err == nil {
+					t.Error("expected error but didn't get an error from LabelsMap")
+				}
+			} else {
+				if err != nil {
+					t.Errorf("got unexpected err: %s", err)
+				}
+			}
+			if err == nil {
+				defaultLabels := expectedDefaultLabels(f)
+				for k, v := range defaultLabels {
+					tt.expectedMap[k] = v
+				}
+				if res := reflect.DeepEqual(got, tt.expectedMap); !res {
+					t.Errorf("mismatch in actual and expected labels return. actual: %#v, expected: %#v", got, tt.expectedMap)
+				}
+			}
+		})
+	}
+}
+
+func expectedDefaultLabels(f Function) map[string]string {
+	return map[string]string{
+		fnlabels.FunctionKey:                  fnlabels.FunctionValue,
+		fnlabels.FunctionNameKey:              f.Name,
+		fnlabels.FunctionRuntimeKey:           f.Runtime,
+		fnlabels.DeprecatedFunctionKey:        fnlabels.FunctionValue,
+		fnlabels.DeprecatedFunctionRuntimeKey: f.Runtime,
+	}
+}