Repository: knative-sandbox/kn-plugin-func
Pull request #655 (2021-11-11 15:24:20)

chore: prefer slices over plural types
- :broom: prefer slices over plural types unless type is needed for method receivers

Minor change to prefer slices of objects over a type to indicate plurality, as it is my understanding that this is a Go idiom, and is a helpful indicator to the developer that the type can be treated as a slice (for example, that `range` statements are available, `len` etc).  Therefore, in places where a type exists only to add plurality (there are no associated methods with the slice type as its receiver), this has been collapsed into the slice form.

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/kn-plugin-func/pull/655#" title="Author self-approved">lkingland</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fkn-plugin-func).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative-sandbox/kn-plugin-func/blob/main/OWNERS)~~ [lkingland]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) Report
> Merging [#655](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) (a5ca219) into [main](https://codecov.io/gh/knative-sandbox/kn-plugin-func/commit/fc1e874a5d2e61025cd6e032318a0a22a638343c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) (fc1e874) will **not change** coverage.
> The diff coverage is `25.00%`.

> :exclamation: Current head a5ca219 differs from pull request most recent head b8b5edd. Consider uploading reports for the commit b8b5edd to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/graphs/tree.svg?width=650&height=150&src=pr&token=da1B9bMsQ9&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)

```diff
@@           Coverage Diff           @@
##             main     #655   +/-   ##
=======================================
  Coverage   36.26%   36.26%           
=======================================
  Files          38       38           
  Lines        3847     3847           
=======================================
  Hits         1395     1395           
  Misses       2268     2268           
  Partials      184      184           
```


| [Impacted Files](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) | Coverage Δ | |
|---|---|---|
| [cmd/config\_envs.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL2NvbmZpZ19lbnZzLmdv) | `2.57% <0.00%> (ø)` | |
| [cmd/config\_volumes.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL2NvbmZpZ192b2x1bWVzLmdv) | `4.91% <0.00%> (ø)` | |
| [cmd/root.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL3Jvb3QuZ28=) | `46.72% <0.00%> (ø)` | |
| [config.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y29uZmlnLmdv) | `61.82% <ø> (ø)` | |
| [function.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-ZnVuY3Rpb24uZ28=) | `57.57% <ø> (ø)` | |
| [knative/deployer.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-a25hdGl2ZS9kZXBsb3llci5nbw==) | `5.62% <ø> (ø)` | |
| [cmd/config\_labels.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL2NvbmZpZ19sYWJlbHMuZ28=) | `69.41% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox). Last update [fc1e874...b8b5edd](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/655?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox).


Patch:
diff --git a/.codecov.yaml b/.codecov.yaml
index 16733ebf..f6fd0b2e 100644
--- a/.codecov.yaml
+++ b/.codecov.yaml
@@ -2,3 +2,4 @@ ignore:
   - third_party
   - vendor
   - testdata
+  - docker/docker_client_generated.go
diff --git a/.github/workflows/pull_requests.yaml b/.github/workflows/pull_requests.yaml
deleted file mode 100644
index 1c95c36c..00000000
--- a/.github/workflows/pull_requests.yaml
+++ /dev/null
@@ -1,96 +0,0 @@
-# Build and test all pull requests
-name: Pull Requests
-
-on: [pull_request]
-
-jobs:
-  build:
-    strategy:
-      matrix:
-        os: ["ubuntu-latest", "windows-latest", "macos-latest"]
-    runs-on: ${{ matrix.os }}
-    steps:
-      - name: Install Bash 4 on Mac
-        if: matrix.os == 'macos-latest'
-        run: |
-          /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-          brew update
-
-          brew install bash
-          brew install gnu-sed
-
-          echo "/usr/local/bin" >> $GITHUB_PATH
-          echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
-      - uses: actions/checkout@v2
-      - uses: actions/setup-go@v2
-        with:
-          go-version: "^1.16"
-      - uses: actions/setup-java@v1
-        with:
-          java-version: '11'
-      - name: Unit Test
-        run: make test
-      - name: Template Unit Tests
-        run: make test-templates
-      - name: Build
-        run: make build
-      - name: Lint
-        run: make check
-      - name: Check that 'func.yaml schema' is up-to-date
-        if: matrix.os != 'windows-latest'
-        run: make schema-check
-
-  integration-test:
-    needs: build
-    runs-on: "ubuntu-latest"
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-      - name: Setup Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: "^1.16"
-      - name: Install Podman
-        run: |
-          . /etc/os-release
-          echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
-          curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key" | sudo apt-key add -
-          sudo apt-get update
-          sudo apt-get -y upgrade
-          sudo apt-get -y install podman
-          podman info
-      - name: Install Binaries
-        run: ./hack/binaries.sh
-      - name: Allocate Cluster
-        run: ./hack/allocate.sh
-      - name: Local Registry Routing
-        run: ./hack/registry.sh
-      - name: Verify Configuration
-        run: ./hack/test.sh
-      - name: Integration Test Podman
-        run: ./hack/test-integration-podman.sh
-      - name: Integration Test
-        run: make test-integration
-
-  e2e-test:
-    needs: integration-test
-    runs-on: "ubuntu-latest"
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-      - name: Setup Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: "^1.16"
-      - name: Build
-        run: make build
-      - name: Install Binaries
-        run: ./hack/binaries.sh
-      - name: Allocate Cluster
-        run: ./hack/allocate.sh
-      - name: Local Registry an Routes (CI)
-        run: ./hack/registry.sh
-      - name: Verify Configuration
-        run: ./hack/test.sh
-      - name: E2E Test
-        run: make test-e2e
diff --git a/Makefile b/Makefile
index 86b8fa0c..1099467e 100644
--- a/Makefile
+++ b/Makefile
@@ -60,7 +60,7 @@ check: bin/golangci-lint ## Check code quality (lint)
 	cd test/_e2e && ../../bin/golangci-lint run --timeout 300s
 
 bin/golangci-lint:
-	curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin v1.40.1
+	curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin v1.43.0
 
 pkged.go: $(TEMPLATES)
 	@rm -rf templates/node/cloudevents/node_modules
diff --git a/buildpacks/builder.go b/buildpacks/builder.go
index ea8a74fc..cc5612b4 100644
--- a/buildpacks/builder.go
+++ b/buildpacks/builder.go
@@ -11,6 +11,8 @@ import (
 	"runtime"
 	"strings"
 
+	"github.com/docker/docker/client"
+
 	fn "knative.dev/kn-plugin-func"
 	"knative.dev/kn-plugin-func/docker"
 
@@ -79,10 +81,11 @@ func (builder *Builder) Build(ctx context.Context, f fn.Function) (err error) {
 		logWriter = &bytes.Buffer{}
 	}
 
-	cli, dockerHost, err := docker.NewDockerClient()
+	cli, dockerHost, err := docker.NewClient(client.DefaultDockerHost)
 	if err != nil {
 		return err
 	}
+	defer cli.Close()
 
 	version, err := cli.ServerVersion(ctx)
 	if err != nil {
diff --git a/cmd/config_envs.go b/cmd/config_envs.go
index fbf20fde..62d2dc67 100644
--- a/cmd/config_envs.go
+++ b/cmd/config_envs.go
@@ -407,7 +407,7 @@ func runRemoveEnvsPrompt(f fn.Function) (err error) {
 		return
 	}
 
-	var newEnvs fn.Envs
+	var newEnvs []fn.Env
 	removed := false
 	for i, e := range f.Envs {
 		if e.String() == selectedEnv {
diff --git a/cmd/config_labels.go b/cmd/config_labels.go
index 46f60b24..86de13f9 100644
--- a/cmd/config_labels.go
+++ b/cmd/config_labels.go
@@ -269,7 +269,7 @@ func runRemoveLabelsPrompt(f fn.Function, saver functionSaver) (err error) {
 		return
 	}
 
-	var newLabels fn.Labels
+	var newLabels []fn.Label
 	removed := false
 	for i, e := range f.Labels {
 		if e.String() == selectedLabel {
diff --git a/cmd/config_volumes.go b/cmd/config_volumes.go
index d1d2ddba..e82af6af 100644
--- a/cmd/config_volumes.go
+++ b/cmd/config_volumes.go
@@ -224,7 +224,7 @@ func runRemoveVolumesPrompt(f fn.Function) (err error) {
 		return
 	}
 
-	var newVolumes fn.Volumes
+	var newVolumes []fn.Volume
 	removed := false
 	for i, v := range f.Volumes {
 		if v.String() == selectedVolume {
diff --git a/cmd/root.go b/cmd/root.go
index 639bce16..a0916ad1 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -299,7 +299,7 @@ func envFromCmd(cmd *cobra.Command) (*util.OrderedMap, []string, error) {
 	return util.NewOrderedMap(), []string{}, nil
 }
 
-func mergeEnvs(envs fn.Envs, envToUpdate *util.OrderedMap, envToRemove []string) (fn.Envs, error) {
+func mergeEnvs(envs []fn.Env, envToUpdate *util.OrderedMap, envToRemove []string) ([]fn.Env, error) {
 	updated := sets.NewString()
 
 	for i := range envs {
@@ -332,7 +332,7 @@ func mergeEnvs(envs fn.Envs, envToUpdate *util.OrderedMap, envToRemove []string)
 
 	errMsg := fn.ValidateEnvs(envs)
 	if len(errMsg) > 0 {
-		return fn.Envs{}, fmt.Errorf(strings.Join(errMsg, "\n"))
+		return []fn.Env{}, fmt.Errorf(strings.Join(errMsg, "\n"))
 	}
 
 	return envs, nil
diff --git a/config.go b/config.go
index 6b4296b3..3bfae61c 100644
--- a/config.go
+++ b/config.go
@@ -26,7 +26,6 @@ var (
 	regLocalEnv         = regexp.MustCompile(`^{{\s*env:(\w+)\s*}}$`)
 )
 
-type Volumes []Volume
 type Volume struct {
 	Secret    *string `yaml:"secret,omitempty" jsonschema:"oneof_required=secret"`
 	ConfigMap *string `yaml:"configMap,omitempty" jsonschema:"oneof_required=configmap"`
@@ -43,7 +42,6 @@ func (v Volume) String() string {
 	return ""
 }
 
-type Envs []Env
 type Env struct {
 	Name  *string `yaml:"name,omitempty" jsonschema:"pattern=^[-._a-zA-Z][-._a-zA-Z0-9]*$"`
 	Value *string `yaml:"value"`
@@ -78,7 +76,6 @@ func (e Env) String() string {
 	return ""
 }
 
-type Labels []Label
 type Label struct {
 	// Key consist of optional prefix part (ended by '/') and name part
 	// Prefix part validation pattern: [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
@@ -141,12 +138,12 @@ type Config struct {
 	Builders        map[string]string `yaml:"builders"`
 	Buildpacks      []string          `yaml:"buildpacks"`
 	HealthEndpoints HealthEndpoints   `yaml:"healthEndpoints"`
-	Volumes         Volumes           `yaml:"volumes"`
-	BuildEnvs       Envs              `yaml:"buildEnvs"`
-	Envs            Envs              `yaml:"envs"`
+	Volumes         []Volume          `yaml:"volumes"`
+	BuildEnvs       []Env             `yaml:"buildEnvs"`
+	Envs            []Env             `yaml:"envs"`
 	Annotations     map[string]string `yaml:"annotations"`
 	Options         Options           `yaml:"options"`
-	Labels          Labels            `yaml:"labels"`
+	Labels          []Label           `yaml:"labels"`
 	Created         time.Time         `yaml:"created"`
 	// Add new values to the toConfig/fromConfig functions.
 }
@@ -169,86 +166,53 @@ func newConfig(root string) (c Config, err error) {
 	if err != nil {
 		return
 	}
+	if err = yaml.UnmarshalStrict(bb, &c); err != nil {
+		return c, formatUnmarshalError(err) // fail fast on sytax errors
+	}
+	return c, validateConfig(c)
+}
 
-	errMsg := ""
-	errMsgHeader := "'func.yaml' config file is not valid:\n"
-	errMsgReg := regexp.MustCompile("not found in type .*")
-
-	// Let's try to unmarshal the config file, any fields that are found
-	// in the data that do not have corresponding struct members, or mapping
-	// keys that are duplicates, will result in an error.
-	err = yaml.UnmarshalStrict(bb, &c)
-	if err != nil {
-		errMsg = err.Error()
-
-		if strings.HasPrefix(errMsg, "yaml: unmarshal errors:") {
-			errMsg = errMsgReg.ReplaceAllString(errMsg, "is not valid")
-			errMsg = strings.Replace(errMsg, "yaml: unmarshal errors:\n", errMsgHeader, 1)
-		} else if strings.HasPrefix(errMsg, "yaml:") {
-			errMsg = errMsgReg.ReplaceAllString(errMsg, "is not valid")
-			errMsg = strings.Replace(errMsg, "yaml: ", errMsgHeader+"  ", 1)
-		}
+func formatUnmarshalError(err error) error {
+	var (
+		e      = err.Error()
+		rxp    = regexp.MustCompile("not found in type .*")
+		header = fmt.Sprintf("'%v' is not valid:\n", ConfigFile)
+	)
+	if strings.HasPrefix(e, "yaml: unmarshal errors:") {
+		e = rxp.ReplaceAllString(e, "is not valid")
+		e = strings.Replace(e, "yaml: unmarshal errors:\n", header, 1)
+	} else if strings.HasPrefix(e, "yaml:") {
+		e = rxp.ReplaceAllString(e, "is not valid")
+		e = strings.Replace(e, "yaml: ", header+"  ", 1)
 	}
+	return errors.New(e)
+}
 
-	// Let's check that all entries in `volumes`, `buildEnvs`, `envs` and `options` contain all required fields
-	volumesErrors := validateVolumes(c.Volumes)
-	buildEnvsErrors := ValidateBuildEnvs((c.BuildEnvs))
-	envsErrors := ValidateEnvs(c.Envs)
-	optionsErrors := validateOptions(c.Options)
-	labelsErrors := ValidateLabels(c.Labels)
-	if len(volumesErrors) > 0 || len(buildEnvsErrors) > 0 || len(envsErrors) > 0 || len(optionsErrors) > 0 || len(labelsErrors) > 0 {
-		// if there aren't any previously reported errors, we need to set the error message header first
-		if errMsg == "" {
-			errMsg = errMsgHeader
-		} else {
-			// if there are some previously reporeted errors, we need to indent them
-			errMsg = errMsg + "\n"
-		}
+func validateConfig(c Config) error {
+	errs := [][]string{
+		validateVolumes(c.Volumes),
+		ValidateBuildEnvs(c.BuildEnvs),
+		ValidateEnvs(c.Envs),
+		validateOptions(c.Options),
+		ValidateLabels(c.Labels),
+	}
 
-		// lets make the error message a little bit nice -> indent each error message
-		for i := range volumesErrors {
-			volumesErrors[i] = "  " + volumesErrors[i]
-		}
-		for i := range buildEnvsErrors {
-			buildEnvsErrors[i] = "  " + buildEnvsErrors[i]
+	var ctr int
+	var b strings.Builder
+	b.WriteString(fmt.Sprintf("'%v' contains errors:", ConfigFile))
+	for _, ee := range errs {
+		if len(ee) > 0 {
+			b.WriteString("\n") // Precede each group of errors with a linebreak
 		}
-		for i := range envsErrors {
-			envsErrors[i] = "  " + envsErrors[i]
+		for _, e := range ee {
+			ctr++
+			b.WriteString("\t" + e)
 		}
-		for i := range optionsErrors {
-			optionsErrors[i] = "  " + optionsErrors[i]
-		}
-		for i := range labelsErrors {
-			labelsErrors[i] = "  " + labelsErrors[i]
-		}
-		errMsg = errMsg + strings.Join(volumesErrors, "\n")
-		// we have errors from both volumes and buildEnvs sections -> let's make sure they are both indented
-		if len(buildEnvsErrors) > 0 && len(volumesErrors) > 0 {
-			errMsg = errMsg + "\n"
-		}
-		errMsg = errMsg + strings.Join(buildEnvsErrors, "\n")
-		// we have errors from volumes, buildEnvs and envs sections -> let's make sure they are indented
-		if len(envsErrors) > 0 && (len(volumesErrors) > 0 || len(buildEnvsErrors) > 0) {
-			errMsg = errMsg + "\n"
-		}
-		errMsg = errMsg + strings.Join(envsErrors, "\n")
-		// lets indent options related errors if there are already some set
-		if len(optionsErrors) > 0 && (len(volumesErrors) > 0 || len(buildEnvsErrors) > 0 || len(envsErrors) > 0) {
-			errMsg = errMsg + "\n"
-		}
-		errMsg = errMsg + strings.Join(optionsErrors, "\n")
-		// now also handle labels related errors
-		if len(labelsErrors) > 0 && (len(optionsErrors) > 0 || len(volumesErrors) > 0 || len(buildEnvsErrors) > 0 || len(envsErrors) > 0) {
-			errMsg = errMsg + "\n"
-		}
-		errMsg = errMsg + strings.Join(labelsErrors, "\n")
 	}
-
-	if errMsg != "" {
-		err = errors.New(errMsg)
+	if ctr == 0 {
+		return nil // No errors were encountered
 	}
-
-	return
+	return errors.New(b.String())
 }
 
 // fromConfig returns a Function populated from config.
@@ -315,7 +279,7 @@ func writeConfig(f Function) (err error) {
 // 	 path: /etc/secret-volume
 // - configMap: example-configMap              	# mount ConfigMap as Volume
 // 	 path: /etc/configMap-volume
-func validateVolumes(volumes Volumes) (errors []string) {
+func validateVolumes(volumes []Volume) (errors []string) {
 
 	for i, vol := range volumes {
 		if vol.Secret != nil && vol.ConfigMap != nil {
@@ -345,7 +309,7 @@ func validateVolumes(volumes Volumes) (errors []string) {
 //   value: value1
 // - name: EXAMPLE2                 				# ENV from the local ENV var
 //   value: {{ env:MY_ENV }}
-func ValidateBuildEnvs(envs Envs) (errors []string) {
+func ValidateBuildEnvs(envs []Env) (errors []string) {
 	for i, env := range envs {
 		if env.Name == nil && env.Value == nil {
 			errors = append(errors, fmt.Sprintf("env entry #%d is not properly set", i))
@@ -386,7 +350,7 @@ func ValidateBuildEnvs(envs Envs) (errors []string) {
 // - name: EXAMPLE4
 //   value: {{ configMap:configMapName:key }}   	# ENV from a key in configMap
 // - value: {{ configMap:configMapName }}          	# all key-pair values from configMap are set as ENV
-func ValidateEnvs(envs Envs) (errors []string) {
+func ValidateEnvs(envs []Env) (errors []string) {
 	for i, env := range envs {
 		if env.Name == nil && env.Value == nil {
 			errors = append(errors, fmt.Sprintf("env entry #%d is not properly set", i))
@@ -429,7 +393,7 @@ func ValidateEnvs(envs Envs) (errors []string) {
 //   value: value1
 // - key: EXAMPLE2                 				# label from the local ENV var
 //   value: {{ env:MY_ENV }}
-func ValidateLabels(labels Labels) (errors []string) {
+func ValidateLabels(labels []Label) (errors []string) {
 	for i, label := range labels {
 		if label.Key == nil && label.Value == nil {
 			errors = append(errors, fmt.Sprintf("label entry #%d is not properly set", i))
diff --git a/docker/docker_client.go b/docker/docker_client.go
index 4f3e88d5..cfc74786 100644
--- a/docker/docker_client.go
+++ b/docker/docker_client.go
@@ -1,18 +1,44 @@
 package docker
 
 import (
+	"context"
+	"errors"
+	"fmt"
 	"net/http"
 	"net/url"
 	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"syscall"
+	"time"
 
 	"knative.dev/kn-plugin-func/ssh"
 
 	"github.com/docker/docker/client"
 )
 
-func NewDockerClient() (dockerClient client.CommonAPIClient, dockerHost string, err error) {
+func NewClient(defaultHost string) (dockerClient client.CommonAPIClient, dockerHost string, err error) {
+	var _url *url.URL
+
 	dockerHost = os.Getenv("DOCKER_HOST")
-	_url, err := url.Parse(dockerHost)
+
+	if dockerHost == "" && runtime.GOOS == "linux" && podmanPresent() {
+		_url, err = url.Parse(defaultHost)
+		if err != nil {
+			return
+		}
+		_, err = os.Stat(_url.Path)
+		switch {
+		case err != nil && !os.IsNotExist(err):
+			return
+		case os.IsNotExist(err):
+			dockerClient, dockerHost, err = newClientWithPodmanService()
+			return
+		}
+	}
+
+	_url, err = url.Parse(dockerHost)
 	isSSH := err == nil && _url.Scheme == "ssh"
 
 	if !isSSH {
@@ -47,3 +73,64 @@ func NewDockerClient() (dockerClient client.CommonAPIClient, dockerHost string,
 
 	return dockerClient, dockerHost, err
 }
+
+func podmanPresent() bool {
+	_, err := exec.LookPath("podman")
+	return err == nil
+}
+
+// creates a docker client that has its own podman service associated with it
+// the service is shutdown when Close() is called on the client
+func newClientWithPodmanService() (dockerClient client.CommonAPIClient, dockerHost string, err error) {
+	tmpDir, err := os.MkdirTemp("", "func-podman-")
+	if err != nil {
+		return
+	}
+
+	podmanSocket := filepath.Join(tmpDir, "podman.sock")
+	dockerHost = fmt.Sprintf("unix://%s", podmanSocket)
+
+	cmd := exec.Command("podman", "system", "service", dockerHost, "--time=0")
+	err = cmd.Start()
+	if err != nil {
+		return
+	}
+
+	dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithHost(dockerHost), client.WithAPIVersionNegotiation())
+	stopPodmanService := func() {
+		_ = cmd.Process.Signal(syscall.SIGTERM)
+		_ = os.RemoveAll(tmpDir)
+	}
+	dockerClient = withPodman{
+		pimpl:      dockerClient,
+		stopPodman: stopPodmanService,
+	}
+
+	podmanServiceRunning := false
+	// give a time to podman to start
+	for i := 0; i < 40; i++ {
+		if _, e := dockerClient.Ping(context.Background()); e == nil {
+			podmanServiceRunning = true
+			break
+		}
+		time.Sleep(time.Millisecond * 250)
+	}
+
+	if !podmanServiceRunning {
+		stopPodmanService()
+		err = errors.New("failed to start podman service")
+	}
+
+	return
+}
+
+type withPodman struct {
+	stopPodman func()
+	pimpl      client.CommonAPIClient
+}
+
+// Close function need to stop associated podman service
+func (w withPodman) Close() error {
+	defer w.stopPodman()
+	return w.pimpl.Close()
+}
diff --git a/docker/docker_client_generated.go b/docker/docker_client_generated.go
new file mode 100644
index 00000000..f13daea2
--- /dev/null
+++ b/docker/docker_client_generated.go
@@ -0,0 +1,488 @@
+package docker
+
+import (
+	"context"
+	"io"
+	"net"
+	"net/http"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/image"
+	"github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/api/types/volume"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+//<editor-fold desc="plain forwards to pimpl">
+
+func (w withPodman) BuildCachePrune(arg0 context.Context, arg1 types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
+	return w.pimpl.BuildCachePrune(arg0, arg1)
+}
+
+func (w withPodman) BuildCancel(arg0 context.Context, arg1 string) error {
+	return w.pimpl.BuildCancel(arg0, arg1)
+}
+
+func (w withPodman) ClientVersion() string {
+	return w.pimpl.ClientVersion()
+}
+
+func (w withPodman) ConfigCreate(arg0 context.Context, arg1 swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
+	return w.pimpl.ConfigCreate(arg0, arg1)
+}
+
+func (w withPodman) ConfigInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Config, []uint8, error) {
+	return w.pimpl.ConfigInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) ConfigList(arg0 context.Context, arg1 types.ConfigListOptions) ([]swarm.Config, error) {
+	return w.pimpl.ConfigList(arg0, arg1)
+}
+
+func (w withPodman) ConfigRemove(arg0 context.Context, arg1 string) error {
+	return w.pimpl.ConfigRemove(arg0, arg1)
+}
+
+func (w withPodman) ConfigUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ConfigSpec) error {
+	return w.pimpl.ConfigUpdate(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) ContainerAttach(arg0 context.Context, arg1 string, arg2 types.ContainerAttachOptions) (types.HijackedResponse, error) {
+	return w.pimpl.ContainerAttach(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerCommit(arg0 context.Context, arg1 string, arg2 types.ContainerCommitOptions) (types.IDResponse, error) {
+	return w.pimpl.ContainerCommit(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerCreate(arg0 context.Context, arg1 *container.Config, arg2 *container.HostConfig, arg3 *network.NetworkingConfig, arg4 *v1.Platform, arg5 string) (container.ContainerCreateCreatedBody, error) {
+	return w.pimpl.ContainerCreate(arg0, arg1, arg2, arg3, arg4, arg5)
+}
+
+func (w withPodman) ContainerDiff(arg0 context.Context, arg1 string) ([]container.ContainerChangeResponseItem, error) {
+	return w.pimpl.ContainerDiff(arg0, arg1)
+}
+
+func (w withPodman) ContainerExecAttach(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) (types.HijackedResponse, error) {
+	return w.pimpl.ContainerExecAttach(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerExecCreate(arg0 context.Context, arg1 string, arg2 types.ExecConfig) (types.IDResponse, error) {
+	return w.pimpl.ContainerExecCreate(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerExecInspect(arg0 context.Context, arg1 string) (types.ContainerExecInspect, error) {
+	return w.pimpl.ContainerExecInspect(arg0, arg1)
+}
+
+func (w withPodman) ContainerExecResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error {
+	return w.pimpl.ContainerExecResize(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerExecStart(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) error {
+	return w.pimpl.ContainerExecStart(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerExport(arg0 context.Context, arg1 string) (io.ReadCloser, error) {
+	return w.pimpl.ContainerExport(arg0, arg1)
+}
+
+func (w withPodman) ContainerInspect(arg0 context.Context, arg1 string) (types.ContainerJSON, error) {
+	return w.pimpl.ContainerInspect(arg0, arg1)
+}
+
+func (w withPodman) ContainerInspectWithRaw(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerJSON, []uint8, error) {
+	return w.pimpl.ContainerInspectWithRaw(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerKill(arg0 context.Context, arg1 string, arg2 string) error {
+	return w.pimpl.ContainerKill(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerList(arg0 context.Context, arg1 types.ContainerListOptions) ([]types.Container, error) {
+	return w.pimpl.ContainerList(arg0, arg1)
+}
+
+func (w withPodman) ContainerLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
+	return w.pimpl.ContainerLogs(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerPause(arg0 context.Context, arg1 string) error {
+	return w.pimpl.ContainerPause(arg0, arg1)
+}
+
+func (w withPodman) ContainerRemove(arg0 context.Context, arg1 string, arg2 types.ContainerRemoveOptions) error {
+	return w.pimpl.ContainerRemove(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerRename(arg0 context.Context, arg1 string, arg2 string) error {
+	return w.pimpl.ContainerRename(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error {
+	return w.pimpl.ContainerResize(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerRestart(arg0 context.Context, arg1 string, arg2 *time.Duration) error {
+	return w.pimpl.ContainerRestart(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerStart(arg0 context.Context, arg1 string, arg2 types.ContainerStartOptions) error {
+	return w.pimpl.ContainerStart(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerStatPath(arg0 context.Context, arg1 string, arg2 string) (types.ContainerPathStat, error) {
+	return w.pimpl.ContainerStatPath(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerStats(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerStats, error) {
+	return w.pimpl.ContainerStats(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerStatsOneShot(arg0 context.Context, arg1 string) (types.ContainerStats, error) {
+	return w.pimpl.ContainerStatsOneShot(arg0, arg1)
+}
+
+func (w withPodman) ContainerStop(arg0 context.Context, arg1 string, arg2 *time.Duration) error {
+	return w.pimpl.ContainerStop(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerTop(arg0 context.Context, arg1 string, arg2 []string) (container.ContainerTopOKBody, error) {
+	return w.pimpl.ContainerTop(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerUnpause(arg0 context.Context, arg1 string) error {
+	return w.pimpl.ContainerUnpause(arg0, arg1)
+}
+
+func (w withPodman) ContainerUpdate(arg0 context.Context, arg1 string, arg2 container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
+	return w.pimpl.ContainerUpdate(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainerWait(arg0 context.Context, arg1 string, arg2 container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) {
+	return w.pimpl.ContainerWait(arg0, arg1, arg2)
+}
+
+func (w withPodman) ContainersPrune(arg0 context.Context, arg1 filters.Args) (types.ContainersPruneReport, error) {
+	return w.pimpl.ContainersPrune(arg0, arg1)
+}
+
+func (w withPodman) CopyFromContainer(arg0 context.Context, arg1 string, arg2 string) (io.ReadCloser, types.ContainerPathStat, error) {
+	return w.pimpl.CopyFromContainer(arg0, arg1, arg2)
+}
+
+func (w withPodman) CopyToContainer(arg0 context.Context, arg1 string, arg2 string, arg3 io.Reader, arg4 types.CopyToContainerOptions) error {
+	return w.pimpl.CopyToContainer(arg0, arg1, arg2, arg3, arg4)
+}
+
+func (w withPodman) DaemonHost() string {
+	return w.pimpl.DaemonHost()
+}
+
+func (w withPodman) DialHijack(arg0 context.Context, arg1 string, arg2 string, arg3 map[string][]string) (net.Conn, error) {
+	return w.pimpl.DialHijack(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) Dialer() func(context.Context) (net.Conn, error) {
+	return w.pimpl.Dialer()
+}
+
+func (w withPodman) DiskUsage(arg0 context.Context) (types.DiskUsage, error) {
+	return w.pimpl.DiskUsage(arg0)
+}
+
+func (w withPodman) DistributionInspect(arg0 context.Context, arg1 string, arg2 string) (registry.DistributionInspect, error) {
+	return w.pimpl.DistributionInspect(arg0, arg1, arg2)
+}
+
+func (w withPodman) Events(arg0 context.Context, arg1 types.EventsOptions) (<-chan events.Message, <-chan error) {
+	return w.pimpl.Events(arg0, arg1)
+}
+
+func (w withPodman) HTTPClient() *http.Client {
+	return w.pimpl.HTTPClient()
+}
+
+func (w withPodman) ImageBuild(arg0 context.Context, arg1 io.Reader, arg2 types.ImageBuildOptions) (types.ImageBuildResponse, error) {
+	return w.pimpl.ImageBuild(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImageCreate(arg0 context.Context, arg1 string, arg2 types.ImageCreateOptions) (io.ReadCloser, error) {
+	return w.pimpl.ImageCreate(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImageHistory(arg0 context.Context, arg1 string) ([]image.HistoryResponseItem, error) {
+	return w.pimpl.ImageHistory(arg0, arg1)
+}
+
+func (w withPodman) ImageImport(arg0 context.Context, arg1 types.ImageImportSource, arg2 string, arg3 types.ImageImportOptions) (io.ReadCloser, error) {
+	return w.pimpl.ImageImport(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) ImageInspectWithRaw(arg0 context.Context, arg1 string) (types.ImageInspect, []uint8, error) {
+	return w.pimpl.ImageInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) ImageList(arg0 context.Context, arg1 types.ImageListOptions) ([]types.ImageSummary, error) {
+	return w.pimpl.ImageList(arg0, arg1)
+}
+
+func (w withPodman) ImageLoad(arg0 context.Context, arg1 io.Reader, arg2 bool) (types.ImageLoadResponse, error) {
+	return w.pimpl.ImageLoad(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImagePull(arg0 context.Context, arg1 string, arg2 types.ImagePullOptions) (io.ReadCloser, error) {
+	return w.pimpl.ImagePull(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImagePush(arg0 context.Context, arg1 string, arg2 types.ImagePushOptions) (io.ReadCloser, error) {
+	return w.pimpl.ImagePush(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImageRemove(arg0 context.Context, arg1 string, arg2 types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
+	return w.pimpl.ImageRemove(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImageSave(arg0 context.Context, arg1 []string) (io.ReadCloser, error) {
+	return w.pimpl.ImageSave(arg0, arg1)
+}
+
+func (w withPodman) ImageSearch(arg0 context.Context, arg1 string, arg2 types.ImageSearchOptions) ([]registry.SearchResult, error) {
+	return w.pimpl.ImageSearch(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImageTag(arg0 context.Context, arg1 string, arg2 string) error {
+	return w.pimpl.ImageTag(arg0, arg1, arg2)
+}
+
+func (w withPodman) ImagesPrune(arg0 context.Context, arg1 filters.Args) (types.ImagesPruneReport, error) {
+	return w.pimpl.ImagesPrune(arg0, arg1)
+}
+
+func (w withPodman) Info(arg0 context.Context) (types.Info, error) {
+	return w.pimpl.Info(arg0)
+}
+
+func (w withPodman) NegotiateAPIVersion(arg0 context.Context) {
+	w.pimpl.NegotiateAPIVersion(arg0)
+}
+
+func (w withPodman) NegotiateAPIVersionPing(arg0 types.Ping) {
+	w.pimpl.NegotiateAPIVersionPing(arg0)
+}
+
+func (w withPodman) NetworkConnect(arg0 context.Context, arg1 string, arg2 string, arg3 *network.EndpointSettings) error {
+	return w.pimpl.NetworkConnect(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) NetworkCreate(arg0 context.Context, arg1 string, arg2 types.NetworkCreate) (types.NetworkCreateResponse, error) {
+	return w.pimpl.NetworkCreate(arg0, arg1, arg2)
+}
+
+func (w withPodman) NetworkDisconnect(arg0 context.Context, arg1 string, arg2 string, arg3 bool) error {
+	return w.pimpl.NetworkDisconnect(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) NetworkInspect(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, error) {
+	return w.pimpl.NetworkInspect(arg0, arg1, arg2)
+}
+
+func (w withPodman) NetworkInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, []uint8, error) {
+	return w.pimpl.NetworkInspectWithRaw(arg0, arg1, arg2)
+}
+
+func (w withPodman) NetworkList(arg0 context.Context, arg1 types.NetworkListOptions) ([]types.NetworkResource, error) {
+	return w.pimpl.NetworkList(arg0, arg1)
+}
+
+func (w withPodman) NetworkRemove(arg0 context.Context, arg1 string) error {
+	return w.pimpl.NetworkRemove(arg0, arg1)
+}
+
+func (w withPodman) NetworksPrune(arg0 context.Context, arg1 filters.Args) (types.NetworksPruneReport, error) {
+	return w.pimpl.NetworksPrune(arg0, arg1)
+}
+
+func (w withPodman) NodeInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Node, []uint8, error) {
+	return w.pimpl.NodeInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) NodeList(arg0 context.Context, arg1 types.NodeListOptions) ([]swarm.Node, error) {
+	return w.pimpl.NodeList(arg0, arg1)
+}
+
+func (w withPodman) NodeRemove(arg0 context.Context, arg1 string, arg2 types.NodeRemoveOptions) error {
+	return w.pimpl.NodeRemove(arg0, arg1, arg2)
+}
+
+func (w withPodman) NodeUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.NodeSpec) error {
+	return w.pimpl.NodeUpdate(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) Ping(arg0 context.Context) (types.Ping, error) {
+	return w.pimpl.Ping(arg0)
+}
+
+func (w withPodman) PluginCreate(arg0 context.Context, arg1 io.Reader, arg2 types.PluginCreateOptions) error {
+	return w.pimpl.PluginCreate(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginDisable(arg0 context.Context, arg1 string, arg2 types.PluginDisableOptions) error {
+	return w.pimpl.PluginDisable(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginEnable(arg0 context.Context, arg1 string, arg2 types.PluginEnableOptions) error {
+	return w.pimpl.PluginEnable(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginInspectWithRaw(arg0 context.Context, arg1 string) (*types.Plugin, []uint8, error) {
+	return w.pimpl.PluginInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) PluginInstall(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) {
+	return w.pimpl.PluginInstall(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginList(arg0 context.Context, arg1 filters.Args) (types.PluginsListResponse, error) {
+	return w.pimpl.PluginList(arg0, arg1)
+}
+
+func (w withPodman) PluginPush(arg0 context.Context, arg1 string, arg2 string) (io.ReadCloser, error) {
+	return w.pimpl.PluginPush(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginRemove(arg0 context.Context, arg1 string, arg2 types.PluginRemoveOptions) error {
+	return w.pimpl.PluginRemove(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginSet(arg0 context.Context, arg1 string, arg2 []string) error {
+	return w.pimpl.PluginSet(arg0, arg1, arg2)
+}
+
+func (w withPodman) PluginUpgrade(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) {
+	return w.pimpl.PluginUpgrade(arg0, arg1, arg2)
+}
+
+func (w withPodman) RegistryLogin(arg0 context.Context, arg1 types.AuthConfig) (registry.AuthenticateOKBody, error) {
+	return w.pimpl.RegistryLogin(arg0, arg1)
+}
+
+func (w withPodman) SecretCreate(arg0 context.Context, arg1 swarm.SecretSpec) (types.SecretCreateResponse, error) {
+	return w.pimpl.SecretCreate(arg0, arg1)
+}
+
+func (w withPodman) SecretInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Secret, []uint8, error) {
+	return w.pimpl.SecretInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) SecretList(arg0 context.Context, arg1 types.SecretListOptions) ([]swarm.Secret, error) {
+	return w.pimpl.SecretList(arg0, arg1)
+}
+
+func (w withPodman) SecretRemove(arg0 context.Context, arg1 string) error {
+	return w.pimpl.SecretRemove(arg0, arg1)
+}
+
+func (w withPodman) SecretUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.SecretSpec) error {
+	return w.pimpl.SecretUpdate(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) ServerVersion(arg0 context.Context) (types.Version, error) {
+	return w.pimpl.ServerVersion(arg0)
+}
+
+func (w withPodman) ServiceCreate(arg0 context.Context, arg1 swarm.ServiceSpec, arg2 types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
+	return w.pimpl.ServiceCreate(arg0, arg1, arg2)
+}
+
+func (w withPodman) ServiceInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.ServiceInspectOptions) (swarm.Service, []uint8, error) {
+	return w.pimpl.ServiceInspectWithRaw(arg0, arg1, arg2)
+}
+
+func (w withPodman) ServiceList(arg0 context.Context, arg1 types.ServiceListOptions) ([]swarm.Service, error) {
+	return w.pimpl.ServiceList(arg0, arg1)
+}
+
+func (w withPodman) ServiceLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
+	return w.pimpl.ServiceLogs(arg0, arg1, arg2)
+}
+
+func (w withPodman) ServiceRemove(arg0 context.Context, arg1 string) error {
+	return w.pimpl.ServiceRemove(arg0, arg1)
+}
+
+func (w withPodman) ServiceUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ServiceSpec, arg4 types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
+	return w.pimpl.ServiceUpdate(arg0, arg1, arg2, arg3, arg4)
+}
+
+func (w withPodman) SwarmGetUnlockKey(arg0 context.Context) (types.SwarmUnlockKeyResponse, error) {
+	return w.pimpl.SwarmGetUnlockKey(arg0)
+}
+
+func (w withPodman) SwarmInit(arg0 context.Context, arg1 swarm.InitRequest) (string, error) {
+	return w.pimpl.SwarmInit(arg0, arg1)
+}
+
+func (w withPodman) SwarmInspect(arg0 context.Context) (swarm.Swarm, error) {
+	return w.pimpl.SwarmInspect(arg0)
+}
+
+func (w withPodman) SwarmJoin(arg0 context.Context, arg1 swarm.JoinRequest) error {
+	return w.pimpl.SwarmJoin(arg0, arg1)
+}
+
+func (w withPodman) SwarmLeave(arg0 context.Context, arg1 bool) error {
+	return w.pimpl.SwarmLeave(arg0, arg1)
+}
+
+func (w withPodman) SwarmUnlock(arg0 context.Context, arg1 swarm.UnlockRequest) error {
+	return w.pimpl.SwarmUnlock(arg0, arg1)
+}
+
+func (w withPodman) SwarmUpdate(arg0 context.Context, arg1 swarm.Version, arg2 swarm.Spec, arg3 swarm.UpdateFlags) error {
+	return w.pimpl.SwarmUpdate(arg0, arg1, arg2, arg3)
+}
+
+func (w withPodman) TaskInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Task, []uint8, error) {
+	return w.pimpl.TaskInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) TaskList(arg0 context.Context, arg1 types.TaskListOptions) ([]swarm.Task, error) {
+	return w.pimpl.TaskList(arg0, arg1)
+}
+
+func (w withPodman) TaskLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
+	return w.pimpl.TaskLogs(arg0, arg1, arg2)
+}
+
+func (w withPodman) VolumeCreate(arg0 context.Context, arg1 volume.VolumeCreateBody) (types.Volume, error) {
+	return w.pimpl.VolumeCreate(arg0, arg1)
+}
+
+func (w withPodman) VolumeInspect(arg0 context.Context, arg1 string) (types.Volume, error) {
+	return w.pimpl.VolumeInspect(arg0, arg1)
+}
+
+func (w withPodman) VolumeInspectWithRaw(arg0 context.Context, arg1 string) (types.Volume, []uint8, error) {
+	return w.pimpl.VolumeInspectWithRaw(arg0, arg1)
+}
+
+func (w withPodman) VolumeList(arg0 context.Context, arg1 filters.Args) (volume.VolumeListOKBody, error) {
+	return w.pimpl.VolumeList(arg0, arg1)
+}
+
+func (w withPodman) VolumeRemove(arg0 context.Context, arg1 string, arg2 bool) error {
+	return w.pimpl.VolumeRemove(arg0, arg1, arg2)
+}
+
+func (w withPodman) VolumesPrune(arg0 context.Context, arg1 filters.Args) (types.VolumesPruneReport, error) {
+	return w.pimpl.VolumesPrune(arg0, arg1)
+}
+
+//</editor-fold>
diff --git a/docker/pusher.go b/docker/pusher.go
index df1aaf7a..73e0fff3 100644
--- a/docker/pusher.go
+++ b/docker/pusher.go
@@ -13,6 +13,8 @@ import (
 	"regexp"
 	"strings"
 
+	"github.com/docker/docker/client"
+
 	fn "knative.dev/kn-plugin-func"
 
 	"github.com/containers/image/v5/pkg/docker/config"
@@ -39,10 +41,11 @@ var ErrUnauthorized = errors.New("bad credentials")
 type VerifyCredentialsCallback func(ctx context.Context, username, password, registry string) error
 
 func CheckAuth(ctx context.Context, username, password, registry string) error {
-	cli, _, err := NewDockerClient()
+	cli, _, err := NewClient(client.DefaultDockerHost)
 	if err != nil {
 		return err
 	}
+	defer cli.Close()
 
 	_, err = cli.RegistryLogin(ctx, types.AuthConfig{Username: username, Password: password, ServerAddress: registry})
 	if err != nil && strings.Contains(err.Error(), "401 Unauthorized") {
@@ -242,10 +245,11 @@ func (n *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err er
 		return "", err
 	}
 
-	cli, _, err := NewDockerClient()
+	cli, _, err := NewClient(client.DefaultDockerHost)
 	if err != nil {
 		return "", fmt.Errorf("failed to create docker api client: %w", err)
 	}
+	defer cli.Close()
 
 	n.progressListener.Stopping()
 	credentials, err := n.credentialsProvider(ctx, registry)
diff --git a/docker/runner.go b/docker/runner.go
index ff67103f..a96fa47c 100644
--- a/docker/runner.go
+++ b/docker/runner.go
@@ -8,6 +8,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/client"
+
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/pkg/stdcopy"
@@ -33,10 +35,11 @@ func (n *Runner) Run(ctx context.Context, f fn.Function) error {
 	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
 
-	cli, _, err := NewDockerClient()
+	cli, _, err := NewClient(client.DefaultDockerHost)
 	if err != nil {
 		return errors.Wrap(err, "failed to create docker api client")
 	}
+	defer cli.Close()
 
 	if f.Image == "" {
 		return errors.New("Function has no associated Image. Has it been built?")
diff --git a/function.go b/function.go
index 0697f98d..fed90928 100644
--- a/function.go
+++ b/function.go
@@ -56,13 +56,13 @@ type Function struct {
 	Buildpacks []string
 
 	// List of volumes to be mounted to the function
-	Volumes Volumes
+	Volumes []Volume
 
 	// Build Env variables to be set
-	BuildEnvs Envs
+	BuildEnvs []Env
 
 	// Env variables to be set
-	Envs Envs
+	Envs []Env
 
 	// Map containing user-supplied annotations
 	// Example: { "division": "finance" }
@@ -72,7 +72,7 @@ type Function struct {
 	Options Options
 
 	// Map of user-supplied labels
-	Labels Labels
+	Labels []Label
 
 	// Health endpoints specified by the language pack
 	HealthEndpoints HealthEndpoints
diff --git a/go.mod b/go.mod
index d498bcb1..a999454e 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c
 	github.com/markbates/pkger v0.17.1
 	github.com/mitchellh/go-homedir v1.1.0
+	github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
 	github.com/ory/viper v1.7.5
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/cobra v1.2.1
diff --git a/knative/deployer.go b/knative/deployer.go
index a6bf4c56..6b0d71c6 100644
--- a/knative/deployer.go
+++ b/knative/deployer.go
@@ -390,7 +390,7 @@ func processLabels(f fn.Function) (map[string]string, error) {
 //   - name: EXAMPLE4
 //     value: {{ configMap:configMapName:key }}  # ENV from a key in ConfigMap
 //   - value: {{ configMap:configMapName }}      # all key-pair values from ConfigMap are set as ENV
-func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.EnvVar, []corev1.EnvFromSource, error) {
+func processEnvs(envs []fn.Env, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.EnvVar, []corev1.EnvFromSource, error) {
 
 	envVars := []corev1.EnvVar{{Name: "BUILT", Value: time.Now().Format("20060102T150405")}}
 	envFrom := []corev1.EnvFromSource{}
@@ -563,7 +563,7 @@ func processLocalEnvValue(val string) (string, error) {
 //   path: /etc/secret-volume
 // - configMap: example-cm                # mount ConfigMap as Volume
 //   path: /etc/cm-volume
-func processVolumes(volumes fn.Volumes, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.Volume, []corev1.VolumeMount, error) {
+func processVolumes(volumes []fn.Volume, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.Volume, []corev1.VolumeMount, error) {
 
 	createdVolumes := sets.NewString()
 	usedPaths := sets.NewString()
diff --git a/pkged.go b/pkged.go
index a38e1462..dca76893 100644
--- a/pkged.go
+++ b/pkged.go
@@ -1,5 +1,6 @@
 // Code generated by pkger; DO NOT EDIT.
 
+//go:build !skippkger
 // +build !skippkger
 
 package function
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 49e37c0d..af64fa05 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -536,6 +536,7 @@ github.com/morikuni/aec
 # github.com/opencontainers/go-digest v1.0.0
 github.com/opencontainers/go-digest
 # github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
+## explicit
 github.com/opencontainers/image-spec/specs-go
 github.com/opencontainers/image-spec/specs-go/v1
 # github.com/opencontainers/runc v1.0.0-rc93


Test patch:
diff --git a/cloudevents/emitter_test.go b/cloudevents/emitter_test.go
index 31fbe631..a16ddbe6 100644
--- a/cloudevents/emitter_test.go
+++ b/cloudevents/emitter_test.go
@@ -1,3 +1,6 @@
+//go:build !integration
+// +build !integration
+
 package cloudevents
 
 import (
diff --git a/cmd/config_labels_test.go b/cmd/config_labels_test.go
index 69ad47a6..197a5622 100644
--- a/cmd/config_labels_test.go
+++ b/cmd/config_labels_test.go
@@ -30,7 +30,7 @@ func (m *mockFunctionLoaderSaver) Save(f fn.Function) error {
 	return nil
 }
 
-func assertLabelEq(t *testing.T, actual fn.Labels, want fn.Labels) {
+func assertLabelEq(t *testing.T, actual []fn.Label, want []fn.Label) {
 	t.Helper()
 	if !reflect.DeepEqual(actual, want) {
 		t.Errorf("labels =  %v, want %v", actual, want)
@@ -113,22 +113,22 @@ func TestNewConfigLabelsCmd(t *testing.T) {
 		return fn.Label{Key: &k, Value: &v}
 	}
 
-	assertLabel := func(ps fn.Labels) {
+	assertLabel := func(ps []fn.Label) {
 		t.Helper()
 		assertLabelEq(t, *labels, ps)
 	}
 
 	run("add", enter, "a", enter, "b", enter)
-	assertLabel(fn.Labels{p("a", "b")})
+	assertLabel([]fn.Label{p("a", "b")})
 
 	run("add", enter, enter, "c", enter, "d", enter)
-	assertLabel(fn.Labels{p("a", "b"), p("c", "d")})
+	assertLabel([]fn.Label{p("a", "b"), p("c", "d")})
 
 	run("add", arrowUp, arrowUp, enter, enter, "e", enter, "f", enter)
-	assertLabel(fn.Labels{p("e", "f"), p("a", "b"), p("c", "d")})
+	assertLabel([]fn.Label{p("e", "f"), p("a", "b"), p("c", "d")})
 
 	run("remove", arrowDown, enter)
-	assertLabel(fn.Labels{p("e", "f"), p("c", "d")})
+	assertLabel([]fn.Label{p("e", "f"), p("c", "d")})
 }
 
 func TestListLabels(t *testing.T) {
diff --git a/cmd/root_test.go b/cmd/root_test.go
index ba13f9df..4cd22b53 100644
--- a/cmd/root_test.go
+++ b/cmd/root_test.go
@@ -18,77 +18,77 @@ func Test_mergeEnvMaps(t *testing.T) {
 	v2 := "y"
 
 	type args struct {
-		envs     fn.Envs
+		envs     []fn.Env
 		toUpdate *util.OrderedMap
 		toRemove []string
 	}
 	tests := []struct {
 		name string
 		args args
-		want fn.Envs
+		want []fn.Env
 	}{
 		{
 			"add new var to empty list",
 			args{
-				fn.Envs{},
+				[]fn.Env{},
 				util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
 				[]string{},
 			},
-			fn.Envs{fn.Env{Name: &a, Value: &v1}},
+			[]fn.Env{{Name: &a, Value: &v1}},
 		},
 		{
 			"add new var",
 			args{
-				fn.Envs{fn.Env{Name: &b, Value: &v2}},
+				[]fn.Env{{Name: &b, Value: &v2}},
 				util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
 				[]string{},
 			},
-			fn.Envs{fn.Env{Name: &b, Value: &v2}, fn.Env{Name: &a, Value: &v1}},
+			[]fn.Env{{Name: &b, Value: &v2}, {Name: &a, Value: &v1}},
 		},
 		{
 			"update var",
 			args{
-				fn.Envs{fn.Env{Name: &a, Value: &v1}},
+				[]fn.Env{{Name: &a, Value: &v1}},
 				util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
 				[]string{},
 			},
-			fn.Envs{fn.Env{Name: &a, Value: &v2}},
+			[]fn.Env{{Name: &a, Value: &v2}},
 		},
 		{
 			"update multiple vars",
 			args{
-				fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
+				[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
 				util.NewOrderedMapWithKVStrings([][]string{{a, v2}, {b, v1}}),
 				[]string{},
 			},
-			fn.Envs{fn.Env{Name: &a, Value: &v2}, fn.Env{Name: &b, Value: &v1}},
+			[]fn.Env{{Name: &a, Value: &v2}, {Name: &b, Value: &v1}},
 		},
 		{
 			"remove var",
 			args{
-				fn.Envs{fn.Env{Name: &a, Value: &v1}},
+				[]fn.Env{{Name: &a, Value: &v1}},
 				util.NewOrderedMap(),
 				[]string{a},
 			},
-			fn.Envs{},
+			[]fn.Env{},
 		},
 		{
 			"remove multiple vars",
 			args{
-				fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
+				[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
 				util.NewOrderedMap(),
 				[]string{a, b},
 			},
-			fn.Envs{},
+			[]fn.Env{},
 		},
 		{
 			"update and remove vars",
 			args{
-				fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
+				[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
 				util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
 				[]string{b},
 			},
-			fn.Envs{fn.Env{Name: &a, Value: &v2}},
+			[]fn.Env{{Name: &a, Value: &v2}},
 		},
 	}
 	for _, tt := range tests {
diff --git a/config_test.go b/config_test.go
index b0790242..cdd790fb 100644
--- a/config_test.go
+++ b/config_test.go
@@ -20,13 +20,13 @@ func Test_validateVolumes(t *testing.T) {
 
 	tests := []struct {
 		name    string
-		volumes Volumes
+		volumes []Volume
 		errs    int
 	}{
 		{
 			"correct entry - single volume with secret",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 					Path:   &path,
 				},
@@ -35,8 +35,8 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"correct entry - single volume with configmap",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					ConfigMap: &cm,
 					Path:      &path,
 				},
@@ -45,12 +45,12 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"correct entry - multiple volumes with secrets",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 					Path:   &path,
 				},
-				Volume{
+				{
 					Secret: &secret2,
 					Path:   &path2,
 				},
@@ -59,12 +59,12 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"correct entry - multiple volumes with both secret and configMap",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 					Path:   &path,
 				},
-				Volume{
+				{
 					ConfigMap: &cm,
 					Path:      &path2,
 				},
@@ -73,8 +73,8 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"missing secret/configMap - single volume",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Path: &path,
 				},
 			},
@@ -82,8 +82,8 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"missing path - single volume with secret",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 				},
 			},
@@ -91,8 +91,8 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"missing path - single volume with configMap",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					ConfigMap: &cm,
 				},
 			},
@@ -100,19 +100,19 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"missing secret/configMap and path - single volume",
-			Volumes{
-				Volume{},
+			[]Volume{
+				{},
 			},
 			1,
 		},
 		{
 			"missing secret/configMap in one volume - multiple volumes",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 					Path:   &path,
 				},
-				Volume{
+				{
 					Path: &path2,
 				},
 			},
@@ -120,15 +120,15 @@ func Test_validateVolumes(t *testing.T) {
 		},
 		{
 			"missing secret/configMap and path in two different volumes - multiple volumes",
-			Volumes{
-				Volume{
+			[]Volume{
+				{
 					Secret: &secret,
 					Path:   &path,
 				},
-				Volume{
+				{
 					Secret: &secret,
 				},
-				Volume{
+				{
 					Path: &path2,
 				},
 			},
@@ -165,13 +165,13 @@ func Test_validateBuildEnvs(t *testing.T) {
 
 	tests := []struct {
 		name string
-		envs Envs
+		envs []Env
 		errs int
 	}{
 		{
 			"correct entry - single env with value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &value,
 				},
@@ -180,8 +180,8 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - missing value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name: &name,
 				},
 			},
@@ -189,8 +189,8 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid name",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &incorrectName,
 					Value: &value,
 				},
@@ -199,8 +199,8 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid name2",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &incorrectName2,
 					Value: &value,
 				},
@@ -209,12 +209,12 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple envs with value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &value,
 				},
-				Env{
+				{
 					Name:  &name2,
 					Value: &value2,
 				},
@@ -223,11 +223,11 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - mmissing value - multiple envs",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name: &name,
 				},
-				Env{
+				{
 					Name: &name2,
 				},
 			},
@@ -235,8 +235,8 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single env with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
@@ -245,16 +245,16 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple envs with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv3,
 				},
@@ -263,20 +263,20 @@ func Test_validateBuildEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - multiple envs with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect3,
 				},
@@ -341,13 +341,13 @@ func Test_validateEnvs(t *testing.T) {
 
 	tests := []struct {
 		name string
-		envs Envs
+		envs []Env
 		errs int
 	}{
 		{
 			"correct entry - single env with value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &value,
 				},
@@ -356,8 +356,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - missing value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name: &name,
 				},
 			},
@@ -365,8 +365,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid name",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &incorrectName,
 					Value: &value,
 				},
@@ -375,8 +375,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid name2",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &incorrectName2,
 					Value: &value,
 				},
@@ -385,12 +385,12 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple envs with value",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &value,
 				},
-				Env{
+				{
 					Name:  &name2,
 					Value: &value2,
 				},
@@ -399,11 +399,11 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - mmissing value - multiple envs",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name: &name,
 				},
-				Env{
+				{
 					Name: &name2,
 				},
 			},
@@ -411,8 +411,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single env with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
@@ -421,16 +421,16 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple envs with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv3,
 				},
@@ -439,20 +439,20 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - multiple envs with value Local env",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnvIncorrect3,
 				},
@@ -461,8 +461,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single secret with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey,
 				},
@@ -471,8 +471,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single configMap with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey,
 				},
@@ -481,36 +481,36 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple configMaps with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey3,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey4,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey5,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey6,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey7,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey8,
 				},
@@ -519,36 +519,36 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple secrets with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey3,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey4,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey5,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey6,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey7,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey8,
 				},
@@ -557,12 +557,12 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - both secret and configmap with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey,
 				},
@@ -571,8 +571,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - single secret with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKeyIncorrect,
 				},
@@ -581,20 +581,20 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - mutliple secrets with key",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKeyIncorrect,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKeyIncorrect2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKeyIncorrect3,
 				},
@@ -603,8 +603,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single whole secret",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &valueSecret,
 				},
 			},
@@ -612,8 +612,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - single whole configMap",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &valueConfigMap,
 				},
 			},
@@ -621,14 +621,14 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - multiple whole secret",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &valueSecret,
 				},
-				Env{
+				{
 					Value: &valueSecret2,
 				},
-				Env{
+				{
 					Value: &valueSecret3,
 				},
 			},
@@ -636,11 +636,11 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - both whole secret and configMap",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &valueSecret,
 				},
-				Env{
+				{
 					Value: &valueConfigMap,
 				},
 			},
@@ -648,8 +648,8 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - single whole secret",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &value,
 				},
 			},
@@ -657,29 +657,29 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"incorrect entry - multiple whole secret",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Value: &valueSecretIncorrect,
 				},
-				Env{
+				{
 					Value: &valueSecretIncorrect2,
 				},
-				Env{
+				{
 					Value: &valueSecretIncorrect3,
 				},
-				Env{
+				{
 					Value: &value,
 				},
-				Env{
+				{
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Value: &valueLocalEnv2,
 				},
-				Env{
+				{
 					Value: &valueLocalEnv3,
 				},
-				Env{
+				{
 					Value: &valueSecret,
 				},
 			},
@@ -687,52 +687,52 @@ func Test_validateEnvs(t *testing.T) {
 		},
 		{
 			"correct entry - all combinations",
-			Envs{
-				Env{
+			[]Env{
+				{
 					Name:  &name,
 					Value: &value,
 				},
-				Env{
+				{
 					Name:  &name2,
 					Value: &value2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueLocalEnv3,
 				},
-				Env{
+				{
 					Value: &valueSecret,
 				},
-				Env{
+				{
 					Value: &valueSecret2,
 				},
-				Env{
+				{
 					Value: &valueSecret3,
 				},
-				Env{
+				{
 					Value: &valueConfigMap,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey2,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueSecretKey3,
 				},
-				Env{
+				{
 					Name:  &name,
 					Value: &valueConfigMapKey,
 				},
@@ -779,13 +779,13 @@ func Test_validateLabels(t *testing.T) {
 
 	tests := []struct {
 		key    string
-		labels Labels
+		labels []Label
 		errs   int
 	}{
 		{
 			"correct entry - single label with value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &value,
 				},
@@ -794,8 +794,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - prefixed label with value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key3,
 					Value: &value3,
 				},
@@ -803,19 +803,19 @@ func Test_validateLabels(t *testing.T) {
 			0,
 		}, {
 			"incorrect entry - missing key",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Value: &value,
 				},
 			},
 			1,
 		}, {
 			"incorrect entry - missing multiple keys",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Value: &value,
 				},
-				Label{
+				{
 					Value: &value2,
 				},
 			},
@@ -823,8 +823,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid key",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &incorrectKey,
 					Value: &value,
 				},
@@ -833,8 +833,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid key2",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &incorrectKey2,
 					Value: &value,
 				},
@@ -843,8 +843,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"incorrect entry - invalid value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &incorrectValue,
 				},
@@ -853,12 +853,12 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - multiple labels with value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &value,
 				},
-				Label{
+				{
 					Key:   &key2,
 					Value: &value2,
 				},
@@ -867,11 +867,11 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - missing value - multiple labels",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key: &key,
 				},
-				Label{
+				{
 					Key: &key2,
 				},
 			},
@@ -879,8 +879,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - single label with value from local env",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv,
 				},
@@ -889,16 +889,16 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - multiple labels with values from Local env",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv2,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv3,
 				},
@@ -907,20 +907,20 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"incorrect entry - multiple labels with values from Local env",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnvIncorrect,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnvIncorrect2,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnvIncorrect3,
 				},
@@ -929,8 +929,8 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - good environment variable value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv4,
 				},
@@ -938,8 +938,8 @@ func Test_validateLabels(t *testing.T) {
 			0,
 		}, {
 			"incorrect entry - bad environment variable value",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnvIncorrect4,
 				},
@@ -948,28 +948,28 @@ func Test_validateLabels(t *testing.T) {
 		},
 		{
 			"correct entry - all combinations",
-			Labels{
-				Label{
+			[]Label{
+				{
 					Key:   &key,
 					Value: &value,
 				},
-				Label{
+				{
 					Key:   &key2,
 					Value: &value2,
 				},
-				Label{
+				{
 					Key:   &key3,
 					Value: &value3,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv2,
 				},
-				Label{
+				{
 					Key:   &key,
 					Value: &valueLocalEnv3,
 				},
diff --git a/docker/credentials_helper_test.go b/docker/credentials_helper_test.go
index 5e60f074..0c536f39 100644
--- a/docker/credentials_helper_test.go
+++ b/docker/credentials_helper_test.go
@@ -1,3 +1,6 @@
+//go:build !integration
+// +build !integration
+
 package docker
 
 import "testing"
diff --git a/docker/docker_client_linux_test.go b/docker/docker_client_linux_test.go
new file mode 100644
index 00000000..361fd08e
--- /dev/null
+++ b/docker/docker_client_linux_test.go
@@ -0,0 +1,114 @@
+package docker_test
+
+import (
+	"context"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+
+	"knative.dev/kn-plugin-func/docker"
+)
+
+// Test that we are starting podman service on behalf of user
+// if docker daemon is not present.
+func TestNewDockerClientWithAutomaticPodman(t *testing.T) {
+
+	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
+	defer cancel()
+
+	defer withMockedPodmanBinary(t)()
+	defer withEnvVar(t, "DOCKER_HOST", "")()
+
+	dockerClient, _, err := docker.NewClient("unix:///var/run/nonexistent.sock")
+	if err != nil {
+		t.Error(err)
+	}
+	defer dockerClient.Close()
+
+	_, err = dockerClient.Ping(ctx)
+	if err != nil {
+		t.Error(err)
+	}
+
+}
+
+const helperGoScriptContent = `package main
+
+import (
+	"fmt"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+)
+
+func main() {
+	dockerHost := os.Args[3]
+	u, err := url.Parse(dockerHost)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+	sock := u.Path
+
+	server := http.Server{}
+	listener, err := net.Listen("unix", sock)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+	// mimics /_ping endpoint
+	server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
+		writer.Header().Add("Content-Type", "text/plain")
+		writer.WriteHeader(200)
+		writer.Write([]byte("OK"))
+	})
+	server.Serve(listener)
+}
+`
+
+func withMockedPodmanBinary(t *testing.T) func() {
+	var err error
+	binDir := t.TempDir()
+
+	newPath := binDir + string(os.PathListSeparator) + os.Getenv("PATH")
+	cleanUpPath := withEnvVar(t, "PATH", newPath)
+
+	helperGoScriptPath := filepath.Join(binDir, "main.go")
+
+	err = ioutil.WriteFile(helperGoScriptPath,
+		[]byte(helperGoScriptContent),
+		0400)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	runnerScriptName := "podman"
+	if runtime.GOOS == "windows" {
+		runnerScriptName = runnerScriptName + ".bat"
+	}
+
+	runnerScriptContent := `#!/bin/sh
+exec go run GO_SCRIPT_PATH $@;
+`
+	if runtime.GOOS == "windows" {
+		runnerScriptContent = `@echo off
+go.exe run GO_SCRIPT_PATH %*
+`
+	}
+
+	runnerScriptPath := filepath.Join(binDir, runnerScriptName)
+	runnerScriptContent = strings.ReplaceAll(runnerScriptContent, "GO_SCRIPT_PATH", helperGoScriptPath)
+	err = ioutil.WriteFile(runnerScriptPath, []byte(runnerScriptContent), 0700)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return func() {
+		cleanUpPath()
+	}
+}
diff --git a/docker/docker_client_test.go b/docker/docker_client_test.go
new file mode 100644
index 00000000..4f227976
--- /dev/null
+++ b/docker/docker_client_test.go
@@ -0,0 +1,86 @@
+package docker_test
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/client"
+	"knative.dev/kn-plugin-func/docker"
+)
+
+// Test that we are creating client in accordance
+// with the DOCKER_HOST environment variable
+func TestNewDockerClient(t *testing.T) {
+	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
+	defer cancel()
+
+	tmpDir := t.TempDir()
+	sock := filepath.Join(tmpDir, "docker.sock")
+
+	defer startMockDaemon(t, sock)()
+
+	defer withEnvVar(t, "DOCKER_HOST", fmt.Sprintf("unix://%s", sock))()
+
+	dockerClient, _, err := docker.NewClient(client.DefaultDockerHost)
+	if err != nil {
+		t.Error(err)
+	}
+	defer dockerClient.Close()
+
+	_, err = dockerClient.Ping(ctx)
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func withEnvVar(t *testing.T, name, value string) func() {
+	oldDh, hadDh := os.LookupEnv(name)
+	err := os.Setenv(name, value)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return func() {
+		if hadDh {
+			os.Setenv(name, oldDh)
+		} else {
+			os.Unsetenv(name)
+		}
+	}
+}
+
+func startMockDaemon(t *testing.T, sock string) func() {
+
+	server := http.Server{}
+	listener, err := net.Listen("unix", sock)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// mimics /_ping endpoint
+	server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
+		writer.Header().Add("Content-Type", "text/plain")
+		writer.WriteHeader(200)
+		_, _ = writer.Write([]byte("OK"))
+	})
+
+	serErrChan := make(chan error)
+	go func() {
+		serErrChan <- server.Serve(listener)
+	}()
+	return func() {
+		err := server.Shutdown(context.Background())
+		if err != nil {
+			t.Fatal(err)
+		}
+		err = <-serErrChan
+		if err != nil && !strings.Contains(err.Error(), "Server closed") {
+			t.Fatal(err)
+		}
+	}
+}
diff --git a/docker/pusher_test.go b/docker/pusher_test.go
index d1f90fbc..d3c7caa4 100644
--- a/docker/pusher_test.go
+++ b/docker/pusher_test.go
@@ -1,3 +1,6 @@
+//go:build !integration
+// +build !integration
+
 package docker
 
 import (
diff --git a/knative/deployer_test.go b/knative/deployer_test.go
index 68f184f0..43065e42 100644
--- a/knative/deployer_test.go
+++ b/knative/deployer_test.go
@@ -1,3 +1,6 @@
+//go:build !integration
+// +build !integration
+
 package knative
 
 import (
diff --git a/repository_test.go b/repository_test.go
index a55aa405..e5a55d37 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -1,3 +1,6 @@
+//go:build !integration
+// +build !integration
+
 package function_test
 
 import (