Repository: GoogleContainerTools/kpt
Pull request #2030 (2021-05-17 17:58:46)

Cli Usability: made changes in the output format of tree command
This pr makes the following changes in the tree command
* Updated the output format as mentioned in Option 2 of the discussion [#1850 ](https://github.com/GoogleContainerTools/kpt/issues/1850#issuecomment-833130968). Encloses the directory name inside the quotes if it is a directory. Adds Package as a prefix and encloses the package name inside quotes for a package. 
* tree command wouldn't accept stdin 
* Updated the unit tests according to the changes made 
* Updated the reference and book in the docs   

phanimarupaka: Please make sure you update the book https://kpt.dev/book/01-getting-started/02-quickstart with new output.
phanimarupaka: @NagaHarshita Here is the decided format.

```
$ kpt pkg tree wordpress/
Package "wordpress"
├── [Kptfile]  Kptfile wordpress
├── [service.yaml]  Service wordpress
├── deployment
│   ├── [deployment.yaml]  Deployment wordpress
│   └── [volume.yaml]  PersistentVolumeClaim wp-pv-claim
└── Package "mysql"
    ├── [Kptfile]  Kptfile mysql
    ├── [deployment.yaml]  PersistentVolumeClaim mysql-pv-claim
    ├── [deployment.yaml]  Deployment wordpress-mysql
    └── [deployment.yaml]  Service wordpress-mysql
```

Directory should not have quotes and `:`

Also update this README as well with new pattern https://github.com/GoogleContainerTools/kpt/tree/next/package-examples/wordpress

Patch:
diff --git a/Makefile b/Makefile
index 5e11bd51..a665f5f6 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,14 @@
 
 GOBIN := $(shell go env GOPATH)/bin
 
+# T refers to an e2e test case matcher. This enables running e2e tests
+# selectively.  For example,
+# To invoke e2e tests related to fnconfig, run:
+# make test-fn-render T=fnconfig
+# make test-fn-eval T=fnconfig
+# By default, make test-fn-render/test-fn-eval will run all tests.
+T ?= ".*"
+
 build:
 	go build -o $(GOBIN)/kpt -v .
 
@@ -70,17 +78,11 @@ test-docker: build
 # KPT_E2E_UPDATE_EXPECTED=true (if expected output to be updated)
 # target to run e2e tests for "kpt fn render" command
 test-fn-render: build
-	PATH=$(GOBIN):$(PATH) go test -v --tags=docker --run=TestFnRender ./e2e/
+	PATH=$(GOBIN):$(PATH) go test -v --tags=docker --run=TestFnRender/testdata/fn-render/$(T) ./e2e/
 
 # target to run e2e tests for "kpt fn eval" command
 test-fn-eval: build
-	PATH=$(GOBIN):$(PATH) go test -v --tags=docker --run=TestFnEval ./e2e/
-
-# target to flush kpt-fn cache
-flush-fn-cache:
-	for fn in set-namespace set-labels set-annotations starlark; do \
-		docker image rm gcr.io/kpt-fn/$$fn:unstable ; \
-	done
+	PATH=$(GOBIN):$(PATH) go test -v --tags=docker --run=TestFnEval/testdata/fn-eval/$(T)  ./e2e/
 
 vet:
 	go vet ./...
diff --git a/go.mod b/go.mod
index 39f62fbd..9c5b47a2 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
 	k8s.io/client-go v0.21.0
 	k8s.io/klog v1.0.0
 	k8s.io/kubectl v0.21.0
-	sigs.k8s.io/cli-utils v0.25.0
+	sigs.k8s.io/cli-utils v0.25.1-0.20210521231537-8200fe56434d
 	sigs.k8s.io/kustomize/kyaml v0.10.20-0.20210506224302-fcfdf6be5152
 )
 
@@ -29,4 +29,4 @@ require (
 replace (
 	k8s.io/cli-runtime v0.20.4 => k8s.io/cli-runtime v0.21.0
 	k8s.io/kubectl v0.20.4 => k8s.io/kubectl v0.21.0
-)
\ No newline at end of file
+)
diff --git a/go.sum b/go.sum
index 17ec20be..7cea8ca6 100644
--- a/go.sum
+++ b/go.sum
@@ -23,7 +23,6 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
@@ -41,7 +40,6 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY
 github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
 github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
 github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
 github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
 github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
 github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
@@ -77,16 +75,13 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
@@ -113,7 +108,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -129,7 +123,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@@ -146,7 +139,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -167,7 +159,6 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
 github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
 github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
 github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -232,7 +223,6 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -331,7 +321,6 @@ github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac/go.mod h1:K
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -355,7 +344,6 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
@@ -377,7 +365,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -407,9 +394,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -418,13 +403,11 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
 github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -450,23 +433,19 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -508,7 +487,6 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -543,11 +521,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -759,9 +734,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -777,7 +750,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -821,11 +793,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@@ -833,7 +803,6 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -851,7 +820,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
 gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -918,8 +886,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
-sigs.k8s.io/cli-utils v0.25.0 h1:uYoe/BZBJBUluU/PknsY9mgqB8jPvQV5ADp9VT9GS2w=
-sigs.k8s.io/cli-utils v0.25.0/go.mod h1:dllg30GE57wkWbvaA9lI1cpDwTKxb9NAENStrQ3fQxg=
+sigs.k8s.io/cli-utils v0.25.1-0.20210521231537-8200fe56434d h1:4cw9zsao9BLIVxgAecemGrEWTJm9blIKJlUc6ULeZuY=
+sigs.k8s.io/cli-utils v0.25.1-0.20210521231537-8200fe56434d/go.mod h1:dllg30GE57wkWbvaA9lI1cpDwTKxb9NAENStrQ3fQxg=
 sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
 sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
 sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
@@ -937,4 +905,4 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQk
 sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
\ No newline at end of file
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/internal/cmddestroy/cmddestroy.go b/internal/cmddestroy/cmddestroy.go
index 41481e31..84962879 100644
--- a/internal/cmddestroy/cmddestroy.go
+++ b/internal/cmddestroy/cmddestroy.go
@@ -137,8 +137,6 @@ func (r *Runner) runE(c *cobra.Command, args []string) error {
 	if r.dryRun {
 		dryRunStrategy = common.DryRunClient
 	}
-	// TODO: This is terrible. Fix it.
-	r.Destroyer.DryRunStrategy = dryRunStrategy
 
 	// TODO(mortent): Figure out if we can do this differently.
 	if r.PreProcess != nil {
@@ -160,6 +158,7 @@ func runDestroy(r *Runner, inv inventory.InventoryInfo, dryRunStrategy common.Dr
 	}
 	option := &apply.DestroyerOption{
 		InventoryPolicy: r.inventoryPolicy,
+		DryRunStrategy:  dryRunStrategy,
 	}
 	ch := r.Destroyer.Run(inv, option)
 
diff --git a/internal/cmdliveinit/cmdliveinit.go b/internal/cmdliveinit/cmdliveinit.go
index c4236130..017764f5 100644
--- a/internal/cmdliveinit/cmdliveinit.go
+++ b/internal/cmdliveinit/cmdliveinit.go
@@ -142,7 +142,7 @@ func (c *ConfigureInventoryInfo) Run(ctx context.Context) error {
 
 	// Autogenerate the name if it is not provided through the flag.
 	if c.Name == "" {
-		randomSuffix := common.RandomStr(time.Now().UTC().UnixNano())
+		randomSuffix := common.RandomStr()
 		name = fmt.Sprintf("%s-%s", defaultInventoryName, randomSuffix)
 	} else {
 		name = c.Name
diff --git a/internal/cmdrender/cmd.go b/internal/cmdrender/cmd.go
index 1220132e..20f8b7ea 100644
--- a/internal/cmdrender/cmd.go
+++ b/internal/cmdrender/cmd.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package cmdget contains the get command
+// Package cmdrender contains the render command
 package cmdrender
 
 import (
@@ -41,6 +41,8 @@ func NewRunner(ctx context.Context, parent string) *Runner {
 	}
 	c.Flags().StringVar(&r.resultsDirPath, "results-dir", "",
 		"path to a directory to save function results")
+	c.Flags().StringVar(&r.imagePullPolicy, "image-pull-policy", "always",
+		"pull image before running the container. It should be one of always, ifNotPresent and never.")
 	cmdutil.FixDocs("kpt", parent, c)
 	r.Command = c
 	return r
@@ -52,10 +54,11 @@ func NewCommand(ctx context.Context, parent string) *cobra.Command {
 
 // Runner contains the run function pipeline run command
 type Runner struct {
-	pkgPath        string
-	resultsDirPath string
-	Command        *cobra.Command
-	ctx            context.Context
+	pkgPath         string
+	resultsDirPath  string
+	imagePullPolicy string
+	Command         *cobra.Command
+	ctx             context.Context
 }
 
 func (r *Runner) preRunE(c *cobra.Command, args []string) error {
@@ -78,7 +81,7 @@ func (r *Runner) preRunE(c *cobra.Command, args []string) error {
 			return fmt.Errorf("results-dir %q check failed: %w", r.resultsDirPath, err)
 		}
 	}
-	return nil
+	return cmdutil.ValidateImagePullPolicyValue(r.imagePullPolicy)
 }
 
 func (r *Runner) runE(c *cobra.Command, _ []string) error {
@@ -87,8 +90,9 @@ func (r *Runner) runE(c *cobra.Command, _ []string) error {
 		return err
 	}
 	executor := Executor{
-		PkgPath:        r.pkgPath,
-		ResultsDirPath: r.resultsDirPath,
+		PkgPath:         r.pkgPath,
+		ResultsDirPath:  r.resultsDirPath,
+		ImagePullPolicy: cmdutil.StringToImagePullPolicy(r.imagePullPolicy),
 	}
 	return executor.Execute(r.ctx)
 }
diff --git a/internal/cmdrender/executor.go b/internal/cmdrender/executor.go
index 25c2240d..0ccff9bf 100644
--- a/internal/cmdrender/executor.go
+++ b/internal/cmdrender/executor.go
@@ -39,8 +39,9 @@ import (
 
 // Executor hydrates a given pkg.
 type Executor struct {
-	PkgPath        string
-	ResultsDirPath string
+	PkgPath         string
+	ResultsDirPath  string
+	ImagePullPolicy fnruntime.ImagePullPolicy
 
 	Printer printer.Printer
 }
@@ -58,9 +59,10 @@ func (e *Executor) Execute(ctx context.Context) error {
 
 	// initialize hydration context
 	hctx := &hydrationContext{
-		root:      root,
-		pkgs:      map[types.UniquePath]*pkgNode{},
-		fnResults: fnresult.NewResultList(),
+		root:            root,
+		pkgs:            map[types.UniquePath]*pkgNode{},
+		fnResults:       fnresult.NewResultList(),
+		imagePullPolicy: e.ImagePullPolicy,
 	}
 
 	resources, err := hydrate(ctx, root, hctx)
@@ -134,6 +136,9 @@ type hydrationContext struct {
 	// fnResults stores function results gathered
 	// during pipeline execution.
 	fnResults *fnresult.ResultList
+
+	// imagePullPolicy controls the image pulling behavior.
+	imagePullPolicy fnruntime.ImagePullPolicy
 }
 
 //
@@ -310,6 +315,11 @@ func (pn *pkgNode) runPipeline(ctx context.Context, hctx *hydrationContext, inpu
 		return input, nil
 	}
 
+	// perform runtime validation for pipeline
+	if err := pn.pkg.ValidatePipeline(); err != nil {
+		return nil, err
+	}
+
 	mutatedResources, err := pn.runMutators(ctx, hctx, input)
 	if err != nil {
 		return nil, errors.E(op, pn.pkg.UniquePath, err)
@@ -380,7 +390,7 @@ func (pn *pkgNode) runValidators(ctx context.Context, hctx *hydrationContext, in
 
 	for i := range pl.Validators {
 		fn := pl.Validators[i]
-		validator, err := fnruntime.NewContainerRunner(ctx, &fn, pn.pkg.UniquePath, hctx.fnResults)
+		validator, err := fnruntime.NewContainerRunner(ctx, &fn, pn.pkg.UniquePath, hctx.fnResults, hctx.imagePullPolicy)
 		if err != nil {
 			return err
 		}
@@ -434,7 +444,7 @@ func fnChain(ctx context.Context, hctx *hydrationContext, pkgPath types.UniquePa
 	var runners []kio.Filter
 	for i := range fns {
 		fn := fns[i]
-		r, err := fnruntime.NewContainerRunner(ctx, &fn, pkgPath, hctx.fnResults)
+		r, err := fnruntime.NewContainerRunner(ctx, &fn, pkgPath, hctx.fnResults, hctx.imagePullPolicy)
 		if err != nil {
 			return nil, err
 		}
diff --git a/internal/docs/generated/fndocs/docs.go b/internal/docs/generated/fndocs/docs.go
index 2de160b9..99b27e19 100644
--- a/internal/docs/generated/fndocs/docs.go
+++ b/internal/docs/generated/fndocs/docs.go
@@ -77,6 +77,16 @@ Flags:
     Container image of the function to execute e.g. ` + "`" + `gcr.io/kpt-fn/set-namespace:v0.1` + "`" + `.
     ` + "`" + `eval` + "`" + ` executes only one function, so do not use ` + "`" + `--exec-path` + "`" + ` flag with this flag.
   
+  --image-pull-policy:
+    If the image should be pulled before rendering the package(s). It can be set
+    to one of always, ifNotPresent, never. If unspecified, always will be the
+    default.
+    If using always, kpt will ensure the function images to run are up-to-date
+    with the remote container registry. This can be useful for tags like v1.
+    If using ifNotPresent, kpt will only pull the image when it can't find it in
+    the local cache.
+    If using never, kpt will only use images from the local cache.
+  
   --include-meta-resources:
     If enabled, meta resources (i.e. ` + "`" + `Kptfile` + "`" + ` and ` + "`" + `functionConfig` + "`" + `) are included
     in the input to the function. By default it is disabled.
@@ -180,6 +190,11 @@ Args:
 
 Flags:
 
+  --image-pull-policy:
+    If the image should be pulled before rendering the package(s). It can be set
+    to one of always, ifNotPresent, never. If unspecified, always will be the
+    default.
+  
   --results-dir:
     Path to a directory to write structured results. Directory must exist.
     Structured results emitted by the functions are aggregated and saved
diff --git a/internal/docs/generated/livedocs/docs.go b/internal/docs/generated/livedocs/docs.go
index 4a831b7b..fb81188f 100644
--- a/internal/docs/generated/livedocs/docs.go
+++ b/internal/docs/generated/livedocs/docs.go
@@ -19,6 +19,14 @@ Args:
     Using '-' as the package path will cause kpt to read resources from stdin.
 
 Flags:
+  --dry-run:
+    It true, kpt will validate the resources in the package and print which
+    resources will be applied and which resources will be pruned, but no resources
+    will be changed.
+    If the --server-side flag is true, kpt will do a server-side dry-run, otherwise
+    it will be a client-side dry-run. Note that the output will differ somewhat
+    between the two alternatives. 
+  
   --field-manager:
     Identifier for the **owner** of the fields being applied. Only usable
     when --server-side flag is specified. Default value is kubectl.
@@ -92,6 +100,10 @@ Args:
     Using '-' as the package path will cause kpt to read resources from stdin.
 
 Flags:
+  --dry-run:
+    It true, kpt will print the resources that will be removed from the cluster, 
+    but no resources will be deleted.
+  
   --inventory-policy:
     Determines how to handle overlaps between the package being currently applied
     and existing resources in the cluster. The available options are:
@@ -120,40 +132,6 @@ var DestroyExamples = `
   kpt live destroy
 `
 
-var DiffShort = `Display the diff between the local package and the live cluster resources.`
-var DiffLong = `
-  kpt live diff [PKG_PATH | -]
-
-Args:
-  PKG_PATH | -:
-    Path to the local package which should be diffed against the cluster. It must
-    contain a Kptfile with inventory information. Defaults to the current working
-    directory.
-    Using '-' as the package path will cause kpt to read resources from stdin.
-
-Environment Variables:
-  KUBECTL_EXTERNAL_DIFF:
-    Commandline diffing tool ('diff; by default) that will be used to show
-    changes.
-    
-    # Use meld to show changes
-    KPT_EXTERNAL_DIFF=meld kpt live diff
-
-Exit statuses:
-  The following exit values are returned:
-  
-    * 0: No differences were found.
-    * 1: Differences were found.
-    * >1 kpt live or diff failed with an error.
-`
-var DiffExamples = `
-  # diff the config in the current directory against the live cluster resources.
-  kpt live diff
-  
-  # specify the local diff program to use.
-  export KUBECTL_EXTERNAL_DIFF=meld; kpt live diff my-dir
-`
-
 var InitShort = `Initialize a package with the information needed for inventory tracking.`
 var InitLong = `
   kpt live init [PKG_PATH] [flags]
@@ -223,71 +201,6 @@ var MigrateExamples = `
   kpt live migrate
 `
 
-var PreviewShort = `Preview the changes apply would make to the cluster`
-var PreviewLong = `
-  kpt live preview [PKG_PATH | -] [flags]
-
-Args:
-
-  PKG_PATH | -:
-    Path to the local package for which a preview of the operations of apply
-    or destroy should be displayed. It must contain a Kptfile with inventory
-    information. Defaults to the current working directory.
-    Using '-' as the package path will cause kpt to read resources from stdin.
-
-Flags:
-
-  --destroy:
-    If true, preview deletion of all resources.
-  
-  --field-manager:
-    Identifier for the **owner** of the fields being applied. Only usable
-    when --server-side flag is specified. Default value is kubectl.
-  
-  --force-conflicts:
-    Force overwrite of field conflicts during apply due to different field
-    managers. Only usable when --server-side flag is specified.
-    Default value is false (error and failure when field managers conflict).
-  
-  --install-resource-group:
-    Install the ResourceGroup CRD into the cluster if it isn't already
-    available. Default is false.
-  
-  --inventory-policy:
-    Determines how to handle overlaps between the package being currently applied
-    and existing resources in the cluster. The available options are:
-    
-      * strict: If any of the resources already exist in the cluster, but doesn't
-        belong to the current package, it is considered an error.
-      * adopt: If a resource already exist in the cluster, but belongs to a 
-        different package, it is considered an error. Resources that doesn't belong
-        to other packages are adopted into the current package.
-        
-    The default value is ` + "`" + `strict` + "`" + `.
-  
-  --output:
-    Determines the output format for the status information. Must be one of the following:
-    
-      * events: The output will be a list of the status events as they become available.
-      * json: The output will be a list of the status events as they become available,
-        each formatted as a json object.
-      * table: The output will be presented as a table that will be updated inline
-        as the status of resources become available.
-  
-    The default value is ‘events’.
-  
-  --server-side:
-    Perform the apply operation server-side rather than client-side.
-    Default value is false (client-side).
-`
-var PreviewExamples = `
-  # preview apply for the package in the current directory. 
-  kpt live preview
-
-  # preview destroy for a package in the my-dir directory.
-  kpt live preview --destroy my-dir
-`
-
 var StatusShort = `Display shows the status for the resources in the cluster`
 var StatusLong = `
   kpt live status [PKG_PATH | -] [flags]
diff --git a/internal/docs/generated/pkgdocs/docs.go b/internal/docs/generated/pkgdocs/docs.go
index cd7edf25..086ea30a 100644
--- a/internal/docs/generated/pkgdocs/docs.go
+++ b/internal/docs/generated/pkgdocs/docs.go
@@ -188,7 +188,7 @@ var InitExamples = `
 
 var TreeShort = `Display resources, files and packages in a tree structure.`
 var TreeLong = `
-  kpt pkg tree [DIR | -]
+  kpt pkg tree [DIR]
 `
 var TreeExamples = `
   # Show resources in the current directory.
diff --git a/internal/errors/resolver/container.go b/internal/errors/resolver/container.go
index 6f2dedeb..f5df0987 100644
--- a/internal/errors/resolver/container.go
+++ b/internal/errors/resolver/container.go
@@ -35,7 +35,6 @@ func (*containerImageErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 		return ResolvedResult{}, false
 	}
 	return ResolvedResult{
-		Message:  containerImageError.Error(),
-		ExitCode: 1,
+		Message: containerImageError.Error(),
 	}, true
 }
diff --git a/internal/errors/resolver/git.go b/internal/errors/resolver/git.go
index 4de5fdfd..877ceca6 100644
--- a/internal/errors/resolver/git.go
+++ b/internal/errors/resolver/git.go
@@ -105,7 +105,6 @@ func (*gitExecErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 		msg = ExecuteTemplate(genericGitExecError, tmplArgs)
 	}
 	return ResolvedResult{
-		Message:  msg,
-		ExitCode: 1,
+		Message: msg,
 	}, true
 }
diff --git a/internal/errors/resolver/live.go b/internal/errors/resolver/live.go
index 5fb46953..94b46fec 100644
--- a/internal/errors/resolver/live.go
+++ b/internal/errors/resolver/live.go
@@ -121,8 +121,7 @@ func (*liveErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 	var invExistsError *cmdliveinit.InvExistsError
 	if errors.As(err, &invExistsError) {
 		return ResolvedResult{
-			Message:  ExecuteTemplate(invInfoAlreadyExistsMsg, tmplArgs),
-			ExitCode: 1,
+			Message: ExecuteTemplate(invInfoAlreadyExistsMsg, tmplArgs),
 		}, true
 	}
 
diff --git a/internal/errors/resolver/misc.go b/internal/errors/resolver/misc.go
index ef6a2810..f21573aa 100644
--- a/internal/errors/resolver/misc.go
+++ b/internal/errors/resolver/misc.go
@@ -30,7 +30,7 @@ type alreadyHandledErrorResolver struct{}
 func (*alreadyHandledErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 	kioErr := errors.UnwrapKioError(err)
 	if goerrors.Is(kioErr, errors.ErrAlreadyHandled) {
-		return ResolvedResult{ExitCode: 1}, true
+		return ResolvedResult{}, true
 	}
 	return ResolvedResult{}, false
 }
diff --git a/internal/errors/resolver/pkg.go b/internal/errors/resolver/pkg.go
index c05079be..e2f7b959 100644
--- a/internal/errors/resolver/pkg.go
+++ b/internal/errors/resolver/pkg.go
@@ -55,20 +55,17 @@ func (*pkgErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 
 		if errors.Is(kptfileError, os.ErrNotExist) {
 			return ResolvedResult{
-				Message:  ExecuteTemplate(noKptfileMsg, tmplArgs),
-				ExitCode: 1,
+				Message: ExecuteTemplate(noKptfileMsg, tmplArgs),
 			}, true
 		}
 
 		return ResolvedResult{
-			Message:  ExecuteTemplate(kptfileReadErrMsg, tmplArgs),
-			ExitCode: 1,
+			Message: ExecuteTemplate(kptfileReadErrMsg, tmplArgs),
 		}, true
 	}
 	if errors.As(err, &validateError) {
 		return ResolvedResult{
-			Message:  validateError.Error(),
-			ExitCode: 1,
+			Message: validateError.Error(),
 		}, true
 	}
 
diff --git a/internal/errors/resolver/resolver.go b/internal/errors/resolver/resolver.go
index 460ac96e..4396e703 100644
--- a/internal/errors/resolver/resolver.go
+++ b/internal/errors/resolver/resolver.go
@@ -28,9 +28,14 @@ func AddErrorResolver(er ErrorResolver) {
 // the error could not be resolved.
 func ResolveError(err error) (ResolvedResult, bool) {
 	for _, resolver := range errorResolvers {
-		msg, found := resolver.Resolve(err)
+		rr, found := resolver.Resolve(err)
+		// If the exit code hasn't been set, we default it to 1. We should
+		// never return exit code 0 for errors.
+		if rr.ExitCode == 0 {
+			rr.ExitCode = 1
+		}
 		if found {
-			return msg, true
+			return rr, true
 		}
 	}
 	return ResolvedResult{}, false
diff --git a/internal/errors/resolver/update.go b/internal/errors/resolver/update.go
index 84efb11b..f5f17374 100644
--- a/internal/errors/resolver/update.go
+++ b/internal/errors/resolver/update.go
@@ -58,8 +58,7 @@ func (*updateErrorResolver) Resolve(err error) (ResolvedResult, bool) {
 
 	if msg != "" {
 		return ResolvedResult{
-			Message:  msg,
-			ExitCode: 1,
+			Message: msg,
 		}, true
 	}
 	return ResolvedResult{}, false
diff --git a/internal/fnruntime/container.go b/internal/fnruntime/container.go
index 7a7daeae..e7037449 100644
--- a/internal/fnruntime/container.go
+++ b/internal/fnruntime/container.go
@@ -37,8 +37,14 @@ const (
 	networkNameHost containerNetworkName = "host"
 	defaultTimeout  time.Duration        = 5 * time.Minute
 	dockerBin       string               = "docker"
+
+	AlwaysPull       ImagePullPolicy = "always"
+	IfNotPresentPull ImagePullPolicy = "ifNotPresent"
+	NeverPull        ImagePullPolicy = "never"
 )
 
+type ImagePullPolicy string
+
 // ContainerFnPermission contains the permission of container
 // function such as network access.
 type ContainerFnPermission struct {
@@ -53,6 +59,8 @@ type ContainerFn struct {
 	Path types.UniquePath
 	// Image is the container image to run
 	Image string
+	// ImagePullPolicy controls the image pulling behavior.
+	ImagePullPolicy ImagePullPolicy
 	// Container function will be killed after this timeour.
 	// The default value is 5 minutes.
 	Timeout time.Duration
@@ -119,6 +127,9 @@ func (f *ContainerFn) getDockerCmd() (*exec.Cmd, context.CancelFunc) {
 		"--user", uidgid,
 		"--security-opt=no-new-privileges",
 	}
+	if f.ImagePullPolicy == NeverPull {
+		args = append(args, "--pull", "never")
+	}
 	for _, storageMount := range f.StorageMounts {
 		args = append(args, "--mount", storageMount.String())
 	}
@@ -157,21 +168,35 @@ func NewContainerEnvFromStringSlice(envStr []string) *runtimeutil.ContainerEnv {
 // prepareImage will check local images and pull it if it doesn't
 // exist.
 func (f *ContainerFn) prepareImage() error {
+	// If ImagePullPolicy is set to "never", we don't need to do anything here.
+	if f.ImagePullPolicy == NeverPull {
+		return nil
+	}
+
 	// check image existence
-	args := []string{"image", "ls", f.Image}
+	foundImageInLocalCache := false
+	args := []string{"image", "inspect", f.Image}
 	cmd := exec.Command(dockerBin, args...)
 	var output []byte
 	var err error
-	if output, err = cmd.CombinedOutput(); err != nil {
-		return &ContainerImageError{
-			Image:  f.Image,
-			Output: string(output),
-		}
-	}
-	if strings.Contains(string(output), strings.Split(f.Image, ":")[0]) {
+	if _, err = cmd.CombinedOutput(); err == nil {
 		// image exists locally
+		foundImageInLocalCache = true
+	}
+
+	// If ImagePullPolicy is set to "ifNotPresent", we scan the local images
+	// first. If there is a match, we just return. This can be useful for local
+	// development to prevent the remote image to accidentally override the
+	// local image when they use the same name and tag.
+	if f.ImagePullPolicy == IfNotPresentPull && foundImageInLocalCache {
 		return nil
 	}
+
+	// If ImagePullPolicy is set to always (which is the default), we will try
+	// to pull the image regardless if the tag has been seen in the local cache.
+	// This can help to ensure we have the latest release for "moving tags" like
+	// v1 and v1.2. The performance cost is very minimal, since `docker pull`
+	// checks the SHA first and only pull the missing docker layer(s).
 	args = []string{"image", "pull", f.Image}
 	// setup timeout
 	timeout := defaultTimeout
@@ -181,7 +206,8 @@ func (f *ContainerFn) prepareImage() error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 	cmd = exec.CommandContext(ctx, dockerBin, args...)
-	if output, err := cmd.CombinedOutput(); err != nil {
+	output, err = cmd.CombinedOutput()
+	if err != nil {
 		return &ContainerImageError{
 			Image:  f.Image,
 			Output: string(output),
@@ -198,5 +224,7 @@ type ContainerImageError struct {
 }
 
 func (e *ContainerImageError) Error() string {
-	return fmt.Sprintf("Function image %q doesn't exist.", e.Image)
+	return fmt.Sprintf(
+		"Function image %q doesn't exist remotely. If you are developing new functions locally, you can choose to set the image pull policy to ifNotPresent or never.\n%v",
+		e.Image, e.Output)
 }
diff --git a/internal/fnruntime/runner.go b/internal/fnruntime/runner.go
index 86623d78..53a903bb 100644
--- a/internal/fnruntime/runner.go
+++ b/internal/fnruntime/runner.go
@@ -29,7 +29,6 @@ import (
 	"github.com/GoogleContainerTools/kpt/internal/types"
 	fnresult "github.com/GoogleContainerTools/kpt/pkg/api/fnresult/v1alpha2"
 	kptfilev1alpha2 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1alpha2"
-	"sigs.k8s.io/kustomize/kyaml/fn/framework"
 	"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
 	"sigs.k8s.io/kustomize/kyaml/kio"
 	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
@@ -38,15 +37,19 @@ import (
 
 // NewContainerRunner returns a kio.Filter given a specification of a container function
 // and it's config.
-func NewContainerRunner(ctx context.Context, f *kptfilev1alpha2.Function, pkgPath types.UniquePath, fnResults *fnresult.ResultList) (kio.Filter, error) {
+func NewContainerRunner(
+	ctx context.Context, f *kptfilev1alpha2.Function,
+	pkgPath types.UniquePath, fnResults *fnresult.ResultList,
+	imagePullPolicy ImagePullPolicy) (kio.Filter, error) {
 	config, err := newFnConfig(f, pkgPath)
 	if err != nil {
 		return nil, err
 	}
 	cfn := &ContainerFn{
-		Path:  pkgPath,
-		Image: f.Image,
-		Ctx:   ctx,
+		Path:            pkgPath,
+		Image:           f.Image,
+		ImagePullPolicy: imagePullPolicy,
+		Ctx:             ctx,
 	}
 	fltr := &runtimeutil.FunctionFilter{
 		Run:            cfn.Run,
@@ -174,6 +177,54 @@ func parseStructuredResult(yml *yaml.RNode, fnResult *fnresult.Result) error {
 	if err != nil {
 		return err
 	}
+
+	return parseNameAndNamespace(yml, fnResult)
+}
+
+// parseNameAndNamespace populates name and namespace in fnResult.Result if a
+// function (e.g. using kyaml Go SDKs) gives results in a schema
+// that puts a resourceRef's name and namespace under a metadata field
+// TODO: fix upstream (https://github.com/GoogleContainerTools/kpt/issues/2091)
+func parseNameAndNamespace(yml *yaml.RNode, fnResult *fnresult.Result) error {
+	items, err := yml.Elements()
+	if err != nil {
+		return err
+	}
+
+	for i := range items {
+		if err := populateResourceRef(items[i], &fnResult.Results[i]); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func populateResourceRef(item *yaml.RNode, resultItem *fnresult.ResultItem) error {
+	r, err := item.Pipe(yaml.Lookup("resourceRef", "metadata"))
+	if err != nil {
+		return err
+	}
+	if r == nil {
+		return nil
+	}
+	nameNode, err := r.Pipe(yaml.Lookup("name"))
+	if err != nil {
+		return err
+	}
+	namespaceNode, err := r.Pipe(yaml.Lookup("namespace"))
+	if err != nil {
+		return err
+	}
+	if nameNode != nil {
+		resultItem.ResourceRef.Name = strings.TrimSpace(nameNode.MustString())
+	}
+	if namespaceNode != nil {
+		namespace := strings.TrimSpace(namespaceNode.MustString())
+		if namespace != "" && namespace != "''" {
+			resultItem.ResourceRef.Namespace = strings.TrimSpace(namespace)
+		}
+	}
 	return nil
 }
 
@@ -300,7 +351,7 @@ func (ri *multiLineFormatter) String() string {
 }
 
 // resultToString converts given structured result item to string format.
-func resultToString(result framework.ResultItem) string {
+func resultToString(result fnresult.ResultItem) string {
 	// TODO: Go SDK should implement Stringer method
 	// for framework.ResultItem. This is a temporary
 	// wrapper that will eventually be moved to Go SDK.
@@ -333,7 +384,7 @@ func resultToString(result framework.ResultItem) string {
 	return s.String()
 }
 
-func resourceRefToString(ref yaml.ResourceMeta) string {
+func resourceRefToString(ref yaml.ResourceIdentifier) string {
 	s := strings.Builder{}
 	if ref.APIVersion != "" {
 		s.WriteString(fmt.Sprintf("%s/", ref.APIVersion))
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index 24d1783b..456fb3f2 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -385,6 +385,54 @@ func (p *Pkg) LocalResources(includeMetaResources bool) (resources []*yaml.RNode
 	return resources, err
 }
 
+// Validates the package pipeline.
+func (p *Pkg) ValidatePipeline() error {
+	pl, err := p.Pipeline()
+	if err != nil {
+		return err
+	}
+
+	if pl.IsEmpty() {
+		return nil
+	}
+
+	// read all resources including function pipeline.
+	resources, err := p.LocalResources(true)
+	if err != nil {
+		return err
+	}
+
+	resourcesByPath := sets.String{}
+
+	for _, r := range resources {
+		rPath, _, err := kioutil.GetFileAnnotations(r)
+		if err != nil {
+			return fmt.Errorf("resource missing path annotation err: %w", err)
+		}
+		resourcesByPath.Insert(filepath.Clean(rPath))
+	}
+
+	for i, fn := range pl.Mutators {
+		if fn.ConfigPath != "" && !resourcesByPath.Has(filepath.Clean(fn.ConfigPath)) {
+			return &kptfilev1alpha2.ValidateError{
+				Field:  fmt.Sprintf("pipeline.%s[%d].configPath", "mutators", i),
+				Value:  fn.ConfigPath,
+				Reason: "functionConfig must exist in the current package",
+			}
+		}
+	}
+	for i, fn := range pl.Validators {
+		if fn.ConfigPath != "" && !resourcesByPath.Has(filepath.Clean(fn.ConfigPath)) {
+			return &kptfilev1alpha2.ValidateError{
+				Field:  fmt.Sprintf("pipeline.%s[%d].configPath", "validators", i),
+				Value:  fn.ConfigPath,
+				Reason: "functionConfig must exist in the current package",
+			}
+		}
+	}
+	return nil
+}
+
 // filterMetaResources filters kpt metadata files such as Kptfile, function configs.
 func filterMetaResources(input []*yaml.RNode, pl *kptfilev1alpha2.Pipeline) (output []*yaml.RNode, err error) {
 	pathsToExclude := fnConfigFilePaths(pl)
diff --git a/internal/util/cmdutil/cmdutil.go b/internal/util/cmdutil/cmdutil.go
index 721eb2be..aedb57d0 100644
--- a/internal/util/cmdutil/cmdutil.go
+++ b/internal/util/cmdutil/cmdutil.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/GoogleContainerTools/kpt/internal/errors"
+	"github.com/GoogleContainerTools/kpt/internal/fnruntime"
 	"github.com/spf13/cobra"
 )
 
@@ -94,3 +95,21 @@ To install docker, follow the instructions at https://docs.docker.com/get-docker
 	}
 	return nil
 }
+
+func ValidateImagePullPolicyValue(v string) error {
+	if v != string(fnruntime.AlwaysPull) && v != string(fnruntime.IfNotPresentPull) && v != string(fnruntime.NeverPull) {
+		return fmt.Errorf("image pull policy must be one of %s, %s and %s", fnruntime.AlwaysPull, fnruntime.IfNotPresentPull, fnruntime.NeverPull)
+	}
+	return nil
+}
+
+func StringToImagePullPolicy(v string) fnruntime.ImagePullPolicy {
+	switch v {
+	case string(fnruntime.NeverPull):
+		return fnruntime.NeverPull
+	case string(fnruntime.IfNotPresentPull):
+		return fnruntime.IfNotPresentPull
+	default:
+		return fnruntime.AlwaysPull
+	}
+}
diff --git a/package-examples/helloworld-kustomize/README.md b/package-examples/helloworld-kustomize/README.md
index d3635410..3b0a8fc7 100644
--- a/package-examples/helloworld-kustomize/README.md
+++ b/package-examples/helloworld-kustomize/README.md
@@ -28,7 +28,7 @@ List the package contents in a tree structure.
 ```shell
 $ kpt pkg tree helloworld-kustomize/
 
-PKG: helloworld-kustomize
+Package "helloworld-kustomize"
 ├── [Kptfile]  Kptfile helloworld-kustomize
 ├── patches
 │   └── [patch.yaml]  Deployment helloworld-gke
diff --git a/package-examples/helloworld-set/README.md b/package-examples/helloworld-set/README.md
index 927f01bd..f2bcbf3d 100644
--- a/package-examples/helloworld-set/README.md
+++ b/package-examples/helloworld-set/README.md
@@ -28,7 +28,7 @@ List the package contents in a tree structure.
 ```shell
 $ kpt pkg tree helloworld-set/
 
-PKG: helloworld-set
+Package "helloworld-set"
 ├── [Kptfile]  Kptfile helloworld-set
 ├── [deploy.yaml]  Deployment helloworld-gke
 └── [service.yaml]  Service helloworld-gke
diff --git a/package-examples/helloworld-tshirt/README.md b/package-examples/helloworld-tshirt/README.md
index 588d40fb..5012ad6f 100644
--- a/package-examples/helloworld-tshirt/README.md
+++ b/package-examples/helloworld-tshirt/README.md
@@ -30,7 +30,7 @@ List the package contents in a tree structure.
 ```shell
 $ kpt pkg tree helloworld-tshirt/
 
-PKG: helloworld-tshirt
+Package "helloworld-tshirt"
 ├── [Kptfile]  Kptfile helloworld-tshirt
 ├── [helloworld.yaml]  Deployment helloworld-gke
 └── [helloworld.yaml]  Service helloworld-gke
diff --git a/package-examples/helloworld/README.md b/package-examples/helloworld/README.md
index 5b84376f..f94bfd1f 100644
--- a/package-examples/helloworld/README.md
+++ b/package-examples/helloworld/README.md
@@ -26,7 +26,7 @@ List the package contents in a tree structure.
 ```shell
 $ kpt pkg tree helloworld/
 
-PKG: helloworld
+Package "helloworld"
 ├── [Kptfile]  Kptfile helloworld
 ├── [deploy.yaml]  Deployment helloworld-gke
 └── [service.yaml]  Service helloworld-gke
diff --git a/package-examples/nginx/Kptfile b/package-examples/nginx/Kptfile
index dd891375..3cb9d11c 100644
--- a/package-examples/nginx/Kptfile
+++ b/package-examples/nginx/Kptfile
@@ -9,3 +9,5 @@ info:
 pipeline:
   validators:
     - image: gcr.io/kpt-fn/kubeval:v0.1
+      configMap:
+        strict: 'true'
diff --git a/package-examples/pipeline-validate/README.md b/package-examples/pipeline-validate/README.md
index f817a1db..08cdcb22 100644
--- a/package-examples/pipeline-validate/README.md
+++ b/package-examples/pipeline-validate/README.md
@@ -38,7 +38,7 @@ List the package contents in a tree structure.
 ```shell
 $ kpt pkg tree pipeline-validate/
 
-PKG: pipeline-validate
+Package "pipeline-validate"
 ├── [Kptfile]  Kptfile kpt-pipeline-validate-example
 └── resources
     ├── [resources.yaml]  Namespace development
diff --git a/package-examples/wordpress/README.md b/package-examples/wordpress/README.md
index 7075eb8a..db42048a 100644
--- a/package-examples/wordpress/README.md
+++ b/package-examples/wordpress/README.md
@@ -30,13 +30,13 @@ List the package contents in a tree structure.
 ```sh
 $ kpt pkg tree wordpress/
 
-PKG: wordpress
+Package "wordpress"
 ├── [Kptfile]  Kptfile wordpress
 ├── [service.yaml]  Service wordpress
 ├── deployment
 │   ├── [deployment.yaml]  Deployment wordpress
 │   └── [volume.yaml]  PersistentVolumeClaim wp-pv-claim
-└── PKG: mysql
+└── Package "mysql"
   ├── [Kptfile]  Kptfile mysql
   ├── [deployment.yaml]  PersistentVolumeClaim mysql-pv-claim
   ├── [deployment.yaml]  Deployment wordpress-mysql
diff --git a/pkg/api/fnresult/v1alpha2/types.go b/pkg/api/fnresult/v1alpha2/types.go
index cc761ca1..8e521bda 100644
--- a/pkg/api/fnresult/v1alpha2/types.go
+++ b/pkg/api/fnresult/v1alpha2/types.go
@@ -36,7 +36,7 @@ type Result struct {
 	// ExitCode is the exit code from running the function
 	ExitCode int `yaml:"exitCode"`
 	// Results is the list of results for the function
-	Results []framework.ResultItem `yaml:"results,omitempty"`
+	Results []ResultItem `yaml:"results,omitempty"`
 }
 
 const (
@@ -73,3 +73,24 @@ func NewResultList() *ResultList {
 		Items: []Result{},
 	}
 }
+
+// ResultItem is a duplicate of framework.ResultItem, except that
+// ResourceRef uses yaml.ResourceIdentifier here, whereas framework.ResultItem
+// uses yaml.ResourceMeta. Eventually, we will need to fix it upstream.
+// TODO: https://github.com/GoogleContainerTools/kpt/issues/2091
+type ResultItem struct {
+	// Message is a human readable message
+	Message string `yaml:"message,omitempty"`
+
+	// Severity is the severity of this result
+	Severity framework.Severity `yaml:"severity,omitempty"`
+
+	// ResourceRef is a reference to a resource
+	ResourceRef yaml.ResourceIdentifier `yaml:"resourceRef,omitempty"`
+
+	// Field is a reference to the field in a resource this result refers to
+	Field framework.Field `yaml:"field,omitempty"`
+
+	// File references a file containing the resource this result refers to
+	File framework.File `yaml:"file,omitempty"`
+}
diff --git a/pkg/api/kptfile/v1alpha2/validation.go b/pkg/api/kptfile/v1alpha2/validation.go
index 93db51de..d629ae66 100644
--- a/pkg/api/kptfile/v1alpha2/validation.go
+++ b/pkg/api/kptfile/v1alpha2/validation.go
@@ -16,17 +16,13 @@ package v1alpha2
 
 import (
 	"fmt"
-	"path"
+	"path/filepath"
 	"regexp"
 	"strings"
 
 	"sigs.k8s.io/kustomize/kyaml/yaml"
 )
 
-const (
-	sourceAllSubPkgs string = "./*"
-)
-
 func (kf *KptFile) Validate() error {
 	if err := kf.Pipeline.validate(); err != nil {
 		return fmt.Errorf("invalid pipeline: %w", err)
@@ -71,7 +67,7 @@ func (f *Function) validate(fnType string, idx int) error {
 
 	var configFields []string
 	if f.ConfigPath != "" {
-		if err := validatePath(f.ConfigPath); err != nil {
+		if err := validateFnConfigPath(f.ConfigPath); err != nil {
 			return &ValidateError{
 				Field:  fmt.Sprintf("pipeline.%s[%d].configPath", fnType, idx),
 				Value:  f.ConfigPath,
@@ -147,23 +143,21 @@ func IsNodeZero(n *yaml.Node) bool {
 		n.Line == 0 && n.Column == 0
 }
 
-// validatePath validates input path and return an error if it's invalid
-func validatePath(p string) error {
-	if path.IsAbs(p) {
-		return fmt.Errorf("path is not relative")
-	}
+// validateFnConfigPath validates syntactic correctness of given functionConfig path
+// and return an error if it's invalid.
+func validateFnConfigPath(p string) error {
 	if strings.TrimSpace(p) == "" {
-		return fmt.Errorf("path cannot have only white spaces")
+		return fmt.Errorf("path must not be empty")
 	}
-	if p != sourceAllSubPkgs && strings.Contains(p, "*") {
-		return fmt.Errorf("path contains asterisk, asterisk is only allowed in './*'")
+	p = filepath.Clean(p)
+	if filepath.IsAbs(p) {
+		return fmt.Errorf("path must be relative")
 	}
-	// backslash (\\), alert bell (\a), backspace (\b), form feed (\f), vertical tab(\v) are
-	// unlikely to be in a valid path
-	for _, c := range "\\\a\b\f\v" {
-		if strings.Contains(p, string(c)) {
-			return fmt.Errorf("path cannot have character %q", c)
-		}
+	if strings.Contains(p, "..") {
+		// fn config must not live outside the package directory
+		// Allowing outside path opens up an attack vector that allows
+		// reading any YAML file on package consumer's machine.
+		return fmt.Errorf("path must not be outside the package")
 	}
 	return nil
 }
diff --git a/pkg/live/apply-crd-task.go b/pkg/live/apply-crd-task.go
index 856cfa70..bc1ccc3e 100644
--- a/pkg/live/apply-crd-task.go
+++ b/pkg/live/apply-crd-task.go
@@ -19,7 +19,9 @@ import (
 	"k8s.io/cli-runtime/pkg/resource"
 	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 	"k8s.io/kubectl/pkg/util"
+	"sigs.k8s.io/cli-utils/pkg/apply/event"
 	"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
+	"sigs.k8s.io/cli-utils/pkg/object"
 )
 
 // ApplyCRDTask encapsulates information necessary to apply a
@@ -30,6 +32,18 @@ type ApplyCRDTask struct {
 	crd     *unstructured.Unstructured
 }
 
+func (a *ApplyCRDTask) Name() string {
+	return "apply-rg-crd"
+}
+
+func (a *ApplyCRDTask) Action() event.ResourceAction {
+	return event.ApplyAction
+}
+
+func (a *ApplyCRDTask) Identifiers() []object.ObjMetadata {
+	return object.UnstructuredsToObjMetas([]*unstructured.Unstructured{a.crd})
+}
+
 // NewApplyCRDTask returns a pointer to an ApplyCRDTask struct,
 // containing fields to run the task.
 func NewApplyCRDTask(factory cmdutil.Factory, crd *unstructured.Unstructured) *ApplyCRDTask {
diff --git a/pkg/live/inventoryrg.go b/pkg/live/inventoryrg.go
index f3207322..ac811a7e 100644
--- a/pkg/live/inventoryrg.go
+++ b/pkg/live/inventoryrg.go
@@ -27,7 +27,6 @@ import (
 	"k8s.io/klog"
 	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 	"sigs.k8s.io/cli-utils/pkg/apply/event"
-	"sigs.k8s.io/cli-utils/pkg/apply/task"
 	"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
 	"sigs.k8s.io/cli-utils/pkg/common"
 	"sigs.k8s.io/cli-utils/pkg/inventory"
@@ -270,13 +269,8 @@ func InstallResourceGroupCRD(factory cmdutil.Factory) error {
 		// Create the tasks to apply the ResourceGroup CRD.
 		tasks := []taskrunner.Task{
 			applyRGTask,
-			taskrunner.NewWaitTask(
-				objs,
-				taskrunner.AllCurrent,
-				applyCRDTimeout),
-			&task.ResetRESTMapperTask{
-				Mapper: mapper,
-			},
+			taskrunner.NewWaitTask("wait-rg-crd", objs, taskrunner.AllCurrent,
+				applyCRDTimeout, mapper),
 		}
 		// Create the task queue channel, and send tasks in order into the channel.
 		taskQueue := make(chan taskrunner.Task, len(tasks))
diff --git a/site/book/01-getting-started/02-quickstart.md b/site/book/01-getting-started/02-quickstart.md
index 453c3281..f543025f 100644
--- a/site/book/01-getting-started/02-quickstart.md
+++ b/site/book/01-getting-started/02-quickstart.md
@@ -19,7 +19,7 @@ Next, let's quickly view the content of the package:
 
 ```shell
 $ kpt pkg tree
-PKG: nginx
+Package "nginx"
 ├── [Kptfile]  Kptfile nginx
 ├── [deployment.yaml]  Deployment my-nginx
 └── [svc.yaml]  Service my-nginx-svc
diff --git a/site/book/02-concepts/01-packages.md b/site/book/02-concepts/01-packages.md
index d2d321e4..14cb7c1a 100644
--- a/site/book/02-concepts/01-packages.md
+++ b/site/book/02-concepts/01-packages.md
@@ -13,13 +13,13 @@ Let's take a look at an example:
 ```shell
 $ kpt pkg get https://github.com/GoogleContainerTools/kpt.git/package-examples/wordpress@v0.3
 $ kpt pkg tree wordpress/
-PKG: wordpress
+Package "wordpress"
 ├── [Kptfile]  Kptfile wordpress
 ├── [service.yaml]  Service wordpress
 ├── deployment
 │   ├── [deployment.yaml]  Deployment wordpress
 │   └── [volume.yaml]  PersistentVolumeClaim wp-pv-claim
-└── PKG: mysql
+└── Package "mysql"
     ├── [Kptfile]  Kptfile mysql
     ├── [deployment.yaml]  PersistentVolumeClaim mysql-pv-claim
     ├── [deployment.yaml]  Deployment wordpress-mysql
diff --git a/site/book/03-packages/02-exploring-a-package.md b/site/book/03-packages/02-exploring-a-package.md
index df097734..5d41485d 100644
--- a/site/book/03-packages/02-exploring-a-package.md
+++ b/site/book/03-packages/02-exploring-a-package.md
@@ -8,13 +8,13 @@ constituent packages, files, and resources:
 
 ```shell
 $ kpt pkg tree wordpress/
-PKG: wordpress
+Package "wordpress"
 ├── [Kptfile]  Kptfile wordpress
 ├── [service.yaml]  Service wordpress
 ├── deployment
 │   ├── [deployment.yaml]  Deployment wordpress
 │   └── [volume.yaml]  PersistentVolumeClaim wp-pv-claim
-└── PKG: mysql
+└── Package "mysql"
     ├── [Kptfile]  Kptfile mysql
     ├── [deployment.yaml]  PersistentVolumeClaim mysql-pv-claim
     ├── [deployment.yaml]  Deployment wordpress-mysql
diff --git a/site/book/03-packages/07-composing-a-package.md b/site/book/03-packages/07-composing-a-package.md
index 9444366b..76874a8a 100644
--- a/site/book/03-packages/07-composing-a-package.md
+++ b/site/book/03-packages/07-composing-a-package.md
@@ -9,15 +9,15 @@ There are two different ways to add a subpackage to a package on the local files
 Let's revisit the `wordpress` package and see how it was composed in the first place.
 Currently, it has the following package hierarchy:
 
-```yaml
+```shell
 $ kpt pkg tree wordpress/
-PKG: wordpress
+Package "wordpress"
 ├── [Kptfile]  Kptfile wordpress
 ├── [service.yaml]  Service wordpress
 ├── deployment
 │   ├── [deployment.yaml]  Deployment wordpress
 │   └── [volume.yaml]  PersistentVolumeClaim wp-pv-claim
-└── PKG: mysql
+└── Package "mysql"
     ├── [Kptfile]  Kptfile mysql
     ├── [deployment.yaml]  PersistentVolumeClaim mysql-pv-claim
     ├── [deployment.yaml]  Deployment wordpress-mysql
diff --git a/site/book/04-using-functions/03-function-results.md b/site/book/04-using-functions/03-function-results.md
index 7a1e4f19..4f77883e 100644
--- a/site/book/04-using-functions/03-function-results.md
+++ b/site/book/04-using-functions/03-function-results.md
@@ -59,7 +59,7 @@ Package "wordpress":
 [PASS] "gcr.io/kpt-fn/set-label:v0.1"
 [FAIL] "gcr.io/kpt-fn/kubeval:v0.1"
   Results:
-    [ERROR] Invalid type. Expected: integer, given: string in object "v1/Service/" in file "service.yaml" in field "spec.ports.0.port"
+    [ERROR] Invalid type. Expected: integer, given: string in object "v1/Service/wordpress" in file "service.yaml" in field "spec.ports.0.port"
   Exit code: 1
 
 For complete results, see /tmp/results.yaml
@@ -87,6 +87,7 @@ items:
         resourceRef:
           apiVersion: v1
           kind: Service
+          name: wordpress
         field:
           path: spec.ports.0.port
         file:
diff --git a/site/installation/README.md b/site/installation/README.md
index 4c248ed4..0d2dd29b 100644
--- a/site/installation/README.md
+++ b/site/installation/README.md
@@ -1,5 +1,7 @@
 Users can get kpt in a variety of ways:
 
+**Note:** Please follow the [migration guide] to make your existing kpt packages compatible
+with kpt `v1.0+` binary.
 
 ## Binaries
 
@@ -93,5 +95,6 @@ kpt help
 [gcr.io/kpt-dev/kpt]: https://console.cloud.google.com/gcr/images/kpt-dev/GLOBAL/kpt?gcrImageListsize=30
 [gcr.io/kpt-dev/kpt-gcloud]: https://console.cloud.google.com/gcr/images/kpt-dev/GLOBAL/kpt-gcloud?gcrImageListsize=30
 [cloud-sdk]: https://github.com/GoogleCloudPlatform/cloud-sdk-docker
-[linux]: https://github.com/GoogleContainerTools/kpt/releases/download/v1.0.0-alpha.3/kpt_linux_amd64
-[darwin]: https://github.com/GoogleContainerTools/kpt/releases/download/v1.0.0-alpha.3/kpt_darwin_amd64
+[linux]: https://github.com/GoogleContainerTools/kpt/releases/download/v1.0.0-alpha.4/kpt_linux_amd64
+[darwin]: https://github.com/GoogleContainerTools/kpt/releases/download/v1.0.0-alpha.4/kpt_darwin_amd64
+[migration guide]: installation/migration/01-migration.md
diff --git a/site/installation/migration/01-migration.md b/site/installation/migration/01-migration.md
new file mode 100644
index 00000000..2e687476
--- /dev/null
+++ b/site/installation/migration/01-migration.md
@@ -0,0 +1,278 @@
+## Migration from kpt `v0.39.0` to kpt `v1.0`
+
+- [Before you begin](#Before-you-begin)
+- [What's new and changed?](#What's-new-and-changed?)
+  - [CLI changes](#CLI-changes)
+  - [Kptfile schema changes](#Kptfile-schema-changes)
+  - [`pkg`](#pkg)
+    - [`sync` merged with `update`](#sync-merged-with-update)
+  - [`cfg`](#cfg)
+    - [Changes to Setters](#Changes-to-Setters)
+    - [Setter validations deprecated](#Setter-validations-deprecated)
+    - [Auto setters deprecated](#Auto-setters-deprecated)
+  - [`fn`](#fn)
+    - [`run` is split into `eval` and `render`](#run-is-split-into-eval-and-render)
+    - [Function Config](#Function-Config)
+    - [Function Results](#Function-Results)
+  - [`live`](#live)
+- [Migration steps](#Migration-steps)
+  - [Automated portion of migration](#Automated-portion-of-migration)
+    - [Changes made by the function](#Changes-made-by-the-function)
+  - [Manual portion of migration](#Manual-portion-of-migration)
+- [Timeline](#Timeline)
+
+kpt `v1.0` is going to be the latest major release of the kpt CLI. The implementation of kpt `v1.0`
+has changed considerably from kpt `v0.39.0`. A rich set of new features have been added
+as a result of the customers’ input and requirements. Some features have been
+deprecated or refactored in ways that make them incompatible with kpt `v0.39.0`.
+Since these are backwards incompatible changes, there should be a way for customers
+to migrate/fix their existing kpt packages which are compatible with `v0.39.0` version of kpt,
+to become compatible with kpt `v1.0`. This document outlines the end to end migration
+journey of customers using a comprehensive kpt package example.
+
+## Before you begin
+
+Please go through [installation instructions] for installing `v1.0` binary and
+at least 1st and 2nd chapters of [kpt book] for understanding the basic model of kpt `v1.0`
+
+## What's new and changed?
+
+### CLI changes
+
+To start with, all the commands in kpt `v1.0` will follow the consistent pattern
+
+```shell
+$ kpt <group> <command> <positional_args> [PKG_PATH | DIR | - STDIN] [flags]
+```
+
+Almost all the existing commands/features in kpt `v0.39.0` are also offered in kpt `v1.0`
+but in a better and enhanced format. In kpt `v0.39.0`, `cfg` group had been a dumping ground
+for many cli commands which don’t have a coherent story. `cfg` is often confused with
+`pkg` and vice-versa. As a major step, we removed the `cfg` subgroup and rearranged
+the functionality. Here are the one-liners for each command group in kpt `v1.0`.
+
+1. `pkg:` Commands for composing and describing packages.
+2. `fn:` Commands for running functions for validation and customization.
+3. `live:` Commands for interacting with a live cluster.
+
+`PKG_PATH vs DIR:` Most of the commands accept only `PKG_PATH` as input which means
+the input directory must have a valid Kptfile. Few commands can work on just the
+simple `DIR` with resources. Commands operate on the current working directory by default.
+
+| [v0.39.0 Commands]                                                                                | [v1.0 Commands]                                                                                                                                                                                                                                      |
+| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `kpt pkg get REPO_URI[.git]/PKG_PATH[@VERSION] LOCAL_DEST_DIRECTORY [flags]`                      | `kpt pkg get REPO_URI[.git]/PKG_PATH[@VERSION] [flags] [LOCAL_DEST_DIRECTORY]` <br> Fetch a remote package from a git subdirectory and writes it to a new local directory.                                                                           |
+| `kpt pkg init DIR [flags]`                                                                        | `kpt pkg init [DIR] [flags]` <br> Initializes an existing empty directory as a kpt package by adding a Kptfile.                                                                                                                                      |
+| `kpt pkg update LOCAL_PKG_DIR[@VERSION] [flags]`                                                  | `kpt pkg update [PKG_PATH][@version] [flags]` <br> Pulls in upstream changes and merges them into a local package.                                                                                                                                   |
+| `kpt pkg fix DIR [flags]`                                                                         | `kpt fn eval --image gcr.io/kpt-fn/fix:unstable` <br> Fix a local package which is using deprecated features.                                                                                                                                        |
+| `kpt pkg desc DIR [flags]`                                                                        | Deprecated in favor of reading Kptfile directly                                                                                                                                                                                                      |
+| `kpt pkg diff DIR[@VERSION] [flags]`                                                              | `kpt pkg diff [PKG_PATH][@version] [flags]` <br> Display differences between upstream and local packages.                                                                                                                                            |
+| `kpt cfg fmt DIR/STDIN [flags]`                                                                   | `kpt fn eval --image gcr.io/kpt-fn/format:unstable`                                                                                                                                                                                                  |
+| `kpt cfg tree DIR/STDIN [flags]`                                                                  | `kpt pkg tree [DIR] [flags]` <br> Displays resources, files and packages in a tree structure.                                                                                                                                                        |
+| `kpt cfg cat DIR/STDIN [flags]`                                                                   | Deprecated. Will be added back soon.                                                                                                                                                                                                                 |
+| `kpt fn run DIR/STDIN [flags]`                                                                    | `kpt fn eval [DIR / -] [flags]` <br> Executes a single function on resources in a directory. <br> <br> `kpt fn render [PKG_PATH]` <br> Executes the pipeline of functions on resources in the package and writes the output to the local filesystem. |
+| `kpt fn source DIR [flags]`                                                                       | `kpt fn source [DIR] [flags]` <br> Reads resources from a local directory and writes them in Function Specification wire format to stdout.                                                                                                           |
+| `kpt fn sink DIR [flags]`                                                                         | `kpt fn sink [DIR] [flags]` <br> Reads resources from stdin and writes them to a local directory.                                                                                                                                                    |
+| `kpt fn export DIR [flags]`                                                                       | Deprecated.                                                                                                                                                                                                                                          |
+| `kpt live init DIR [flags]`                                                                       | `kpt live init [PKG_PATH] [flags]` <br> Initializes the package with the name, namespace and id of the resource that will keep track of the package inventory.                                                                                       |
+| `kpt live apply DIR/STDIN [flags]`                                                                | `kpt live apply [PKG_PATH / -] [flags]` <br> Creates, updates and deletes resources in the cluster to make the remote cluster resources match the local package configuration.                                                                       |
+| `kpt live preview DIR/STDIN [flags]`                                                              | Deprecated. The functionality will be provided by `dry-run` flag in `apply` and `destroy` commands.                                                                                                                                                  |
+| `kpt live destroy DIR/STDIN [flags]`                                                              | `kpt live destroy [PKG_PATH / -] [flags]` <br> Removes all files belonging to a package from the cluster.                                                                                                                                            |
+| `kpt live status DIR/STDIN [flags]`                                                               | `kpt live status [PKG_PATH / -] [flags]` <br> Shows the resource status for resources belonging to the package.                                                                                                                                      |
+| `kpt live diff DIR/STDIN [flags]`                                                                 | Deprecated. The functionality will be provided by `dry-run` flag in `apply` and `destroy` commands.                                                                                                                                                  |
+| `kpt cfg set DIR setter_name setter_value`                                                        | `kpt fn eval --image gcr.io/kpt-fn/apply-setters:v0.1 -- 'foo=bar' 'env=[dev, stage]'`                                                                                                                                                               |
+| `kpt cfg create-setter DIR setter_name setter_value`                                              | `kpt fn eval --image gcr.io/kpt-fn/search-replace:v0.1 -- 'by-value=nginx' 'put-comment=kpt-set: ${image}'`                                                                                                                                          |
+| `kpt cfg create-subst DIR subst_name --field-value nginx:1.7.1 --pattern ${image}:${tag} [flags]` | `kpt fn eval --image gcr.io/kpt-fn/search-replace:v0.1 -- 'by-value=nginx:1.7.1' 'put-comment=kpt-set: ${image}:${tag}'`                                                                                                                             |
+| `kpt cfg delete-setter DIR setter_name`                                                           | `kpt fn eval --image gcr.io/kpt-fn/search-replace:v0.1 -- 'by-value=nginx' put-comment=''`                                                                                                                                                           |
+| `kpt cfg delete-subst DIR subst_name [flags]`                                                     | `kpt fn eval --image gcr.io/kpt-fn/search-replace:0.1 -- 'by-value=nginx:1.7.1' put-comment=''`                                                                                                                                                      |
+| `kpt cfg annotate DIR/STDIN [flags]`                                                              | `kpt fn eval --image gcr.io/kpt-fn/set-annotation:v0.1 -- 'name=foo' 'value=bar'`                                                                                                                                                                    |
+| `kpt cfg grep DIR/STDIN [flags]`                                                                  | `kpt fn eval --image gcr.io/kpt-fn/search-replace:v0.1 -- 'by-value=foo' 'by-path=bar'`                                                                                                                                                              |
+| `kpt cfg count DIR/STDIN [flags]`                                                                 | Deprecated.                                                                                                                                                                                                                                          |
+
+### Kptfile schema changes
+
+The existing [v1alpha1 Kptfile] format/schema is not compatible with kpt `v1.0`.
+Here is the schema for [v1alpha2 Kptfile] which is compatible with kpt `v1.0`.
+
+1. The `packageMetaData` section in `v1alpha1` Kptfile is transformed to `info` section in `v1alpha2` Kptfile.
+2. `upstream` section, in the `v1alpha1` Kptfile is split into `upstream` and `upstreamLock`
+   sections in `v1alpha2` Kptfile. `upstream` section can be modified by users to declare
+   the desired version of the package, `upstreamLock` is the resolved package state
+   and should not be modified by users.
+3. The `OpenAPI` section in `v1alpha1` Kptfile is deprecated. [Details below](#changes-to-setters).
+4. `dependencies` section in `v1alpha1` Kptfile is deprecated. [Details below](#sync-merged-with-update).
+5. `functions` section in `v1alpha1` Kptfile holds definitions for Starlark functions only.
+   This section is deprecated and all the functions can be declared in the `pipeline`
+   section including [Starlark functions].
+6. `inventory-object` is moved to `inventory` section in `v1alpha2` Kptfile. [Details below](#live-group-changes).
+
+### `pkg`
+
+#### `sync` merged with `update`
+
+Nothing to worry about. `sync` is just a declarative version of `update` functionality.
+The functionality is offered by `kpt pkg update` in a more user friendly fashion.
+Running the new `kpt pkg update` command on a package which has subpackages, simply
+means traversing the package hierarchy, and for each package encountered, update
+the package if needed. If you want to declaratively update nested subpackages,
+you can declare the desired version in the upstream section of the respective
+subpackage and invoking `kpt pkg update` on the root package will take care of updating
+all the nested subpackages. [Update guide]
+
+### `cfg`
+
+#### Changes to Setters
+
+`Setters` and `substitutions` no longer follow the complex and verbose OpenAPI format.
+The setters and substitutions are simplified to use new and simple setter pattern
+comments. Creating a setter is as simple as adding a line comment to the desired field.
+Users need not add any `OpenAPI` definitions. Please refer to [apply-setters] for
+the information about new syntax for setters and how to apply setter values, this is
+equivalent to the `kpt cfg set` in `v0.39.0`.
+[Setter Inheritance] works as usual in kpt `v1.0`.
+
+Here is the [simple example] of setter syntax transformation.
+
+#### Setter validations deprecated
+
+We want to keep the authoring experience of setters as simple as possible and
+hence [OpenAPI validations] and [required setters] feature offered by `v0.39.0` setters
+is no longer offered in `v1.0` version of kpt. However, we are working on providing
+an easy way to achieve similar functionality retaining the simplicity of setters,
+which is scoped for post `v1.0` release. Stay tuned.
+
+#### Auto setters deprecated
+
+[Auto-setters] feature is deprecated in `v1.0` version of kpt. Since the setters are
+migrated to a new and simple declarative version, package consumers can easily
+declare all the setter values and render them all at once.
+
+### `fn`
+
+#### `run` is split into `eval` and `render`
+
+The functionality of `kpt fn run` command in `v0.39.0` is split into two different CLI
+commands that execute functions corresponding to two fundamentally different approaches:
+
+`kpt fn render:` Executes the pipeline of functions declared in the package and its
+subpackages. This is a declarative way to run functions. [render guide]
+
+`kpt fn eval:` Executes a given function on the package. The image to run and the
+functionConfig is specified as CLI argument. This is an imperative way to run
+functions. [eval guide]
+
+#### Function Config
+
+As a result of these changes, we no longer need function configs to hold
+`config.kubernetes.io/function` annotation. Functions can be declared in the
+pipeline section of Kptfile and invoked using `kpt fn render`. The declared function
+can point to the [function config] using `configPath` argument.
+
+#### Function Results
+
+In both `render` and `eval`, structured results can be enabled using the `--results-dir` flag.
+Please refer to the [function results] section for more information on the new structure of function results.
+
+### `live`
+
+Kpt live in `v1.0` no longer uses an inventory object to track the grouping of resources
+in the cluster. Instead, it uses a more expressive `ResourceGroup` CRD. Please refer
+to the detailed guide on [migrating inventory objects] to the `ResourceGroup` equivalent.
+
+## Migration Steps
+
+Based on the changes discussed above, this section walks you through an end to end
+workflow to migrate an [example kpt package] which is compatible with `v0.39.0` version
+of kpt, and make it compatible with kpt `v1.0`.
+
+### Automated portion of migration
+
+Invoke `gcr.io/kpt-fn/fix` function from the root directory of the target kpt package.
+
+```shell
+# you must be using v1.0 version of kpt
+$ kpt fn eval --image gcr.io/kpt-fn/fix:unstable --include-meta-resources
+```
+
+#### Changes made by the function
+
+1. Best effort is made by the function to transform the `packageMetaData` section to
+   the `info` section.
+2. `upstream` section, in the `v1alpha1` Kptfile is converted to `upstream` and `upstreamLock`
+   sections in `v1alpha2` version of Kptfile.
+3. `dependencies` section is removed from Kptfile.
+4. Starlark functions section is removed from Kptfile.
+5. `Setters` and `substitutions` are converted to new and simple setter patterns. The
+   setter comments in the resources are updated to follow new comment syntax.
+6. The `apply-setters` function and all the setters are added to the mutators section
+   in the pipeline.
+7. Function annotation from function configs will be removed and corresponding function
+   definitions will be declared in `pipeline` section of Kptfile. Reference to function config
+   is added via [configPath] option.
+
+Note: This function modifies only the local package files and doesn’t make any
+changes to the resources in the live cluster.
+
+### Manual portion of migration
+
+1. All the functions are treated as `mutators` by the `gcr.io/kpt-fn/fix` function and
+   are added to the `mutators` section in the pipeline. Users must manually go through
+   the functions and move the validator functions to the `validators` section in the
+   pipeline section of `v1alpha2` Kptfile.
+   1. The order of functions also must be re-arranged manually by users if needed.
+   2. Also, note that the [function config] is used to configure the function
+      and is not included in the input list of resources to function.
+2. [OpenAPI validations] and required setters feature offered by `v0.39.0` setters is no
+   longer offered in `v1.0` version of kpt. Users must write their own validation functions
+   to achieve the functionality.
+   `Tip:` Adding a [starlark function] would be an easier alternative to achieve the
+   equivalent validation functionality.
+3. If you have used [Starlark runtime] in `v0.39.0`, please checkout the new and improved
+   [Starlark functions] and declare it accordingly in the pipeline.
+4. [Auto-setters] feature is deprecated in `v1.0` version of kpt. Since the setters are
+   migrated to a new and simple declarative version, package consumers can easily
+   declare all the setter values and render them all at once.
+5. The `gcr.io/kpt-fn/fix` function doesn’t migrate the `inventory object` to `ResourceGroup`.
+   If you are using the inventory object to manage live resources, please refer to
+   `live migrate` command docs to perform [live migration] separately.
+
+Finally, test your migrated kpt package end-to-end and make sure that the functionality
+is as expected. `gcr.io/kpt-fn/fix` is a helper for migration and doesn't guarantee
+functional parity.
+
+## Timeline
+
+1. `Right now:` You can [install] and try the pre-release version of kpt `v1.0` binary.
+2. `June 1, 2021:` `gcr.io/kpt-fn/fix` function will be released in [kpt-functions-catalog].
+   You can start migrating your existing kpt packages using the function.
+3. `July 1, 2021:` kpt `v1.0` binary will be released. The existing kpt packages are not
+   compatible with the kpt `v1.0` binary and users will be prompted to migrate/fix their packages.
+4. `TBD:` Feature releases and bug fixes for pre-v1 versions of kpt will be reserved
+   for serious security bugs only. Customers will be asked to migrate to kpt `v1.0`.
+
+[v0.39.0 commands]: https://googlecontainertools.github.io/kpt/reference/
+[v1.0 commands]: https://kpt.dev/reference/
+[v1alpha2 kptfile]: https://github.com/GoogleContainerTools/kpt/blob/next/pkg/api/kptfile/v1alpha2/types.go
+[starlark functions]: https://catalog.kpt.dev/starlark/v0.1/
+[starlark function]: https://catalog.kpt.dev/starlark/v0.1/
+[apply-setters]: https://catalog.kpt.dev/apply-setters/v0.1/
+[setter inheritance]: https://googlecontainertools.github.io/kpt/concepts/setters/#inherit-setter-values-from-parent-package
+[openapi validations]: https://googlecontainertools.github.io/kpt/guides/producer/setters/#openapi-validations
+[required setters]: https://googlecontainertools.github.io/kpt/guides/producer/setters/#required-setters
+[auto-setters]: https://googlecontainertools.github.io/kpt/concepts/setters/#auto-setters
+[migrating inventory objects]: https://googlecontainertools.github.io/kpt/reference/live/alpha/
+[live migration]: https://googlecontainertools.github.io/kpt/reference/live/alpha/
+[configpath]: https://kpt.dev/book/04-using-functions/01-declarative-function-execution?id=configpath
+[example kpt package]: https://github.com/GoogleContainerTools/kpt-functions-catalog/tree/master/testdata/fix
+[simple example]: https://github.com/GoogleContainerTools/kpt-functions-catalog/tree/master/functions/go/fix#examples
+[function config]: https://kpt.dev/book/04-using-functions/02-imperative-function-execution?id=specifying-functionconfig
+[starlark runtime]: https://googlecontainertools.github.io/kpt/guides/producer/functions/starlark/
+[update guide]: https://kpt.dev/book/03-packages/05-updating-a-package
+[render guide]: https://kpt.dev/book/04-using-functions/01-declarative-function-execution
+[eval guide]: https://kpt.dev/book/04-using-functions/02-imperative-function-execution
+[function results]: https://kpt.dev/book/04-using-functions/03-function-results
+[kpt book]: https://kpt.dev/book/
+[installation instructions]: https://kpt.dev/installation/
+[install]: https://kpt.dev/installation/
+[kpt-functions-catalog]: https://catalog.kpt.dev/
+[v1alpha1 kptfile]: https://github.com/GoogleContainerTools/kpt/blob/master/pkg/kptfile/pkgfile.go#L39
diff --git a/site/reference/fn/eval/README.md b/site/reference/fn/eval/README.md
index d5027053..e88f25b6 100644
--- a/site/reference/fn/eval/README.md
+++ b/site/reference/fn/eval/README.md
@@ -81,6 +81,16 @@ fn-args:
   Container image of the function to execute e.g. `gcr.io/kpt-fn/set-namespace:v0.1`.
   `eval` executes only one function, so do not use `--exec-path` flag with this flag.
 
+--image-pull-policy:
+  If the image should be pulled before rendering the package(s). It can be set
+  to one of always, ifNotPresent, never. If unspecified, always will be the
+  default.
+  If using always, kpt will ensure the function images to run are up-to-date
+  with the remote container registry. This can be useful for tags like v1.
+  If using ifNotPresent, kpt will only pull the image when it can't find it in
+  the local cache.
+  If using never, kpt will only use images from the local cache.
+
 --include-meta-resources:
   If enabled, meta resources (i.e. `Kptfile` and `functionConfig`) are included
   in the input to the function. By default it is disabled.
diff --git a/site/reference/fn/render/README.md b/site/reference/fn/render/README.md
index 4e072ec2..e9dcc574 100644
--- a/site/reference/fn/render/README.md
+++ b/site/reference/fn/render/README.md
@@ -46,6 +46,11 @@ PKG_PATH:
 #### Flags
 
 ```shell
+--image-pull-policy:
+  If the image should be pulled before rendering the package(s). It can be set
+  to one of always, ifNotPresent, never. If unspecified, always will be the
+  default.
+
 --results-dir:
   Path to a directory to write structured results. Directory must exist.
   Structured results emitted by the functions are aggregated and saved
diff --git a/site/reference/pkg/tree/README.md b/site/reference/pkg/tree/README.md
index 287658d6..a0757d4a 100644
--- a/site/reference/pkg/tree/README.md
+++ b/site/reference/pkg/tree/README.md
@@ -18,7 +18,7 @@ description: >
 <!--mdtogo:Long-->
 
 ```
-kpt pkg tree [DIR | -]
+kpt pkg tree [DIR]
 ```
 
 <!--mdtogo-->
@@ -28,7 +28,6 @@ kpt pkg tree [DIR | -]
 ```
 DIR:
   Path to a directory containing KRM resource(s). Defaults to the current working directory.
-  Using '-' as as the directory path will cause kpt to read resources from stdin.
 ```
 
 ### Examples
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 5930988b..13a84c31 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -8,7 +8,7 @@ This directory contains the files that are copied from 3rd-party projects and mo
   - `runfn`: KRM function runner
 - `cmdconfig`: Files copied from [cmd/config] v0.9.9 library
   - `commands`: Command files copied from [cmd/config]
-- `cli-utils`: Files copied from [cli-utils] v0.25.0 library
+- `cli-utils`: Files copied from [cli-utils] 8200fe56434dbb4fa897912524793af3c3384828 library
   - `commands`: Command files copied from [cli-utils/cmd]
 
 # Copyright and Licenses
@@ -24,4 +24,4 @@ The modifications made in the 3rd-party files may be contributed to upstream. Th
 [kyaml]: https://github.com/kubernetes-sigs/kustomize/tree/8d72528eb5c73df80b20aae0a5e584c056879387/kyaml
 [cmd/config]: https://github.com/kubernetes-sigs/kustomize/tree/b9c36caa1c5c6ee64926021841ea441773d0767c/cmd/config
 [cli-utils]: https://github.com/kubernetes-sigs/cli-utils
-[cli-utils/cmd]: https://github.com/kubernetes-sigs/cli-utils/tree/v0.25.0
+[cli-utils/cmd]: https://github.com/kubernetes-sigs/cli-utils/tree/8200fe56434dbb4fa897912524793af3c3384828
diff --git a/thirdparty/cli-utils/printers/events/formatter.go b/thirdparty/cli-utils/printers/events/formatter.go
index 736c5518..3fc3ab17 100644
--- a/thirdparty/cli-utils/printers/events/formatter.go
+++ b/thirdparty/cli-utils/printers/events/formatter.go
@@ -8,8 +8,6 @@ import (
 	"io"
 	"strings"
 
-	"k8s.io/apimachinery/pkg/api/meta"
-	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"sigs.k8s.io/cli-utils/pkg/apply/event"
@@ -29,78 +27,57 @@ type formatter struct {
 	print printFunc
 }
 
-func (ef *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats, c list.Collector) error {
-	switch ae.Type {
-	case event.ApplyEventCompleted:
-		output := fmt.Sprintf("%d resource(s) applied. %d created, %d unchanged, %d configured, %d failed",
-			as.Sum(), as.Created, as.Unchanged, as.Configured, as.Failed)
-		// Only print information about serverside apply if some of the
-		// resources actually were applied serverside.
-		if as.ServersideApplied > 0 {
-			output += fmt.Sprintf(", %d serverside applied", as.ServersideApplied)
-		}
-		ef.print(output)
-		for id, se := range c.LatestStatus() {
-			ef.printResourceStatus(id, se)
-		}
-	case event.ApplyEventResourceUpdate:
-		gk := ae.Identifier.GroupKind
-		name := ae.Identifier.Name
-		if ae.Error != nil {
-			ef.print("%s failed: %s", resourceIDToString(gk, name),
-				ae.Error.Error())
-		} else {
-			ef.print("%s %s", resourceIDToString(gk, name),
-				strings.ToLower(ae.Operation.String()))
-		}
+func (ef *formatter) FormatApplyEvent(ae event.ApplyEvent) error {
+	gk := ae.Identifier.GroupKind
+	name := ae.Identifier.Name
+	if ae.Error != nil {
+		ef.print("%s apply failed: %s", resourceIDToString(gk, name),
+			ae.Error.Error())
+	} else {
+		ef.print("%s %s", resourceIDToString(gk, name),
+			strings.ToLower(ae.Operation.String()))
 	}
 	return nil
 }
 
-func (ef *formatter) FormatStatusEvent(se event.StatusEvent, _ list.Collector) error {
-	if se.Type == event.StatusEventResourceUpdate {
-		id := se.Resource.Identifier
-		ef.printResourceStatus(id, se)
-	}
+func (ef *formatter) FormatStatusEvent(se event.StatusEvent) error {
+	id := se.Identifier
+	ef.printResourceStatus(id, se)
 	return nil
 }
 
-func (ef *formatter) FormatPruneEvent(pe event.PruneEvent, ps *list.PruneStats) error {
-	switch pe.Type {
-	case event.PruneEventCompleted:
-		ef.print("%d resource(s) pruned, %d skipped, %d failed", ps.Pruned, ps.Skipped, ps.Failed)
-	case event.PruneEventResourceUpdate:
-		gk := pe.Identifier.GroupKind
-		switch pe.Operation {
-		case event.Pruned:
-			ef.print("%s pruned", resourceIDToString(gk, pe.Identifier.Name))
-		case event.PruneSkipped:
-			ef.print("%s prune skipped", resourceIDToString(gk, pe.Identifier.Name))
-		}
-	case event.PruneEventFailed:
-		ef.print("%s prune failed: %s", resourceIDToString(pe.Identifier.GroupKind, pe.Identifier.Name),
+func (ef *formatter) FormatPruneEvent(pe event.PruneEvent) error {
+	gk := pe.Identifier.GroupKind
+	if pe.Error != nil {
+		ef.print("%s prune failed: %s", resourceIDToString(gk, pe.Identifier.Name),
 			pe.Error.Error())
+		return nil
+	}
+
+	switch pe.Operation {
+	case event.Pruned:
+		ef.print("%s pruned", resourceIDToString(gk, pe.Identifier.Name))
+	case event.PruneSkipped:
+		ef.print("%s prune skipped", resourceIDToString(gk, pe.Identifier.Name))
 	}
 	return nil
 }
 
-func (ef *formatter) FormatDeleteEvent(de event.DeleteEvent, ds *list.DeleteStats) error {
-	switch de.Type {
-	case event.DeleteEventCompleted:
-		ef.print("%d resource(s) deleted, %d skipped", ds.Deleted, ds.Skipped)
-	case event.DeleteEventResourceUpdate:
-		obj := de.Object
-		gvk := obj.GetObjectKind().GroupVersionKind()
-		name := getName(obj)
-		switch de.Operation {
-		case event.Deleted:
-			ef.print("%s deleted", resourceIDToString(gvk.GroupKind(), name))
-		case event.DeleteSkipped:
-			ef.print("%s delete skipped", resourceIDToString(gvk.GroupKind(), name))
-		}
-	case event.DeleteEventFailed:
-		ef.print("%s deletion failed: %s", resourceIDToString(de.Identifier.GroupKind, de.Identifier.Name),
+func (ef *formatter) FormatDeleteEvent(de event.DeleteEvent) error {
+	gk := de.Identifier.GroupKind
+	name := de.Identifier.Name
+
+	if de.Error != nil {
+		ef.print("%s deletion failed: %s", resourceIDToString(gk, name),
 			de.Error.Error())
+		return nil
+	}
+
+	switch de.Operation {
+	case event.Deleted:
+		ef.print("%s deleted", resourceIDToString(gk, name))
+	case event.DeleteSkipped:
+		ef.print("%s delete skipped", resourceIDToString(gk, name))
 	}
 	return nil
 }
@@ -109,18 +86,46 @@ func (ef *formatter) FormatErrorEvent(_ event.ErrorEvent) error {
 	return nil
 }
 
-func (ef *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) {
-	ef.print("%s is %s: %s", resourceIDToString(id.GroupKind, id.Name),
-		se.Resource.Status.String(), se.Resource.Message)
-}
+func (ef *formatter) FormatActionGroupEvent(age event.ActionGroupEvent, ags []event.ActionGroup,
+	as *list.ApplyStats, ps *list.PruneStats, ds *list.DeleteStats, c list.Collector) error {
+	if age.Action == event.ApplyAction && age.Type == event.Finished {
+		output := fmt.Sprintf("%d resource(s) applied. %d created, %d unchanged, %d configured, %d failed",
+			as.Sum(), as.Created, as.Unchanged, as.Configured, as.Failed)
+		// Only print information about serverside apply if some of the
+		// resources actually were applied serverside.
+		if as.ServersideApplied > 0 {
+			output += fmt.Sprintf(", %d serverside applied", as.ServersideApplied)
+		}
+		ef.print(output)
+	}
 
-func getName(obj runtime.Object) string {
-	if acc, err := meta.Accessor(obj); err == nil {
-		if n := acc.GetName(); len(n) > 0 {
-			return n
+	if age.Action == event.PruneAction && age.Type == event.Finished {
+		ef.print("%d resource(s) pruned, %d skipped, %d failed", ps.Pruned, ps.Skipped, ps.Failed)
+	}
+
+	if age.Action == event.DeleteAction && age.Type == event.Finished {
+		ef.print("%d resource(s) deleted, %d skipped", ds.Deleted, ds.Skipped)
+	}
+
+	if age.Action == event.WaitAction && age.Type == event.Started {
+		ag, found := list.ActionGroupByName(age.GroupName, ags)
+		if !found {
+			panic(fmt.Errorf("unknown action group name %q", age.GroupName))
+		}
+		for id, se := range c.LatestStatus() {
+			// Only print information about objects that we actually care about
+			// for this wait task.
+			if found := object.ObjMetas(ag.Identifiers).Contains(id); found {
+				ef.printResourceStatus(id, se)
+			}
 		}
 	}
-	return "<unknown>"
+	return nil
+}
+
+func (ef *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) {
+	ef.print("%s is %s: %s", resourceIDToString(id.GroupKind, id.Name),
+		se.PollResourceInfo.Status.String(), se.PollResourceInfo.Message)
 }
 
 // resourceIDToString returns the string representation of a GroupKind and a resource name.
diff --git a/thirdparty/cli-utils/printers/events/printer.go b/thirdparty/cli-utils/printers/events/printer.go
index 8742d12a..38c871cc 100644
--- a/thirdparty/cli-utils/printers/events/printer.go
+++ b/thirdparty/cli-utils/printers/events/printer.go
@@ -6,12 +6,14 @@ package events
 import (
 	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/printer"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
+	"sigs.k8s.io/cli-utils/pkg/common"
 	"sigs.k8s.io/cli-utils/pkg/print/list"
 )
 
 func NewPrinter(ioStreams genericclioptions.IOStreams) printer.Printer {
 	return &list.BaseListPrinter{
-		IOStreams:        ioStreams,
-		FormatterFactory: NewFormatter,
+		FormatterFactory: func(previewStrategy common.DryRunStrategy) list.Formatter {
+			return NewFormatter(ioStreams, previewStrategy)
+		},
 	}
 }
diff --git a/thirdparty/cli-utils/printers/json/formatter.go b/thirdparty/cli-utils/printers/json/formatter.go
index c402654c..7d8a7b19 100644
--- a/thirdparty/cli-utils/printers/json/formatter.go
+++ b/thirdparty/cli-utils/printers/json/formatter.go
@@ -28,9 +28,56 @@ type formatter struct {
 	ioStreams      genericclioptions.IOStreams
 }
 
-func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats, c list.Collector) error {
-	switch ae.Type {
-	case event.ApplyEventCompleted:
+func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent) error {
+	eventInfo := jf.baseResourceEvent(ae.Identifier)
+	if ae.Error != nil {
+		eventInfo["error"] = ae.Error.Error()
+		return jf.printEvent("apply", "resourceFailed", eventInfo)
+	}
+	eventInfo["operation"] = ae.Operation.String()
+	return jf.printEvent("apply", "resourceApplied", eventInfo)
+}
+
+func (jf *formatter) FormatStatusEvent(se event.StatusEvent) error {
+	return jf.printResourceStatus(se)
+}
+
+func (jf *formatter) printResourceStatus(se event.StatusEvent) error {
+	eventInfo := jf.baseResourceEvent(se.Identifier)
+	eventInfo["status"] = se.PollResourceInfo.Status.String()
+	eventInfo["message"] = se.PollResourceInfo.Message
+	return jf.printEvent("status", "resourceStatus", eventInfo)
+}
+
+func (jf *formatter) FormatPruneEvent(pe event.PruneEvent) error {
+	eventInfo := jf.baseResourceEvent(pe.Identifier)
+	if pe.Error != nil {
+		eventInfo["error"] = pe.Error.Error()
+		return jf.printEvent("prune", "resourceFailed", eventInfo)
+	}
+	eventInfo["operation"] = pe.Operation.String()
+	return jf.printEvent("prune", "resourcePruned", eventInfo)
+}
+
+func (jf *formatter) FormatDeleteEvent(de event.DeleteEvent) error {
+	eventInfo := jf.baseResourceEvent(de.Identifier)
+	if de.Error != nil {
+		eventInfo["error"] = de.Error.Error()
+		return jf.printEvent("delete", "resourceFailed", eventInfo)
+	}
+	eventInfo["operation"] = de.Operation.String()
+	return jf.printEvent("delete", "resourceDeleted", eventInfo)
+}
+
+func (jf *formatter) FormatErrorEvent(ee event.ErrorEvent) error {
+	return jf.printEvent("error", "error", map[string]interface{}{
+		"error": ee.Err.Error(),
+	})
+}
+
+func (jf *formatter) FormatActionGroupEvent(age event.ActionGroupEvent, ags []event.ActionGroup,
+	as *list.ApplyStats, ps *list.PruneStats, ds *list.DeleteStats, c list.Collector) error {
+	if age.Action == event.ApplyAction && age.Type == event.Finished {
 		if err := jf.printEvent("apply", "completed", map[string]interface{}{
 			"count":           as.Sum(),
 			"createdCount":    as.Created,
@@ -41,112 +88,47 @@ func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats,
 		}); err != nil {
 			return err
 		}
-
-		for id, se := range c.LatestStatus() {
-			if err := jf.printResourceStatus(id, se); err != nil {
-				return err
-			}
-		}
-	case event.ApplyEventResourceUpdate:
-		gk := ae.Identifier.GroupKind
-		eventInfo := map[string]interface{}{
-			"group":     gk.Group,
-			"kind":      gk.Kind,
-			"namespace": ae.Identifier.Namespace,
-			"name":      ae.Identifier.Name,
-			"operation": ae.Operation.String(),
-		}
-		if ae.Error != nil {
-			eventInfo["error"] = ae.Error.Error()
-		}
-
-		return jf.printEvent("apply", "resourceApplied", eventInfo)
 	}
-	return nil
-}
 
-func (jf *formatter) FormatStatusEvent(se event.StatusEvent, _ list.Collector) error {
-	if se.Type == event.StatusEventResourceUpdate {
-		id := se.Resource.Identifier
-		return jf.printResourceStatus(id, se)
-	}
-	return nil
-}
-
-func (jf *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) error {
-	return jf.printEvent("status", "resourceStatus",
-		map[string]interface{}{
-			"group":     id.GroupKind.Group,
-			"kind":      id.GroupKind.Kind,
-			"namespace": id.Namespace,
-			"name":      id.Name,
-			"status":    se.Resource.Status.String(),
-			"message":   se.Resource.Message,
-		})
-}
-
-func (jf *formatter) FormatPruneEvent(pe event.PruneEvent, ps *list.PruneStats) error {
-	switch pe.Type {
-	case event.PruneEventCompleted:
+	if age.Action == event.PruneAction && age.Type == event.Finished {
 		return jf.printEvent("prune", "completed", map[string]interface{}{
 			"pruned":  ps.Pruned,
 			"skipped": ps.Skipped,
 		})
-	case event.PruneEventResourceUpdate:
-		gk := pe.Identifier.GroupKind
-		return jf.printEvent("prune", "resourcePruned", map[string]interface{}{
-			"group":     gk.Group,
-			"kind":      gk.Kind,
-			"namespace": pe.Identifier.Namespace,
-			"name":      pe.Identifier.Name,
-			"operation": pe.Operation.String(),
-		})
-	case event.PruneEventFailed:
-		gk := pe.Identifier.GroupKind
-		return jf.printEvent("prune", "resourceFailed", map[string]interface{}{
-			"group":     gk.Group,
-			"kind":      gk.Kind,
-			"namespace": pe.Identifier.Namespace,
-			"name":      pe.Identifier.Name,
-			"error":     pe.Error.Error(),
-		})
 	}
-	return nil
-}
 
-func (jf *formatter) FormatDeleteEvent(de event.DeleteEvent, ds *list.DeleteStats) error {
-	switch de.Type {
-	case event.DeleteEventCompleted:
+	if age.Action == event.DeleteAction && age.Type == event.Finished {
 		return jf.printEvent("delete", "completed", map[string]interface{}{
 			"deleted": ds.Deleted,
 			"skipped": ds.Skipped,
 		})
-	case event.DeleteEventResourceUpdate:
-		gk := de.Identifier.GroupKind
-		return jf.printEvent("delete", "resourceDeleted", map[string]interface{}{
-			"group":     gk.Group,
-			"kind":      gk.Kind,
-			"namespace": de.Identifier.Namespace,
-			"name":      de.Identifier.Name,
-			"operation": de.Operation.String(),
-		})
-	case event.DeleteEventFailed:
-		gk := de.Identifier.GroupKind
-		return jf.printEvent("delete", "resourceFailed", map[string]interface{}{
-			"group":     gk.Group,
-			"kind":      gk.Kind,
-			"namespace": de.Identifier.Namespace,
-			"name":      de.Identifier.Name,
-			"error":     de.Error.Error(),
-		})
+	}
+
+	if age.Action == event.WaitAction && age.Type == event.Started {
+		ag, found := list.ActionGroupByName(age.GroupName, ags)
+		if !found {
+			panic(fmt.Errorf("unknown action group name %q", age.GroupName))
+		}
+		for id, se := range c.LatestStatus() {
+			// Only print information about objects that we actually care about
+			// for this wait task.
+			if found := object.ObjMetas(ag.Identifiers).Contains(id); found {
+				if err := jf.printResourceStatus(se); err != nil {
+					return err
+				}
+			}
+		}
 	}
 	return nil
 }
 
-func (jf *formatter) FormatErrorEvent(ee event.ErrorEvent) error {
-	return jf.printEvent("error", "error", map[string]interface{}{
-		"error": ee.Err.Error(),
-	})
+func (jf *formatter) baseResourceEvent(identifier object.ObjMetadata) map[string]interface{} {
+	return map[string]interface{}{
+		"group":     identifier.GroupKind.Group,
+		"kind":      identifier.GroupKind.Kind,
+		"namespace": identifier.Namespace,
+		"name":      identifier.Name,
+	}
 }
 
 func (jf *formatter) printEvent(t, eventType string, content map[string]interface{}) error {
diff --git a/thirdparty/cli-utils/printers/json/printer.go b/thirdparty/cli-utils/printers/json/printer.go
index a4d57fdd..2a673a66 100644
--- a/thirdparty/cli-utils/printers/json/printer.go
+++ b/thirdparty/cli-utils/printers/json/printer.go
@@ -6,12 +6,14 @@ package json
 import (
 	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/printer"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
+	"sigs.k8s.io/cli-utils/pkg/common"
 	"sigs.k8s.io/cli-utils/pkg/print/list"
 )
 
 func NewPrinter(ioStreams genericclioptions.IOStreams) printer.Printer {
 	return &list.BaseListPrinter{
-		IOStreams:        ioStreams,
-		FormatterFactory: NewFormatter,
+		FormatterFactory: func(previewStrategy common.DryRunStrategy) list.Formatter {
+			return NewFormatter(ioStreams, previewStrategy)
+		},
 	}
 }
diff --git a/thirdparty/cli-utils/printers/printers.go b/thirdparty/cli-utils/printers/printers.go
index 93ffc5e0..94fbdaac 100644
--- a/thirdparty/cli-utils/printers/printers.go
+++ b/thirdparty/cli-utils/printers/printers.go
@@ -9,7 +9,6 @@ import (
 	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/printer"
 	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/table"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
-	"sigs.k8s.io/cli-utils/pkg/print/list"
 )
 
 const (
@@ -25,10 +24,7 @@ func GetPrinter(printerType string, ioStreams genericclioptions.IOStreams) print
 			IOStreams: ioStreams,
 		}
 	case JSONPrinter:
-		return &list.BaseListPrinter{
-			IOStreams:        ioStreams,
-			FormatterFactory: json.NewFormatter,
-		}
+		return json.NewPrinter(ioStreams)
 	default:
 		return events.NewPrinter(ioStreams)
 	}
diff --git a/thirdparty/cli-utils/printers/table/collector.go b/thirdparty/cli-utils/printers/table/collector.go
index 24ddcade..b4ae5d2a 100644
--- a/thirdparty/cli-utils/printers/table/collector.go
+++ b/thirdparty/cli-utils/printers/table/collector.go
@@ -15,7 +15,7 @@ import (
 	"sigs.k8s.io/cli-utils/pkg/print/table"
 )
 
-func newResourceStateCollector(resourceGroups []event.ResourceGroup) *ResourceStateCollector {
+func newResourceStateCollector(resourceGroups []event.ActionGroup) *ResourceStateCollector {
 	resourceInfos := make(map[object.ObjMetadata]*ResourceInfo)
 	for _, group := range resourceGroups {
 		action := group.Action
@@ -70,15 +70,15 @@ type ResourceInfo struct {
 
 	// ApplyOpResult contains the result after
 	// a resource has been applied to the cluster.
-	ApplyOpResult *event.ApplyEventOperation
+	ApplyOpResult event.ApplyEventOperation
 
 	// PruneOpResult contains the result after
 	// a prune operation on a resource
-	PruneOpResult *event.PruneEventOperation
+	PruneOpResult event.PruneEventOperation
 
 	// DeleteOpResult contains the result after
 	// a delete operation on a resource
-	DeleteOpResult *event.DeleteEventOperation
+	DeleteOpResult event.DeleteEventOperation
 }
 
 // Identifier returns the identifier for the given resource.
@@ -182,48 +182,37 @@ func (r *ResourceStateCollector) processEvent(ev event.Event) error {
 // processStatusEvent handles events pertaining to a status
 // update for a resource.
 func (r *ResourceStateCollector) processStatusEvent(e event.StatusEvent) {
-	if e.Type == event.StatusEventResourceUpdate {
-		klog.V(7).Infoln("processing status event")
-		resource := e.Resource
-		if resource == nil {
-			klog.V(4).Infoln("status event missing Resource field; no processing")
-			return
-		}
-		previous, found := r.resourceInfos[resource.Identifier]
-		if !found {
-			klog.V(4).Infof("%s status event not found in ResourceInfos; no processing", resource.Identifier)
-			return
-		}
-		previous.resourceStatus = e.Resource
+	klog.V(7).Infoln("processing status event")
+	previous, found := r.resourceInfos[e.Identifier]
+	if !found {
+		klog.V(4).Infof("%s status event not found in ResourceInfos; no processing", e.Identifier)
+		return
 	}
+	previous.resourceStatus = e.PollResourceInfo
 }
 
 // processApplyEvent handles events relating to apply operations
 func (r *ResourceStateCollector) processApplyEvent(e event.ApplyEvent) {
-	if e.Type == event.ApplyEventResourceUpdate {
-		identifier := e.Identifier
-		klog.V(7).Infof("processing apply event for %s", identifier)
-		previous, found := r.resourceInfos[identifier]
-		if !found {
-			klog.V(4).Infof("%s apply event not found in ResourceInfos; no processing", identifier)
-			return
-		}
-		previous.ApplyOpResult = &e.Operation
+	identifier := e.Identifier
+	klog.V(7).Infof("processing apply event for %s", identifier)
+	previous, found := r.resourceInfos[identifier]
+	if !found {
+		klog.V(4).Infof("%s apply event not found in ResourceInfos; no processing", identifier)
+		return
 	}
+	previous.ApplyOpResult = e.Operation
 }
 
 // processPruneEvent handles event related to prune operations.
 func (r *ResourceStateCollector) processPruneEvent(e event.PruneEvent) {
-	if e.Type == event.PruneEventResourceUpdate {
-		identifier := e.Identifier
-		klog.V(7).Infof("processing prune event for %s", identifier)
-		previous, found := r.resourceInfos[identifier]
-		if !found {
-			klog.V(4).Infof("%s prune event not found in ResourceInfos; no processing", identifier)
-			return
-		}
-		previous.PruneOpResult = &e.Operation
+	identifier := e.Identifier
+	klog.V(7).Infof("processing prune event for %s", identifier)
+	previous, found := r.resourceInfos[identifier]
+	if !found {
+		klog.V(4).Infof("%s prune event not found in ResourceInfos; no processing", identifier)
+		return
 	}
+	previous.PruneOpResult = e.Operation
 }
 
 // ResourceState contains the latest state for all the resources.
diff --git a/thirdparty/cli-utils/printers/table/printer.go b/thirdparty/cli-utils/printers/table/printer.go
index b303d33e..ffa412dc 100644
--- a/thirdparty/cli-utils/printers/table/printer.go
+++ b/thirdparty/cli-utils/printers/table/printer.go
@@ -35,7 +35,7 @@ func (t *Printer) Print(ch <-chan event.Event, _ common.DryRunStrategy) error {
 	}
 	// Create a new collector and initialize it with the resources
 	// we are interested in.
-	coll := newResourceStateCollector(initEvent.ResourceGroups)
+	coll := newResourceStateCollector(initEvent.ActionGroups)
 
 	stop := make(chan struct{})
 
@@ -89,11 +89,11 @@ var (
 			var text string
 			switch resInfo.ResourceAction {
 			case event.ApplyAction:
-				if resInfo.ApplyOpResult != nil {
+				if resInfo.ApplyOpResult != event.ApplyUnspecified {
 					text = resInfo.ApplyOpResult.String()
 				}
 			case event.PruneAction:
-				if resInfo.PruneOpResult != nil {
+				if resInfo.PruneOpResult != event.PruneUnspecified {
 					text = resInfo.PruneOpResult.String()
 				}
 			}
diff --git a/thirdparty/cli-utils/status/cmdstatus.go b/thirdparty/cli-utils/status/cmdstatus.go
index 216551b4..d332949a 100644
--- a/thirdparty/cli-utils/status/cmdstatus.go
+++ b/thirdparty/cli-utils/status/cmdstatus.go
@@ -57,6 +57,7 @@ func NewRunner(ctx context.Context, provider provider.Provider, loader manifestr
 		Long:    livedocs.StatusLong,
 		Example: livedocs.StatusExamples,
 	}
+	r.Command = c
 	c.Flags().DurationVar(&r.period, "poll-period", 2*time.Second,
 		"Polling period for resource statuses.")
 	c.Flags().StringVar(&r.pollUntil, "poll-until", "known",
@@ -64,8 +65,6 @@ func NewRunner(ctx context.Context, provider provider.Provider, loader manifestr
 	c.Flags().StringVar(&r.output, "output", "events", "Output format.")
 	c.Flags().DurationVar(&r.timeout, "timeout", 0,
 		"How long to wait before exiting")
-
-	r.Command = c
 	return r
 }
 
diff --git a/thirdparty/cli-utils/status/printers/table/table_printer.go b/thirdparty/cli-utils/status/printers/table/table_printer.go
index 6f248889..d838c978 100644
--- a/thirdparty/cli-utils/status/printers/table/table_printer.go
+++ b/thirdparty/cli-utils/status/printers/table/table_printer.go
@@ -33,7 +33,7 @@ func NewTablePrinter(ioStreams genericclioptions.IOStreams) *tablePrinter {
 
 // Print take an event channel and outputs the status events on the channel
 // until the channel is closed .
-// nolint
+//nolint:interfacer
 func (t *tablePrinter) Print(ch <-chan event.Event, identifiers []object.ObjMetadata,
 	cancelFunc collector.ObserverFunc) {
 	coll := collector.NewResourceStatusCollector(identifiers)
diff --git a/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go b/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go
index d37f64d7..e5f2bc56 100644
--- a/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go
+++ b/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go
@@ -58,6 +58,8 @@ func GetEvalFnRunner(ctx context.Context, parent string) *EvalFnRunner {
 		"a list of environment variables to be used by functions")
 	r.Command.Flags().BoolVar(
 		&r.AsCurrentUser, "as-current-user", false, "use the uid and gid that kpt is running with to run the function in the container")
+	r.Command.Flags().StringVar(&r.ImagePullPolicy, "image-pull-policy", "always",
+		"pull image before running the container. It should be one of always, ifNotPresent and never.")
 	cmdutil.FixDocs("kpt", parent, c)
 	return r
 }
@@ -75,6 +77,7 @@ type EvalFnRunner struct {
 	FnConfigPath         string
 	RunFns               runfn.RunFns
 	ResultsDir           string
+	ImagePullPolicy      string
 	Network              bool
 	Mounts               []string
 	Env                  []string
@@ -224,6 +227,9 @@ func (r *EvalFnRunner) preRunE(c *cobra.Command, args []string) error {
 	if r.Image == "" && r.ExecPath == "" {
 		return errors.Errorf("must specify --image or --exec-path")
 	}
+	if err := cmdutil.ValidateImagePullPolicyValue(r.ImagePullPolicy); err != nil {
+		return err
+	}
 	var dataItems []string
 	if c.ArgsLenAtDash() >= 0 {
 		dataItems = append(dataItems, args[c.ArgsLenAtDash():]...)
@@ -287,6 +293,7 @@ func (r *EvalFnRunner) preRunE(c *cobra.Command, args []string) error {
 		AsCurrentUser:        r.AsCurrentUser,
 		FnConfigPath:         r.FnConfigPath,
 		IncludeMetaResources: r.IncludeMetaResources,
+		ImagePullPolicy:      cmdutil.StringToImagePullPolicy(r.ImagePullPolicy),
 		// fn eval should remove all files when all resources
 		// are deleted.
 		ContinueOnEmptyResult: true,
diff --git a/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go b/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go
index a0c54df4..276562fd 100644
--- a/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go
+++ b/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go
@@ -17,7 +17,7 @@ import (
 func GetTreeRunner(name string) *TreeRunner {
 	r := &TreeRunner{}
 	c := &cobra.Command{
-		Use:     "tree [DIR | -]",
+		Use:     "tree [DIR]",
 		Short:   pkgdocs.TreeShort,
 		Long:    pkgdocs.TreeLong,
 		Example: pkgdocs.TreeExamples,
@@ -44,13 +44,8 @@ func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
 	if len(args) == 0 {
 		args = append(args, root)
 	}
-	if args[0] == "-" {
-		input = &kio.ByteReader{Reader: c.InOrStdin()}
-	} else {
-		root = filepath.Clean(args[0])
-		input = kio.LocalPackageReader{PackagePath: args[0], MatchFilesGlob: r.getMatchFilesGlob()}
-	}
-
+	root = filepath.Clean(args[0])
+	input = kio.LocalPackageReader{PackagePath: args[0], MatchFilesGlob: r.getMatchFilesGlob()}
 	fltrs := []kio.Filter{&filters.IsLocalConfig{
 		IncludeLocalConfig: true,
 	}}
diff --git a/thirdparty/cmdconfig/commands/cmdtree/tree.go b/thirdparty/cmdconfig/commands/cmdtree/tree.go
index 87df97f1..0450aa01 100644
--- a/thirdparty/cmdconfig/commands/cmdtree/tree.go
+++ b/thirdparty/cmdconfig/commands/cmdtree/tree.go
@@ -23,8 +23,8 @@ const (
 	// TreeStructurePackage configures TreeWriter to generate the tree structure off of the
 	// Resources packages.
 	TreeStructurePackage TreeStructure = "directory"
-
-	PkgPrefix = "PKG: "
+	// %q holds the package name
+	PkgNameFormat = "Package %q"
 )
 
 var GraphStructures = []string{string(TreeStructurePackage)}
@@ -51,7 +51,6 @@ func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
 
 	// create the new tree
 	tree := treeprint.New()
-	tree.SetValue(p.Root)
 
 	// add each package to the tree
 	treeIndex := map[string]treeprint.Tree{}
@@ -87,21 +86,25 @@ func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
 		}
 	}
 
-	out := tree.String()
+	if p.Root == "." {
+		// get the path to current working directory
+		d, err := os.Getwd()
+		if err != nil {
+			return err
+		}
+		p.Root = d
+	}
 	_, err := os.Stat(filepath.Join(p.Root, kptfilev1alpha2.KptFileName))
 	if !os.IsNotExist(err) {
-		if p.Root == "." {
-			// replace . with the current working dir name
-			d, err := os.Getwd()
-			if err != nil {
-				return err
-			}
-			out = strings.TrimPrefix(out, ".")
-			out = filepath.Base(d) + out
-		}
-		out = PkgPrefix + out
+		// if Kptfile exists in the root directory, it is a kpt package
+		// print only package name and not entire path
+		tree.SetValue(fmt.Sprintf(PkgNameFormat, filepath.Base(p.Root)))
+	} else {
+		// else it is just a directory, so print only directory name
+		tree.SetValue(filepath.Base(p.Root))
 	}
 
+	out := tree.String()
 	_, err = io.WriteString(p.Writer, out)
 	return err
 }
@@ -112,9 +115,9 @@ func branchName(root, dirRelPath string) string {
 	name := filepath.Base(dirRelPath)
 	_, err := os.Stat(filepath.Join(root, dirRelPath, kptfilev1alpha2.KptFileName))
 	if !os.IsNotExist(err) {
-		// add Pkg: prefix indicating that it is a separate package as it has
+		// add Package prefix indicating that it is a separate package as it has
 		// Kptfile
-		return PkgPrefix + name
+		return fmt.Sprintf(PkgNameFormat, name)
 	}
 	return name
 }
diff --git a/thirdparty/kyaml/runfn/runfn.go b/thirdparty/kyaml/runfn/runfn.go
index e65f6178..c977f9ac 100644
--- a/thirdparty/kyaml/runfn/runfn.go
+++ b/thirdparty/kyaml/runfn/runfn.go
@@ -88,6 +88,8 @@ type RunFns struct {
 	// IncludeMetaResources indicates will kpt add pkg meta resources such as
 	// Kptfile to the input resources to the function.
 	IncludeMetaResources bool
+
+	ImagePullPolicy fnruntime.ImagePullPolicy
 }
 
 // Execute runs the command
@@ -430,11 +432,12 @@ func (r *RunFns) defaultFnFilterProvider(spec runtimeutil.FunctionSpec, fnConfig
 			return nil, err
 		}
 		c := &fnruntime.ContainerFn{
-			Path:          r.uniquePath,
-			Image:         spec.Container.Image,
-			UIDGID:        uidgid,
-			StorageMounts: r.StorageMounts,
-			Env:           spec.Container.Env,
+			Path:            r.uniquePath,
+			Image:           spec.Container.Image,
+			ImagePullPolicy: r.ImagePullPolicy,
+			UIDGID:          uidgid,
+			StorageMounts:   r.StorageMounts,
+			Env:             spec.Container.Env,
 			Perm: fnruntime.ContainerFnPermission{
 				AllowNetwork: spec.Container.Network,
 				// mounts are always from CLI flags so we allow


Test patch:
diff --git a/e2e/fn_test.go b/e2e/fn_test.go
index cc8cbaf2..9a813ee8 100644
--- a/e2e/fn_test.go
+++ b/e2e/fn_test.go
@@ -24,26 +24,37 @@ import (
 )
 
 func TestFnRender(t *testing.T) {
-	runTests(t, filepath.Join(".", "testdata", "fn-render"))
+	runAllTests(t, filepath.Join(".", "testdata", "fn-render"))
 }
 
 func TestFnEval(t *testing.T) {
-	runTests(t, filepath.Join(".", "testdata", "fn-eval"))
+	runAllTests(t, filepath.Join(".", "testdata", "fn-eval"))
 }
 
 // runTests will scan test cases in 'path', run the command
 // on all of the packages in path, and test that
 // the diff between the results and the original package is as
 // expected
-func runTests(t *testing.T, path string) {
+func runAllTests(t *testing.T, path string) {
 	cases, err := runner.ScanTestCases(path)
 	if err != nil {
 		t.Fatalf("failed to scan test cases: %s", err)
 	}
+	// Run all the sequential tests first then run the parallel tests.
+	runTests(t, cases, true)
+	runTests(t, cases, false)
+}
+
+func runTests(t *testing.T, cases *runner.TestCases, sequential bool) {
 	for _, c := range *cases {
 		c := c // capture range variable
+		if c.Config.Sequential != sequential {
+			continue
+		}
 		t.Run(c.Path, func(t *testing.T) {
-			t.Parallel()
+			if !c.Config.Sequential {
+				t.Parallel()
+			}
 			r, err := runner.NewRunner(t, c, c.Config.TestType)
 			if err != nil {
 				t.Fatalf("failed to create test runner: %s", err)
diff --git a/e2e/live/end-to-end-test.sh b/e2e/live/end-to-end-test.sh
index 68bc70ec..b8809938 100755
--- a/e2e/live/end-to-end-test.sh
+++ b/e2e/live/end-to-end-test.sh
@@ -632,7 +632,7 @@ assertContains "namespace: continue-err-namespace"
 ${BIN_DIR}/kpt live apply e2e/live/testdata/continue-on-error > $OUTPUT_DIR/status
 assertRGInventory "continue-err-namespace" "2"
 assertContains "pod/pod-a created"
-assertContains "pod/pod-B failed"
+assertContains "pod/pod-B apply failed"
 assertPodExists "pod-a" "continue-err-namespace"
 assertPodNotExists "pod-B" "continue-err-namespace"
 printResult
@@ -664,7 +664,7 @@ wait 2
 # in the "rbac-error" namespace (succeeds).
 ${BIN_DIR}/kpt live apply e2e/live/testdata/rbac-error-step-2 > $OUTPUT_DIR/status
 assertRGInventory "rbac-error" "1"
-assertContains "configmap/error-config-map failed"
+assertContains "configmap/error-config-map apply failed"
 assertContains "configmap/valid-config-map created"
 assertContains "2 resource(s) applied. 1 created, 0 unchanged, 0 configured, 1 failed"
 assertContains "0 resource(s) pruned, 0 skipped, 0 failed"
diff --git a/e2e/testdata/fn-eval/function-chain/.expected/exec.sh b/e2e/testdata/fn-eval/function-chain/.expected/exec.sh
index 1c7b0301..7fbfddd7 100644
--- a/e2e/testdata/fn-eval/function-chain/.expected/exec.sh
+++ b/e2e/testdata/fn-eval/function-chain/.expected/exec.sh
@@ -1,6 +1,21 @@
 #! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-kpt fn source |\
-kpt fn eval - --image gcr.io/kpt-fn/set-namespace:v0.1 -- namespace=staging |\
-kpt fn eval - --image gcr.io/kpt-fn/set-label:v0.1 -- foo=bar |\
-kpt fn sink .
+set -eo pipefail
+
+kpt fn source \
+| kpt fn eval - --image gcr.io/kpt-fn/set-namespace:v0.1 -- namespace=staging \
+| kpt fn eval - --image gcr.io/kpt-fn/set-label:v0.1 -- foo=bar \
+| kpt fn sink .
diff --git a/e2e/testdata/fn-eval/function-env/.expected/config.yaml b/e2e/testdata/fn-eval/function-env/.expected/config.yaml
new file mode 100644
index 00000000..0678c588
--- /dev/null
+++ b/e2e/testdata/fn-eval/function-env/.expected/config.yaml
@@ -0,0 +1,16 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exitCode: 1
+stdErr: "FOO=BAR EXPORT_ENV=export_env_value"
diff --git a/e2e/testdata/fn-eval/function-env/.expected/exec.sh b/e2e/testdata/fn-eval/function-env/.expected/exec.sh
new file mode 100644
index 00000000..37edc9f1
--- /dev/null
+++ b/e2e/testdata/fn-eval/function-env/.expected/exec.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+IMAGE_TAG="gcr.io/kpt-fn-demo/printenv:v0.1"
+export EXPORT_ENV="export_env_value"
+
+kpt fn source \
+| kpt fn eval - --image $IMAGE_TAG -e EXPORT_ENV -e FOO=BAR
diff --git a/e2e/testdata/fn-eval/function-env/.krmginore b/e2e/testdata/fn-eval/function-env/.krmginore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/function-env/.krmginore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/image-pull-policy-always/.expected/config.yaml b/e2e/testdata/fn-eval/image-pull-policy-always/.expected/config.yaml
new file mode 100644
index 00000000..d301c435
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-always/.expected/config.yaml
@@ -0,0 +1,20 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+testType: eval
+image: gcr.io/kpt-fn-demo/foo:v0.1
+sequential: true
+imagePullPolicy: always
+exitCode: 1
+stdErr: foo
diff --git a/e2e/testdata/fn-eval/image-pull-policy-always/.expected/setup.sh b/e2e/testdata/fn-eval/image-pull-policy-always/.expected/setup.sh
new file mode 100644
index 00000000..066ff92c
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-always/.expected/setup.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+# Function gcr.io/kpt-fn-demo/foo:v0.1 prints "foo" to stderr and
+# function gcr.io/kpt-fn-demo/bar:v0.1 prints "bar" to stderr.
+# We intentionally tag a wrong image as gcr.io/kpt-fn-demo/foo:v0.1, since we
+# expect the correct image to be pulled and override the wrong image.
+docker pull gcr.io/kpt-fn-demo/bar:v0.1
+docker tag gcr.io/kpt-fn-demo/bar:v0.1 gcr.io/kpt-fn-demo/foo:v0.1
diff --git a/e2e/testdata/fn-eval/image-pull-policy-always/.krmignore b/e2e/testdata/fn-eval/image-pull-policy-always/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-always/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/config.yaml b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/config.yaml
new file mode 100644
index 00000000..57a97454
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/config.yaml
@@ -0,0 +1,20 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+testType: eval
+image: gcr.io/kpt-fn-demo/bar:v0.1
+sequential: true
+imagePullPolicy: ifNotPresent
+exitCode: 1
+stdErr: foo
diff --git a/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/setup.sh b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/setup.sh
new file mode 100644
index 00000000..1a03f012
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.expected/setup.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+# Function gcr.io/kpt-fn-demo/foo:v0.1 prints "foo" to stderr and
+# function gcr.io/kpt-fn-demo/bar:v0.1 prints "bar" to stderr.
+# We intentionally tag a wrong image as pull gcr.io/kpt-fn-demo/bar:v0.1
+docker pull gcr.io/kpt-fn-demo/foo:v0.1
+docker tag gcr.io/kpt-fn-demo/foo:v0.1 gcr.io/kpt-fn-demo/bar:v0.1
diff --git a/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.krmignore b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-if-not-present/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/image-pull-policy-never/.expected/config.yaml b/e2e/testdata/fn-eval/image-pull-policy-never/.expected/config.yaml
new file mode 100644
index 00000000..07550a24
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-never/.expected/config.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+testType: eval
+image: gcr.io/kpt-fn/not-exist:unstable
+imagePullPolicy: never
+exitCode: 1
+stdErr: "No such image"
diff --git a/e2e/testdata/fn-eval/image-pull-policy-never/.krmignore b/e2e/testdata/fn-eval/image-pull-policy-never/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-never/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/image-pull-policy-never/resources.yaml b/e2e/testdata/fn-eval/image-pull-policy-never/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-eval/image-pull-policy-never/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-eval/out-of-place/.expected/diff.patch b/e2e/testdata/fn-eval/out-of-place/.expected/diff.patch
new file mode 100644
index 00000000..63ccc2b3
--- /dev/null
+++ b/e2e/testdata/fn-eval/out-of-place/.expected/diff.patch
@@ -0,0 +1,19 @@
+diff --git a/resources.yaml b/resources.yaml
+index 7a494c9..254b9cd 100644
+--- a/resources.yaml
++++ b/resources.yaml
+@@ -15,6 +15,7 @@ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+   name: nginx-deployment
++  namespace: staging
+ spec:
+   replicas: 3
+ ---
+@@ -22,5 +23,6 @@ apiVersion: custom.io/v1
+ kind: Custom
+ metadata:
+   name: custom
++  namespace: staging
+ spec:
+   image: nginx:1.2.3
diff --git a/e2e/testdata/fn-eval/out-of-place/.expected/exec.sh b/e2e/testdata/fn-eval/out-of-place/.expected/exec.sh
new file mode 100644
index 00000000..4ae6a34a
--- /dev/null
+++ b/e2e/testdata/fn-eval/out-of-place/.expected/exec.sh
@@ -0,0 +1,30 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+# create a temporary directory
+TEMP_DIR=$(mktemp -d)
+
+kpt fn source \
+| kpt fn eval - --image gcr.io/kpt-fn/set-namespace:v0.1 -- namespace=staging \
+| kpt fn sink $TEMP_DIR
+
+# copy back the resources
+rm -r ./*
+cp $TEMP_DIR/* .
+
+# remove temporary directory
+rm -r $TEMP_DIR
diff --git a/e2e/testdata/fn-eval/out-of-place/.krmignore b/e2e/testdata/fn-eval/out-of-place/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/out-of-place/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/out-of-place/resources.yaml b/e2e/testdata/fn-eval/out-of-place/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-eval/out-of-place/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-eval/output-to-stdout/.expected/exec.sh b/e2e/testdata/fn-eval/output-to-stdout/.expected/exec.sh
index 7a989309..a662d1e3 100644
--- a/e2e/testdata/fn-eval/output-to-stdout/.expected/exec.sh
+++ b/e2e/testdata/fn-eval/output-to-stdout/.expected/exec.sh
@@ -1,4 +1,19 @@
 #! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
 
 kpt fn source |\
 kpt fn eval - --image gcr.io/kpt-fn/set-namespace:v0.1 -- namespace=staging
diff --git a/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/config.yaml b/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/config.yaml
new file mode 100644
index 00000000..3b172ec1
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/config.yaml
@@ -0,0 +1,15 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exitCode: 1
diff --git a/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/exec.sh b/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/exec.sh
new file mode 100644
index 00000000..54cca89b
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/disable-network/.expected/exec.sh
@@ -0,0 +1,21 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+kpt fn eval \
+--image gcr.io/kpt-fn/kubeval:v0.1 \
+-- \
+schema_location='https://kubernetesjsonschema.dev'
diff --git a/e2e/testdata/fn-eval/privilege-options/disable-network/.krmginore b/e2e/testdata/fn-eval/privilege-options/disable-network/.krmginore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/disable-network/.krmginore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/privilege-options/disable-network/resources.yaml b/e2e/testdata/fn-eval/privilege-options/disable-network/resources.yaml
new file mode 100644
index 00000000..502aebb0
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/disable-network/resources.yaml
@@ -0,0 +1,21 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: some-secret
+  namespace: default
+data:
+  less_sensitive_key: less sensitive data goes here
+  private_key: sensitive data goes here
diff --git a/e2e/testdata/fn-eval/privilege-options/enable-network/.expected/exec.sh b/e2e/testdata/fn-eval/privilege-options/enable-network/.expected/exec.sh
new file mode 100644
index 00000000..934d7f8b
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/enable-network/.expected/exec.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+kpt fn eval \
+--image gcr.io/kpt-fn/kubeval:v0.1 \
+--network \
+-- \
+schema_location='https://kubernetesjsonschema.dev'
diff --git a/e2e/testdata/fn-eval/privilege-options/enable-network/.krmginore b/e2e/testdata/fn-eval/privilege-options/enable-network/.krmginore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/enable-network/.krmginore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-eval/privilege-options/enable-network/resources.yaml b/e2e/testdata/fn-eval/privilege-options/enable-network/resources.yaml
new file mode 100644
index 00000000..502aebb0
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/enable-network/resources.yaml
@@ -0,0 +1,21 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: some-secret
+  namespace: default
+data:
+  less_sensitive_key: less sensitive data goes here
+  private_key: sensitive data goes here
diff --git a/e2e/testdata/fn-eval/privilege-options/mount/.expected/exec.sh b/e2e/testdata/fn-eval/privilege-options/mount/.expected/exec.sh
new file mode 100644
index 00000000..cf8813c0
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/mount/.expected/exec.sh
@@ -0,0 +1,30 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+# Download schema file
+SCHEMA_DIR="schema/master-standalone"
+mkdir -p "$SCHEMA_DIR"
+curl -sSL 'https://kubernetesjsonschema.dev/master-standalone/configmap-v1.json' -o $SCHEMA_DIR/configmap-v1.json
+
+kpt fn eval \
+--image gcr.io/kpt-fn/kubeval:v0.1 \
+--as-current-user \
+--mount type=bind,src=$(pwd)/schema,dst=/schema-dir \
+-- \
+schema_location=file:///schema-dir
+
+# Remove 'schema' to avoid unwanted diff
+rm -r schema
diff --git a/e2e/testdata/fn-eval/privilege-options/mount/.krmignore b/e2e/testdata/fn-eval/privilege-options/mount/.krmignore
new file mode 100644
index 00000000..5a7b8490
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/mount/.krmignore
@@ -0,0 +1,2 @@
+schema
+.expected
diff --git a/e2e/testdata/fn-eval/privilege-options/mount/resources.yaml b/e2e/testdata/fn-eval/privilege-options/mount/resources.yaml
new file mode 100644
index 00000000..502aebb0
--- /dev/null
+++ b/e2e/testdata/fn-eval/privilege-options/mount/resources.yaml
@@ -0,0 +1,21 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: some-secret
+  namespace: default
+data:
+  less_sensitive_key: less sensitive data goes here
+  private_key: sensitive data goes here
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.expected/diff.patch b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.expected/diff.patch
new file mode 100644
index 00000000..79e7c68f
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.expected/diff.patch
@@ -0,0 +1,39 @@
+diff --git a/db/resources.yaml b/db/resources.yaml
+index ac1fd96..2fa6e44 100644
+--- a/db/resources.yaml
++++ b/db/resources.yaml
+@@ -15,5 +15,15 @@ apiVersion: apps/v1
+ kind: StatefulSet
+ metadata:
+   name: db
++  namespace: staging
++  labels:
++    tier: db
+ spec:
+   replicas: 3
++  selector:
++    matchLabels:
++      tier: db
++  template:
++    metadata:
++      labels:
++        tier: db
+diff --git a/resources.yaml b/resources.yaml
+index 7a494c9..254b9cd 100644
+--- a/resources.yaml
++++ b/resources.yaml
+@@ -15,6 +15,7 @@ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+   name: nginx-deployment
++  namespace: staging
+ spec:
+   replicas: 3
+ ---
+@@ -22,5 +23,6 @@ apiVersion: custom.io/v1
+ kind: Custom
+ metadata:
+   name: custom
++  namespace: staging
+ spec:
+   image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.krmignore b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/Kptfile b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/Kptfile
new file mode 100644
index 00000000..c759bf1e
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/Kptfile
@@ -0,0 +1,9 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app-with-db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/set-namespace:unstable
+      configMap:
+        namespace: staging
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/Kptfile b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/Kptfile
new file mode 100644
index 00000000..94a14166
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/Kptfile
@@ -0,0 +1,8 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/set-label:unstable
+      configPath: labelconfig.yaml
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/labelconfig.yaml b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/labelconfig.yaml
new file mode 100644
index 00000000..aabf4956
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/labelconfig.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: label-config
+data:
+  tier: db
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/resources.yaml b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/resources.yaml
new file mode 100644
index 00000000..ac1fd966
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/db/resources.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: db
+spec:
+  replicas: 3
diff --git a/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/resources.yaml b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-ancestorfn-not-mutate-subpkg-config/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnconfig-cannot-refer-subpkg b/e2e/testdata/fn-render/fnconfig-cannot-refer-subpkg
new file mode 160000
index 00000000..17877d7a
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-cannot-refer-subpkg
@@ -0,0 +1,1 @@
+Subproject commit 17877d7adaa3ee4433c7b9298b87b5b9d3243a62
diff --git a/e2e/testdata/fn-render/fnconfig-not-relative/.expected/config.yaml b/e2e/testdata/fn-render/fnconfig-not-relative/.expected/config.yaml
new file mode 100644
index 00000000..17f87915
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-not-relative/.expected/config.yaml
@@ -0,0 +1,20 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exitCode: 1
+stdErr: |
+  Kptfile is invalid:
+  Field: `pipeline.mutators[0].configPath`
+  Value: "/some/root/labelconfig.yaml"
+  Reason: path must be relative
\ No newline at end of file
diff --git a/e2e/testdata/fn-render/fnconfig-not-relative/.krmignore b/e2e/testdata/fn-render/fnconfig-not-relative/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-not-relative/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/fnconfig-not-relative/Kptfile b/e2e/testdata/fn-render/fnconfig-not-relative/Kptfile
new file mode 100644
index 00000000..c42cf1fc
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-not-relative/Kptfile
@@ -0,0 +1,8 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app-with-db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/set-label:unstable
+      configPath: /some/root/labelconfig.yaml
diff --git a/e2e/testdata/fn-render/fnconfig-not-relative/labelconfig.yaml b/e2e/testdata/fn-render/fnconfig-not-relative/labelconfig.yaml
new file mode 100644
index 00000000..aabf4956
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-not-relative/labelconfig.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: label-config
+data:
+  tier: db
diff --git a/e2e/testdata/fn-render/fnconfig-not-relative/resources.yaml b/e2e/testdata/fn-render/fnconfig-not-relative/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-not-relative/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnconfig-outside-package/.expected/config.yaml b/e2e/testdata/fn-render/fnconfig-outside-package/.expected/config.yaml
new file mode 100644
index 00000000..af9e9345
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-outside-package/.expected/config.yaml
@@ -0,0 +1,20 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exitCode: 1
+stdErr: |
+  Kptfile is invalid:
+  Field: `pipeline.mutators[1].configPath`
+  Value: "some/../../home/.kube/config"
+  Reason: path must not be outside the package
\ No newline at end of file
diff --git a/e2e/testdata/fn-render/fnconfig-outside-package/.krmignore b/e2e/testdata/fn-render/fnconfig-outside-package/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-outside-package/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/fnconfig-outside-package/Kptfile b/e2e/testdata/fn-render/fnconfig-outside-package/Kptfile
new file mode 100644
index 00000000..51520938
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-outside-package/Kptfile
@@ -0,0 +1,10 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app-with-db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/set-label:unstable
+      configPath: labelconfig.yaml
+    - image: gcr.io/kpt-fn/set-label:unstable
+      configPath: some/../../home/.kube/config # config pointing outside the package
diff --git a/e2e/testdata/fn-render/fnconfig-outside-package/labelconfig.yaml b/e2e/testdata/fn-render/fnconfig-outside-package/labelconfig.yaml
new file mode 100644
index 00000000..aabf4956
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-outside-package/labelconfig.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: label-config
+data:
+  tier: db
diff --git a/e2e/testdata/fn-render/fnconfig-outside-package/resources.yaml b/e2e/testdata/fn-render/fnconfig-outside-package/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-outside-package/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.expected/diff.patch b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.expected/diff.patch
new file mode 100644
index 00000000..c0929f05
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.expected/diff.patch
@@ -0,0 +1,28 @@
+diff --git a/resources.yaml b/resources.yaml
+index 7a494c9..d22aec8 100644
+--- a/resources.yaml
++++ b/resources.yaml
+@@ -15,12 +15,23 @@ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+   name: nginx-deployment
++  labels:
++    tier: db
+ spec:
+   replicas: 3
++  selector:
++    matchLabels:
++      tier: db
++  template:
++    metadata:
++      labels:
++        tier: db
+ ---
+ apiVersion: custom.io/v1
+ kind: Custom
+ metadata:
+   name: custom
++  labels:
++    tier: db
+ spec:
+   image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.krmignore b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/Kptfile b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/Kptfile
new file mode 100644
index 00000000..33a4e86a
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/Kptfile
@@ -0,0 +1,8 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app-with-db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/set-label:unstable
+      configPath: confs/labelconfig.yaml
diff --git a/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/confs/labelconfig.yaml b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/confs/labelconfig.yaml
new file mode 100644
index 00000000..aabf4956
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/confs/labelconfig.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: label-config
+data:
+  tier: db
diff --git a/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/resources.yaml b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/fnconfig-pkgfn-refers-subdir/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/fnresult-fn-failure/.expected/results.yaml b/e2e/testdata/fn-render/fnresult-fn-failure/.expected/results.yaml
index 023dcf41..f24539a7 100755
--- a/e2e/testdata/fn-render/fnresult-fn-failure/.expected/results.yaml
+++ b/e2e/testdata/fn-render/fnresult-fn-failure/.expected/results.yaml
@@ -21,6 +21,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: selector
           file:
@@ -30,6 +31,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: template
           file:
@@ -39,6 +41,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: spec.replicas
           file:
@@ -52,6 +55,7 @@ items:
           resourceRef:
             apiVersion: custom.io/v1
             kind: Custom
+            name: custom
           file:
             path: resources.yaml
             index: 1
diff --git a/e2e/testdata/fn-render/generator-subpkgs/.expected/diff.patch b/e2e/testdata/fn-render/generator-subpkgs/.expected/diff.patch
new file mode 100644
index 00000000..50c85a58
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/.expected/diff.patch
@@ -0,0 +1,25 @@
+diff --git a/db/configmap_db-map.yaml b/db/configmap_db-map.yaml
+new file mode 100644
+index 0000000..7a0f3af
+--- /dev/null
++++ b/db/configmap_db-map.yaml
+@@ -0,0 +1,6 @@
++apiVersion: v1
++kind: ConfigMap
++metadata:
++  name: db-map
++data:
++  db-key: db-value
+diff --git a/def/configmap_parent-map.yaml b/def/configmap_parent-map.yaml
+new file mode 100644
+index 0000000..fd1e17d
+--- /dev/null
++++ b/def/configmap_parent-map.yaml
+@@ -0,0 +1,7 @@
++apiVersion: v1
++kind: ConfigMap
++metadata:
++  name: parent-map
++  namespace: def
++data:
++  parent-key: parent-value
\ No newline at end of file
diff --git a/e2e/testdata/fn-render/generator-subpkgs/.krmignore b/e2e/testdata/fn-render/generator-subpkgs/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/generator-subpkgs/Kptfile b/e2e/testdata/fn-render/generator-subpkgs/Kptfile
new file mode 100644
index 00000000..9fa913c0
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/Kptfile
@@ -0,0 +1,8 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app-with-generator
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/upsert-resource:unstable
+      configPath: fn-config.yaml
diff --git a/e2e/testdata/fn-render/generator-subpkgs/db/Kptfile b/e2e/testdata/fn-render/generator-subpkgs/db/Kptfile
new file mode 100644
index 00000000..020dc8ac
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/db/Kptfile
@@ -0,0 +1,8 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: db
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/upsert-resource:unstable
+      configPath: fn-config.yaml
diff --git a/e2e/testdata/fn-render/generator-subpkgs/db/fn-config.yaml b/e2e/testdata/fn-render/generator-subpkgs/db/fn-config.yaml
new file mode 100644
index 00000000..7a0f3af0
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/db/fn-config.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: db-map
+data:
+  db-key: db-value
diff --git a/e2e/testdata/fn-render/generator-subpkgs/db/resources.yaml b/e2e/testdata/fn-render/generator-subpkgs/db/resources.yaml
new file mode 100644
index 00000000..ac1fd966
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/db/resources.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: db
+spec:
+  replicas: 3
diff --git a/e2e/testdata/fn-render/generator-subpkgs/fn-config.yaml b/e2e/testdata/fn-render/generator-subpkgs/fn-config.yaml
new file mode 100644
index 00000000..fd1e17d7
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/fn-config.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: parent-map
+  namespace: def
+data:
+  parent-key: parent-value
diff --git a/e2e/testdata/fn-render/generator-subpkgs/resources.yaml b/e2e/testdata/fn-render/generator-subpkgs/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/generator-subpkgs/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/image-pull-policy-always/.expected/config.yaml b/e2e/testdata/fn-render/image-pull-policy-always/.expected/config.yaml
new file mode 100644
index 00000000..f2d8305a
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-always/.expected/config.yaml
@@ -0,0 +1,18 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+imagePullPolicy: always
+sequential: true
+exitCode: 1
+stdErr: foo
diff --git a/e2e/testdata/fn-render/image-pull-policy-always/.expected/setup.sh b/e2e/testdata/fn-render/image-pull-policy-always/.expected/setup.sh
new file mode 100644
index 00000000..066ff92c
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-always/.expected/setup.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+# Function gcr.io/kpt-fn-demo/foo:v0.1 prints "foo" to stderr and
+# function gcr.io/kpt-fn-demo/bar:v0.1 prints "bar" to stderr.
+# We intentionally tag a wrong image as gcr.io/kpt-fn-demo/foo:v0.1, since we
+# expect the correct image to be pulled and override the wrong image.
+docker pull gcr.io/kpt-fn-demo/bar:v0.1
+docker tag gcr.io/kpt-fn-demo/bar:v0.1 gcr.io/kpt-fn-demo/foo:v0.1
diff --git a/e2e/testdata/fn-render/image-pull-policy-always/.krmignore b/e2e/testdata/fn-render/image-pull-policy-always/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-always/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/image-pull-policy-always/Kptfile b/e2e/testdata/fn-render/image-pull-policy-always/Kptfile
new file mode 100644
index 00000000..fabd1f9e
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-always/Kptfile
@@ -0,0 +1,7 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app
+pipeline:
+  mutators:
+    - image: pull gcr.io/kpt-fn-demo/foo:v0.1
diff --git a/e2e/testdata/fn-render/image-pull-policy-always/resources.yaml b/e2e/testdata/fn-render/image-pull-policy-always/resources.yaml
new file mode 100644
index 00000000..6590f6ca
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-always/resources.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
diff --git a/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/config.yaml b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/config.yaml
new file mode 100644
index 00000000..aab7626b
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/config.yaml
@@ -0,0 +1,18 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+imagePullPolicy: ifNotPresent
+sequential: true
+exitCode: 1
+stdErr: foo
diff --git a/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/setup.sh b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/setup.sh
new file mode 100644
index 00000000..1a03f012
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.expected/setup.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+
+# Function gcr.io/kpt-fn-demo/foo:v0.1 prints "foo" to stderr and
+# function gcr.io/kpt-fn-demo/bar:v0.1 prints "bar" to stderr.
+# We intentionally tag a wrong image as pull gcr.io/kpt-fn-demo/bar:v0.1
+docker pull gcr.io/kpt-fn-demo/foo:v0.1
+docker tag gcr.io/kpt-fn-demo/foo:v0.1 gcr.io/kpt-fn-demo/bar:v0.1
diff --git a/e2e/testdata/fn-render/image-pull-policy-if-not-present/.krmignore b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-if-not-present/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/image-pull-policy-if-not-present/Kptfile b/e2e/testdata/fn-render/image-pull-policy-if-not-present/Kptfile
new file mode 100644
index 00000000..fabd1f9e
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-if-not-present/Kptfile
@@ -0,0 +1,7 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app
+pipeline:
+  mutators:
+    - image: pull gcr.io/kpt-fn-demo/foo:v0.1
diff --git a/e2e/testdata/fn-render/image-pull-policy-if-not-present/resources.yaml b/e2e/testdata/fn-render/image-pull-policy-if-not-present/resources.yaml
new file mode 100644
index 00000000..6590f6ca
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-if-not-present/resources.yaml
@@ -0,0 +1,19 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
diff --git a/e2e/testdata/fn-render/image-pull-policy-never/.expected/config.yaml b/e2e/testdata/fn-render/image-pull-policy-never/.expected/config.yaml
new file mode 100644
index 00000000..17802a23
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-never/.expected/config.yaml
@@ -0,0 +1,17 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+imagePullPolicy: never
+exitCode: 1
+stdErr: "No such image"
diff --git a/e2e/testdata/fn-render/image-pull-policy-never/.krmignore b/e2e/testdata/fn-render/image-pull-policy-never/.krmignore
new file mode 100644
index 00000000..9d7a4007
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-never/.krmignore
@@ -0,0 +1,1 @@
+.expected
diff --git a/e2e/testdata/fn-render/image-pull-policy-never/Kptfile b/e2e/testdata/fn-render/image-pull-policy-never/Kptfile
new file mode 100644
index 00000000..75889211
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-never/Kptfile
@@ -0,0 +1,7 @@
+apiVersion: kpt.dev/v1alpha2
+kind: Kptfile
+metadata:
+  name: app
+pipeline:
+  mutators:
+    - image: gcr.io/kpt-fn/not-exist:unstable
diff --git a/e2e/testdata/fn-render/image-pull-policy-never/resources.yaml b/e2e/testdata/fn-render/image-pull-policy-never/resources.yaml
new file mode 100644
index 00000000..7a494c9c
--- /dev/null
+++ b/e2e/testdata/fn-render/image-pull-policy-never/resources.yaml
@@ -0,0 +1,26 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: nginx-deployment
+spec:
+  replicas: 3
+---
+apiVersion: custom.io/v1
+kind: Custom
+metadata:
+  name: custom
+spec:
+  image: nginx:1.2.3
diff --git a/e2e/testdata/fn-render/kubeval-failure/.expected/config.yaml b/e2e/testdata/fn-render/kubeval-failure/.expected/config.yaml
index b257d428..5e58752b 100644
--- a/e2e/testdata/fn-render/kubeval-failure/.expected/config.yaml
+++ b/e2e/testdata/fn-render/kubeval-failure/.expected/config.yaml
@@ -15,3 +15,4 @@
 # One of the functions in the pipeline fails resulting in
 # non-zero exit code and no changes in the resources.
 exitCode: 1
+stdOut: "[ERROR] selector is required in object \"apps/v1/Deployment/nginx-deployment\" in file \"resources.yaml\" in field \"selector\""
diff --git a/e2e/testdata/fn-render/kubeval-failure/.expected/results.yaml b/e2e/testdata/fn-render/kubeval-failure/.expected/results.yaml
index 929e572a..352d463d 100755
--- a/e2e/testdata/fn-render/kubeval-failure/.expected/results.yaml
+++ b/e2e/testdata/fn-render/kubeval-failure/.expected/results.yaml
@@ -12,6 +12,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: selector
           file:
@@ -21,6 +22,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: template
           file:
@@ -30,6 +32,7 @@ items:
           resourceRef:
             apiVersion: apps/v1
             kind: Deployment
+            name: nginx-deployment
           field:
             path: spec.replicas
           file:
@@ -41,6 +44,7 @@ items:
           resourceRef:
             apiVersion: custom.io/v1
             kind: Custom
+            name: custom
           file:
             path: resources.yaml
             index: 1
diff --git a/e2e/testdata/fn-render/missing-fnconfig/.expected/config.yaml b/e2e/testdata/fn-render/missing-fnconfig/.expected/config.yaml
index a415bb95..be2387df 100644
--- a/e2e/testdata/fn-render/missing-fnconfig/.expected/config.yaml
+++ b/e2e/testdata/fn-render/missing-fnconfig/.expected/config.yaml
@@ -13,7 +13,11 @@
 # limitations under the License.
 
 exitCode: 1
-stdErr: 'Error: missing function config "labelconfig1.yaml"'
+stdErr: |
+  Kptfile is invalid:
+  Field: `pipeline.mutators[1].configPath`
+  Value: "labelconfig1.yaml"
+  Reason: functionConfig must exist in the current package
 stdOut: |
   [RUNNING] "gcr.io/kpt-fn/set-labels:unstable"
   [PASS] "gcr.io/kpt-fn/set-labels:unstable"
diff --git a/internal/errors/resolver/resolver_test.go b/internal/errors/resolver/resolver_test.go
new file mode 100644
index 00000000..6153b14e
--- /dev/null
+++ b/internal/errors/resolver/resolver_test.go
@@ -0,0 +1,50 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package resolver
+
+import (
+	"testing"
+
+	"github.com/GoogleContainerTools/kpt/internal/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestResolveError_DefaultExitCode(t *testing.T) {
+	org := errorResolvers
+	AddErrorResolver(&TestErrorResolver{})
+	defer func() {
+		errorResolvers = org
+	}()
+
+	rr, ok := ResolveError(&TestError{})
+	assert.True(t, ok)
+	assert.Equal(t, 1, rr.ExitCode)
+}
+
+type TestErrorResolver struct{}
+
+func (t *TestErrorResolver) Resolve(err error) (ResolvedResult, bool) {
+	var testError *TestError
+	if errors.As(err, &testError) {
+		return ResolvedResult{}, true
+	}
+	return ResolvedResult{}, false
+}
+
+type TestError struct{}
+
+func (e *TestError) Error() string {
+	return "this is a test"
+}
diff --git a/internal/fnruntime/runner_test.go b/internal/fnruntime/runner_test.go
index 3ec3dd6f..c2d36c4d 100644
--- a/internal/fnruntime/runner_test.go
+++ b/internal/fnruntime/runner_test.go
@@ -24,6 +24,8 @@ import (
 	"strings"
 	"testing"
 
+	fnresult "github.com/GoogleContainerTools/kpt/pkg/api/fnresult/v1alpha2"
+
 	"github.com/GoogleContainerTools/kpt/internal/types"
 	kptfilev1alpha2 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1alpha2"
 	"github.com/stretchr/testify/assert"
@@ -447,3 +449,147 @@ spec:
 		}
 	}
 }
+
+func TestGetResourceRefMetadata(t *testing.T) {
+	tests := map[string]struct {
+		input    string // input
+		expected string // expected result
+	}{
+		"new format with name": {
+			input: `
+message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+			expected: `message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+		},
+		"new format with namespace": {
+			input: `
+message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+    namespace: my-namespace
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+			expected: `message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+    namespace: my-namespace
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+		},
+		"old format with name": {
+			input: `
+message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    metadata:
+      name: nginx-deployment
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+			expected: `message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+		},
+		"old format with namespace": {
+			input: `
+message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    metadata:
+      name: nginx-deployment
+      namespace: my-namespace
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+			expected: `message: selector is required
+severity: error
+resourceRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: nginx-deployment
+    namespace: my-namespace
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+		},
+		"no resourceRef": {
+			input: `
+message: selector is required
+severity: error
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+			expected: `message: selector is required
+severity: error
+field:
+    path: selector
+file:
+    path: resources.yaml
+`,
+		},
+	}
+	for _, tc := range tests {
+		yml, err := yaml.Parse(tc.input)
+		assert.NoError(t, err)
+
+		result := &fnresult.ResultItem{}
+		err = yaml.Unmarshal([]byte(tc.input), result)
+		assert.NoError(t, err)
+		assert.NoError(t, populateResourceRef(yml, result))
+
+		out, err := yaml.Marshal(result)
+		assert.NoError(t, err)
+		assert.Equal(t, tc.expected, string(out))
+	}
+}
diff --git a/pkg/api/kptfile/v1alpha2/validation_test.go b/pkg/api/kptfile/v1alpha2/validation_test.go
index 22600dd2..3c04002f 100644
--- a/pkg/api/kptfile/v1alpha2/validation_test.go
+++ b/pkg/api/kptfile/v1alpha2/validation_test.go
@@ -111,6 +111,62 @@ metadata:
 			},
 			valid: false,
 		},
+		{
+			name: "pipeline: configpath referring file in parent",
+			kptfile: KptFile{
+				Pipeline: &Pipeline{
+					Mutators: []Function{
+						{
+							Image:      "image",
+							ConfigPath: "../config.yaml",
+						},
+					},
+				},
+			},
+			valid: false,
+		},
+		{
+			name: "pipeline: cleaned configpath does not contain ..",
+			kptfile: KptFile{
+				Pipeline: &Pipeline{
+					Mutators: []Function{
+						{
+							Image:      "image",
+							ConfigPath: "a/b/../config.yaml",
+						},
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			name: "pipeline: cleaned configpath contains ..",
+			kptfile: KptFile{
+				Pipeline: &Pipeline{
+					Mutators: []Function{
+						{
+							Image:      "image",
+							ConfigPath: "a/b/../../../config.yaml",
+						},
+					},
+				},
+			},
+			valid: false,
+		},
+		{
+			name: "pipeline: configpath contains invalid .. references",
+			kptfile: KptFile{
+				Pipeline: &Pipeline{
+					Mutators: []Function{
+						{
+							Image:      "image",
+							ConfigPath: "a/.../config.yaml",
+						},
+					},
+				},
+			},
+			valid: false,
+		},
 	}
 
 	for _, c := range cases {
@@ -236,7 +292,7 @@ func TestValidatePath(t *testing.T) {
 		},
 		{
 			"./a/.../b",
-			true,
+			false,
 		},
 		{
 			".",
@@ -244,23 +300,23 @@ func TestValidatePath(t *testing.T) {
 		},
 		{
 			"a\\b",
-			false,
+			true,
 		},
 		{
 			"a\b",
-			false,
+			true,
 		},
 		{
 			"a\v",
-			false,
+			true,
 		},
 		{
 			"a:\\b\\c",
-			false,
+			true,
 		},
 		{
 			"../a/../b",
-			true,
+			false,
 		},
 		{
 			"a//b",
@@ -270,26 +326,10 @@ func TestValidatePath(t *testing.T) {
 			"a/b/.",
 			true,
 		},
-		{
-			"a/*/b",
-			false,
-		},
-		{
-			"./*",
-			true,
-		},
-		{
-			"a/b\\c",
-			false,
-		},
 		{
 			"././././",
 			true,
 		},
-		{
-			"./!&^%$/#(@)/_-=+|<;>?:'\"/'`",
-			true,
-		},
 		{
 			"",
 			false,
@@ -298,16 +338,12 @@ func TestValidatePath(t *testing.T) {
 			"\t \n",
 			false,
 		},
-		{
-			"*",
-			false,
-		},
 	}
 
 	for _, c := range cases {
-		ret := validatePath(c.Path)
+		ret := validateFnConfigPath(c.Path)
 		if (ret == nil) != c.Valid {
-			t.Fatalf("returned value for path %s should be %t, got %t",
+			t.Fatalf("returned value for path %q should be %t, got %t",
 				c.Path, c.Valid, (ret == nil))
 		}
 	}
diff --git a/pkg/test/runner/README.md b/pkg/test/runner/README.md
index ed071ca1..b5e74d9f 100644
--- a/pkg/test/runner/README.md
+++ b/pkg/test/runner/README.md
@@ -22,6 +22,10 @@ in `.expected`:
     used to run the test. Default: `eval`.
   - `exitCode`: The expected exit code for the command. Default: 0.
   - `skip`: Runner will skip the test if `skip` is set to true. Default: false.
+  - `sequential`: This test case should be run sequentially. Default: false.
+  - `imagePullPolicy`: The image pull policy to be used. It can be set to one of
+    `always`, `ifNotPresent` and `never`. Default value is inherited from the
+    CLI flag.
   - `notIdempotent`: The functions and commands should be idempotent, but in
     some cases it's not doable. The runner will not run the command twice to
     check idempotent if this is set to true. Default: false.
diff --git a/pkg/test/runner/config.go b/pkg/test/runner/config.go
index bacdaa70..292044e5 100644
--- a/pkg/test/runner/config.go
+++ b/pkg/test/runner/config.go
@@ -20,6 +20,7 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/GoogleContainerTools/kpt/internal/fnruntime"
 	"github.com/GoogleContainerTools/kpt/internal/types"
 	"gopkg.in/yaml.v3"
 )
@@ -67,6 +68,14 @@ type TestCaseConfig struct {
 	// By default, tests are assumed to be idempotent, so it defaults to false.
 	NonIdempotent bool `json:"nonIdempotent,omitempty" yaml:"nonIdempotent,omitempty"`
 
+	// Sequential means should this test case be run sequentially. Default: false
+	Sequential bool `json:"sequential,omitempty" yaml:"sequential,omitempty"`
+
+	// ImagePullPolicy controls the image pulling behavior. It can be set to one
+	// of always, ifNotPresent and never. If unspecified, the default will be
+	// the same as the CLI flag.
+	ImagePullPolicy fnruntime.ImagePullPolicy `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
+
 	// Skip means should this test case be skipped. Default: false
 	Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"`
 
diff --git a/pkg/test/runner/runner.go b/pkg/test/runner/runner.go
index 998ccf1c..e06d460b 100644
--- a/pkg/test/runner/runner.go
+++ b/pkg/test/runner/runner.go
@@ -181,6 +181,9 @@ func (r *Runner) runFnEval() error {
 			if resultsDir != "" {
 				kptArgs = append(kptArgs, "--results-dir", resultsDir)
 			}
+			if r.testCase.Config.ImagePullPolicy != "" {
+				kptArgs = append(kptArgs, "--image-pull-policy", string(r.testCase.Config.ImagePullPolicy))
+			}
 			if r.testCase.Config.EvalConfig.Network {
 				kptArgs = append(kptArgs, "--network")
 			}
@@ -311,6 +314,10 @@ func (r *Runner) runFnRender() error {
 				kptArgs = append(kptArgs, "--results-dir", resultsDir)
 			}
 
+			if r.testCase.Config.ImagePullPolicy != "" {
+				kptArgs = append(kptArgs, "--image-pull-policy", string(r.testCase.Config.ImagePullPolicy))
+			}
+
 			if r.testCase.Config.DisableOutputTruncate {
 				kptArgs = append(kptArgs, "--truncate-output=false")
 			}
@@ -331,16 +338,16 @@ func (r *Runner) runFnRender() error {
 		if err != nil {
 			return err
 		}
-		// we passed result check, now we should break if the command error
-		// is expected
-		if fnErr != nil {
-			break
-		}
-
+		// we passed result check, now we should run teardown script and break
+		// if the command error is expected
 		err = r.runTearDownScript(pkgPath)
 		if err != nil {
 			return err
 		}
+		if fnErr != nil {
+			break
+		}
+
 	}
 	return nil
 }
diff --git a/thirdparty/cli-utils/printers/events/formatter_test.go b/thirdparty/cli-utils/printers/events/formatter_test.go
index d946eb0a..b995d29d 100644
--- a/thirdparty/cli-utils/printers/events/formatter_test.go
+++ b/thirdparty/cli-utils/printers/events/formatter_test.go
@@ -32,7 +32,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunNone,
 			event: event.ApplyEvent{
 				Operation:  event.Created,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep created",
@@ -41,7 +40,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunClient,
 			event: event.ApplyEvent{
 				Operation:  event.Configured,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep configured (dry-run)",
@@ -50,7 +48,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunServer,
 			event: event.ApplyEvent{
 				Operation:  event.Configured,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
 			},
 			expected: "cronjob.batch/my-cron configured (dry-run-server)",
@@ -58,42 +55,10 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 		"apply event with error should display the error": {
 			dryRunStrategy: common.DryRunServer,
 			event: event.ApplyEvent{
-				Operation:  event.Failed,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 				Error:      fmt.Errorf("this is a test error"),
 			},
-			expected: "deployment.apps/my-dep failed: this is a test error (dry-run-server)",
-		},
-		"completed event": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.ApplyEvent{
-				Type: event.ApplyEventCompleted,
-			},
-			applyStats: &list.ApplyStats{
-				ServersideApplied: 1,
-			},
-			statusCollector: &fakeCollector{
-				m: map[object.ObjMetadata]event.StatusEvent{
-					{ //nolint:gofmt
-						GroupKind: schema.GroupKind{
-							Group: "apps",
-							Kind:  "Deployment",
-						},
-						Namespace: "foo",
-						Name:      "my-dep",
-					}: {
-						Resource: &pollevent.ResourceStatus{
-							Status:  status.CurrentStatus,
-							Message: "Resource is Current",
-						},
-					},
-				},
-			},
-			expected: `
-1 resource(s) applied. 0 created, 0 unchanged, 0 configured, 0 failed, 1 serverside applied
-deployment.apps/my-dep is Current: Resource is Current
-`,
+			expected: "deployment.apps/my-dep apply failed: this is a test error (dry-run-server)",
 		},
 	}
 
@@ -101,7 +66,7 @@ deployment.apps/my-dep is Current: Resource is Current
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatApplyEvent(tc.event, tc.applyStats, tc.statusCollector)
+			err := formatter.FormatApplyEvent(tc.event)
 			assert.NoError(t, err)
 
 			assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
@@ -119,8 +84,15 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
 		"resource update with Current status": {
 			dryRunStrategy: common.DryRunNone,
 			event: event.StatusEvent{
-				Type: event.StatusEventResourceUpdate,
-				Resource: &pollevent.ResourceStatus{
+				Identifier: object.ObjMetadata{
+					GroupKind: schema.GroupKind{
+						Group: "apps",
+						Kind:  "Deployment",
+					},
+					Namespace: "foo",
+					Name:      "bar",
+				},
+				PollResourceInfo: &pollevent.ResourceStatus{
 					Identifier: object.ObjMetadata{
 						GroupKind: schema.GroupKind{
 							Group: "apps",
@@ -141,7 +113,7 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatStatusEvent(tc.event, tc.statusCollector)
+			err := formatter.FormatStatusEvent(tc.event)
 			assert.NoError(t, err)
 
 			assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
@@ -160,7 +132,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunNone,
 			event: event.PruneEvent{
 				Operation:  event.Pruned,
-				Type:       event.PruneEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep pruned",
@@ -169,7 +140,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunClient,
 			event: event.PruneEvent{
 				Operation:  event.PruneSkipped,
-				Type:       event.PruneEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep prune skipped (dry-run)",
@@ -177,30 +147,18 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 		"resource with prune error": {
 			dryRunStrategy: common.DryRunNone,
 			event: event.PruneEvent{
-				Type:       event.PruneEventFailed,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 				Error:      fmt.Errorf("this is a test"),
 			},
 			expected: "deployment.apps/my-dep prune failed: this is a test",
 		},
-		"prune event with completed status": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.PruneEvent{
-				Type: event.PruneEventCompleted,
-			},
-			pruneStats: &list.PruneStats{
-				Pruned:  1,
-				Skipped: 2,
-			},
-			expected: "1 resource(s) pruned, 2 skipped, 0 failed",
-		},
 	}
 
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatPruneEvent(tc.event, tc.pruneStats)
+			err := formatter.FormatPruneEvent(tc.event)
 			assert.NoError(t, err)
 
 			assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
@@ -219,49 +177,37 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
 		"resource deleted without no dryrun": {
 			dryRunStrategy: common.DryRunNone,
 			event: event.DeleteEvent{
-				Operation: event.Deleted,
-				Type:      event.DeleteEventResourceUpdate,
-				Object:    createObject("apps", "Deployment", "default", "my-dep"),
+				Operation:  event.Deleted,
+				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
+				Object:     createObject("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep deleted",
 		},
 		"resource skipped with client dryrun": {
 			dryRunStrategy: common.DryRunClient,
 			event: event.DeleteEvent{
-				Operation: event.DeleteSkipped,
-				Type:      event.DeleteEventResourceUpdate,
-				Object:    createObject("apps", "Deployment", "", "my-dep"),
+				Operation:  event.DeleteSkipped,
+				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
+				Object:     createObject("apps", "Deployment", "", "my-dep"),
 			},
 			expected: "deployment.apps/my-dep delete skipped (dry-run)",
 		},
 		"resource with delete error": {
 			dryRunStrategy: common.DryRunServer,
 			event: event.DeleteEvent{
-				Type:       event.DeleteEventFailed,
 				Object:     createObject("apps", "Deployment", "", "my-dep"),
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 				Error:      fmt.Errorf("this is a test"),
 			},
 			expected: "deployment.apps/my-dep deletion failed: this is a test (dry-run-server)",
 		},
-		"delete event with completed status": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.DeleteEvent{
-				Type: event.DeleteEventCompleted,
-			},
-			deleteStats: &list.DeleteStats{
-				Deleted: 1,
-				Skipped: 2,
-			},
-			expected: "1 resource(s) deleted, 2 skipped",
-		},
 	}
 
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatDeleteEvent(tc.event, tc.deleteStats)
+			err := formatter.FormatDeleteEvent(tc.event)
 			assert.NoError(t, err)
 
 			assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
@@ -292,11 +238,3 @@ func createIdentifier(group, kind, namespace, name string) object.ObjMetadata {
 		},
 	}
 }
-
-type fakeCollector struct {
-	m map[object.ObjMetadata]event.StatusEvent
-}
-
-func (f *fakeCollector) LatestStatus() map[object.ObjMetadata]event.StatusEvent {
-	return f.m
-}
diff --git a/thirdparty/cli-utils/printers/json/formatter_test.go b/thirdparty/cli-utils/printers/json/formatter_test.go
index 269bb00d..d80d69ae 100644
--- a/thirdparty/cli-utils/printers/json/formatter_test.go
+++ b/thirdparty/cli-utils/printers/json/formatter_test.go
@@ -21,17 +21,14 @@ import (
 
 func TestFormatter_FormatApplyEvent(t *testing.T) {
 	testCases := map[string]struct {
-		dryRunStrategy  common.DryRunStrategy
-		event           event.ApplyEvent
-		applyStats      *list.ApplyStats
-		statusCollector list.Collector
-		expected        []map[string]interface{}
+		dryRunStrategy common.DryRunStrategy
+		event          event.ApplyEvent
+		expected       []map[string]interface{}
 	}{
 		"resource created without dryrun": {
 			dryRunStrategy: common.DryRunNone,
 			event: event.ApplyEvent{
 				Operation:  event.Created,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: []map[string]interface{}{
@@ -51,7 +48,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunClient,
 			event: event.ApplyEvent{
 				Operation:  event.Configured,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 			},
 			expected: []map[string]interface{}{
@@ -71,7 +67,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunServer,
 			event: event.ApplyEvent{
 				Operation:  event.Configured,
-				Type:       event.ApplyEventResourceUpdate,
 				Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
 			},
 			expected: []map[string]interface{}{
@@ -87,63 +82,13 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 				},
 			},
 		},
-		"completed event": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.ApplyEvent{
-				Type: event.ApplyEventCompleted,
-			},
-			applyStats: &list.ApplyStats{
-				ServersideApplied: 1,
-			},
-			statusCollector: &fakeCollector{
-				m: map[object.ObjMetadata]event.StatusEvent{
-					{ //nolint:gofmt
-						GroupKind: schema.GroupKind{
-							Group: "apps",
-							Kind:  "Deployment",
-						},
-						Namespace: "foo",
-						Name:      "my-dep",
-					}: {
-						Resource: &pollevent.ResourceStatus{
-							Status:  status.CurrentStatus,
-							Message: "Resource is Current",
-						},
-					},
-				},
-			},
-			expected: []map[string]interface{}{
-				{
-					"configuredCount": 0,
-					"count":           1,
-					"createdCount":    0,
-					"eventType":       "completed",
-					"failedCount":     0,
-					"serverSideCount": 1,
-					"type":            "apply",
-					"unchangedCount":  0,
-					"timestamp":       "",
-				},
-				{
-					"eventType": "resourceStatus",
-					"group":     "apps",
-					"kind":      "Deployment",
-					"message":   "Resource is Current",
-					"name":      "my-dep",
-					"namespace": "foo",
-					"status":    "Current",
-					"timestamp": "",
-					"type":      "status",
-				},
-			},
-		},
 	}
 
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatApplyEvent(tc.event, tc.applyStats, tc.statusCollector)
+			err := formatter.FormatApplyEvent(tc.event)
 			assert.NoError(t, err)
 
 			objects := strings.Split(strings.TrimSpace(out.String()), "\n")
@@ -160,16 +105,22 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
 
 func TestFormatter_FormatStatusEvent(t *testing.T) {
 	testCases := map[string]struct {
-		dryRunStrategy  common.DryRunStrategy
+		previewStrategy common.DryRunStrategy
 		event           event.StatusEvent
-		statusCollector list.Collector
 		expected        map[string]interface{}
 	}{
 		"resource update with Current status": {
-			dryRunStrategy: common.DryRunNone,
+			previewStrategy: common.DryRunNone,
 			event: event.StatusEvent{
-				Type: event.StatusEventResourceUpdate,
-				Resource: &pollevent.ResourceStatus{
+				Identifier: object.ObjMetadata{
+					GroupKind: schema.GroupKind{
+						Group: "apps",
+						Kind:  "Deployment",
+					},
+					Namespace: "foo",
+					Name:      "bar",
+				},
+				PollResourceInfo: &pollevent.ResourceStatus{
 					Identifier: object.ObjMetadata{
 						GroupKind: schema.GroupKind{
 							Group: "apps",
@@ -199,8 +150,8 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
-			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatStatusEvent(tc.event, tc.statusCollector)
+			formatter := NewFormatter(ioStreams, tc.previewStrategy)
+			err := formatter.FormatStatusEvent(tc.event)
 			assert.NoError(t, err)
 
 			assertOutput(t, tc.expected, out.String())
@@ -219,7 +170,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunNone,
 			event: event.PruneEvent{
 				Operation:  event.Pruned,
-				Type:       event.PruneEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: map[string]interface{}{
@@ -237,7 +187,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunClient,
 			event: event.PruneEvent{
 				Operation:  event.PruneSkipped,
-				Type:       event.PruneEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 			},
 			expected: map[string]interface{}{
@@ -251,30 +200,13 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
 				"type":      "prune",
 			},
 		},
-		"prune event with completed status": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.PruneEvent{
-				Type: event.PruneEventCompleted,
-			},
-			pruneStats: &list.PruneStats{
-				Pruned:  1,
-				Skipped: 2,
-			},
-			expected: map[string]interface{}{
-				"eventType": "completed",
-				"skipped":   2,
-				"pruned":    1,
-				"timestamp": "",
-				"type":      "prune",
-			},
-		},
 	}
 
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatPruneEvent(tc.event, tc.pruneStats)
+			err := formatter.FormatPruneEvent(tc.event)
 			assert.NoError(t, err)
 
 			assertOutput(t, tc.expected, out.String())
@@ -294,7 +226,6 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunNone,
 			event: event.DeleteEvent{
 				Operation:  event.Deleted,
-				Type:       event.DeleteEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
 			},
 			expected: map[string]interface{}{
@@ -312,7 +243,6 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
 			dryRunStrategy: common.DryRunClient,
 			event: event.DeleteEvent{
 				Operation:  event.DeleteSkipped,
-				Type:       event.DeleteEventResourceUpdate,
 				Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
 			},
 			expected: map[string]interface{}{
@@ -326,30 +256,13 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
 				"type":      "delete",
 			},
 		},
-		"delete event with completed status": {
-			dryRunStrategy: common.DryRunNone,
-			event: event.DeleteEvent{
-				Type: event.DeleteEventCompleted,
-			},
-			deleteStats: &list.DeleteStats{
-				Deleted: 1,
-				Skipped: 2,
-			},
-			expected: map[string]interface{}{
-				"deleted":   1,
-				"eventType": "completed",
-				"skipped":   2,
-				"timestamp": "",
-				"type":      "delete",
-			},
-		},
 	}
 
 	for tn, tc := range testCases {
 		t.Run(tn, func(t *testing.T) {
 			ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
 			formatter := NewFormatter(ioStreams, tc.dryRunStrategy)
-			err := formatter.FormatDeleteEvent(tc.event, tc.deleteStats)
+			err := formatter.FormatDeleteEvent(tc.event)
 			assert.NoError(t, err)
 
 			assertOutput(t, tc.expected, out.String())
@@ -394,11 +307,3 @@ func createIdentifier(group, kind, namespace, name string) object.ObjMetadata {
 		},
 	}
 }
-
-type fakeCollector struct {
-	m map[object.ObjMetadata]event.StatusEvent
-}
-
-func (f *fakeCollector) LatestStatus() map[object.ObjMetadata]event.StatusEvent {
-	return f.m
-}
diff --git a/thirdparty/cli-utils/printers/table/collector_test.go b/thirdparty/cli-utils/printers/table/collector_test.go
index 2a4511e6..2a561821 100644
--- a/thirdparty/cli-utils/printers/table/collector_test.go
+++ b/thirdparty/cli-utils/printers/table/collector_test.go
@@ -35,15 +35,15 @@ const testMessage = "test message for ResourceStatus"
 
 func TestResourceStateCollector_New(t *testing.T) {
 	testCases := map[string]struct {
-		resourceGroups []event.ResourceGroup
+		resourceGroups []event.ActionGroup
 		resourceInfos  map[object.ObjMetadata]*ResourceInfo
 	}{
 		"no resources": {
-			resourceGroups: []event.ResourceGroup{},
+			resourceGroups: []event.ActionGroup{},
 			resourceInfos:  map[object.ObjMetadata]*ResourceInfo{},
 		},
 		"several resources for apply": {
-			resourceGroups: []event.ResourceGroup{
+			resourceGroups: []event.ActionGroup{
 				{
 					Action: event.ApplyAction,
 					Identifiers: []object.ObjMetadata{
@@ -61,7 +61,7 @@ func TestResourceStateCollector_New(t *testing.T) {
 			},
 		},
 		"several resources for prune": {
-			resourceGroups: []event.ResourceGroup{
+			resourceGroups: []event.ActionGroup{
 				{
 					Action: event.ApplyAction,
 					Identifiers: []object.ObjMetadata{
@@ -104,39 +104,30 @@ func TestResourceStateCollector_New(t *testing.T) {
 
 func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
 	testCases := map[string]struct {
-		resourceGroups []event.ResourceGroup
+		resourceGroups []event.ActionGroup
 		statusEvent    event.StatusEvent
 	}{
 		"nil StatusEvent.Resource does not crash": {
-			resourceGroups: []event.ResourceGroup{},
+			resourceGroups: []event.ActionGroup{},
 			statusEvent: event.StatusEvent{
-				Type:     event.StatusEventResourceUpdate,
-				Resource: nil,
-			},
-		},
-		"type StatusEventCompleted does nothing": {
-			resourceGroups: []event.ResourceGroup{},
-			statusEvent: event.StatusEvent{
-				Type:     event.StatusEventCompleted,
 				Resource: nil,
 			},
 		},
 		"unfound Resource identifier does not crash": {
-			resourceGroups: []event.ResourceGroup{
+			resourceGroups: []event.ActionGroup{
 				{
 					Action:      event.ApplyAction,
 					Identifiers: []object.ObjMetadata{depID},
 				},
 			},
 			statusEvent: event.StatusEvent{
-				Type: event.StatusEventResourceUpdate,
-				Resource: &pe.ResourceStatus{
+				PollResourceInfo: &pe.ResourceStatus{
 					Identifier: customID, // Does not match identifier in resourceGroups
 				},
 			},
 		},
 		"basic status event for applying two resources updates resourceStatus": {
-			resourceGroups: []event.ResourceGroup{
+			resourceGroups: []event.ActionGroup{
 				{
 					Action: event.ApplyAction,
 					Identifiers: []object.ObjMetadata{
@@ -145,15 +136,14 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
 				},
 			},
 			statusEvent: event.StatusEvent{
-				Type: event.StatusEventResourceUpdate,
-				Resource: &pe.ResourceStatus{
+				PollResourceInfo: &pe.ResourceStatus{
 					Identifier: depID,
 					Message:    testMessage,
 				},
 			},
 		},
 		"several resources for prune": {
-			resourceGroups: []event.ResourceGroup{
+			resourceGroups: []event.ActionGroup{
 				{
 					Action: event.ApplyAction,
 					Identifiers: []object.ObjMetadata{
@@ -168,8 +158,7 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
 				},
 			},
 			statusEvent: event.StatusEvent{
-				Type: event.StatusEventResourceUpdate,
-				Resource: &pe.ResourceStatus{
+				PollResourceInfo: &pe.ResourceStatus{
 					Identifier: depID,
 					Message:    testMessage,
 				},
@@ -186,7 +175,7 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
 				resourceInfo, found := rsc.resourceInfos[id]
 				if found {
 					// Validate the ResourceStatus was set from StatusEvent
-					if resourceInfo.resourceStatus != tc.statusEvent.Resource {
+					if resourceInfo.resourceStatus != tc.statusEvent.PollResourceInfo {
 						t.Errorf("status event not processed for %s", id)
 					}
 				}
@@ -199,5 +188,5 @@ func getID(e event.StatusEvent) (object.ObjMetadata, bool) {
 	if e.Resource == nil {
 		return object.ObjMetadata{}, false
 	}
-	return e.Resource.Identifier, true
+	return e.Identifier, true
 }
diff --git a/thirdparty/cli-utils/printers/table/printer_test.go b/thirdparty/cli-utils/printers/table/printer_test.go
index ca6a4f2e..7af0b5b0 100644
--- a/thirdparty/cli-utils/printers/table/printer_test.go
+++ b/thirdparty/cli-utils/printers/table/printer_test.go
@@ -35,7 +35,7 @@ func TestActionColumnDef(t *testing.T) {
 		"applied": {
 			resource: &ResourceInfo{
 				ResourceAction: event.ApplyAction,
-				ApplyOpResult:  &createdOpResult,
+				ApplyOpResult:  createdOpResult,
 			},
 			columnWidth:    15,
 			expectedOutput: "Created",
@@ -43,7 +43,7 @@ func TestActionColumnDef(t *testing.T) {
 		"pruned": {
 			resource: &ResourceInfo{
 				ResourceAction: event.PruneAction,
-				PruneOpResult:  &prunedOpResult,
+				PruneOpResult:  prunedOpResult,
 			},
 			columnWidth:    15,
 			expectedOutput: "Pruned",
@@ -51,7 +51,7 @@ func TestActionColumnDef(t *testing.T) {
 		"trimmed output": {
 			resource: &ResourceInfo{
 				ResourceAction: event.ApplyAction,
-				ApplyOpResult:  &createdOpResult,
+				ApplyOpResult:  createdOpResult,
 			},
 			columnWidth:    5,
 			expectedOutput: "Creat",
diff --git a/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go b/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go
index 708ab8a0..4ee3f623 100644
--- a/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go
+++ b/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/GoogleContainerTools/kpt/internal/fnruntime"
 	"github.com/GoogleContainerTools/kpt/thirdparty/kyaml/runfn"
 	"github.com/spf13/cobra"
 	"github.com/stretchr/testify/assert"
@@ -182,6 +183,7 @@ apiVersion: v1
 			expectedStruct: &runfn.RunFns{
 				Path:                  "dir",
 				ResultsDir:            "foo/",
+				ImagePullPolicy:       fnruntime.AlwaysPull,
 				Env:                   []string{},
 				ContinueOnEmptyResult: true,
 				Ctx:                   context.TODO(),
@@ -218,6 +220,7 @@ apiVersion: v1
 			path: "dir",
 			expectedStruct: &runfn.RunFns{
 				Path:                  "dir",
+				ImagePullPolicy:       fnruntime.AlwaysPull,
 				Env:                   []string{"FOO=BAR", "BAR"},
 				ContinueOnEmptyResult: true,
 				Ctx:                   context.TODO(),
@@ -240,6 +243,7 @@ apiVersion: v1
 			expectedStruct: &runfn.RunFns{
 				Path:                  "dir",
 				AsCurrentUser:         true,
+				ImagePullPolicy:       fnruntime.AlwaysPull,
 				Env:                   []string{},
 				ContinueOnEmptyResult: true,
 				Ctx:                   context.TODO(),
diff --git a/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go b/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go
index d0cff4e1..35e570b7 100644
--- a/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go
+++ b/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go
@@ -83,12 +83,12 @@ spec:
 		return
 	}
 
-	if !assert.Equal(t, `.
+	if !assert.Equal(t, fmt.Sprintf(`%s
 ├── [f1.yaml]  Abstraction foo
 ├── [f1.yaml]  Deployment foo
 ├── [f1.yaml]  Service foo
 └── [f2.yaml]  Deployment bar
-`, b.String()) {
+`, filepath.Base(d)), b.String()) {
 		return
 	}
 }
@@ -163,7 +163,7 @@ spec:
 ├── [f1.yaml]  Deployment foo
 ├── [f1.yaml]  Service foo
 └── [f2.yaml]  Deployment bar
-`, d), b.String()) {
+`, filepath.Base(d)), b.String()) {
 		return
 	}
 }
@@ -209,7 +209,7 @@ resources:
 
 	if !assert.Equal(t, fmt.Sprintf(`%s
 └── [f2.yaml]  Deployment bar
-`, d), b.String()) {
+`, filepath.Base(d)), b.String()) {
 		return
 	}
 }
@@ -305,15 +305,15 @@ openAPI:
 		return
 	}
 
-	if !assert.Equal(t, fmt.Sprintf(`PKG: %s
+	if !assert.Equal(t, fmt.Sprintf(`Package %q
 ├── [Kptfile]  Kptfile mainpkg
 ├── [f1.yaml]  Abstraction foo
 ├── [f1.yaml]  Deployment foo
 ├── [f1.yaml]  Service foo
-└── PKG: subpkg
+└── Package "subpkg"
     ├── [Kptfile]  Kptfile subpkg
     └── [f2.yaml]  Deployment bar
-`, d), b.String()) {
+`, filepath.Base(d)), b.String()) {
 		return
 	}
 }
@@ -389,124 +389,13 @@ metadata:
 		return
 	}
 
-	if !assert.Equal(t, `PKG: Mainpkg
+	if !assert.Equal(t, `Package "Mainpkg"
 ├── [Kptfile]  Kptfile Mainpkg
 ├── [f1.yaml]  Deployment foo
-└── PKG: Subpkg
+└── Package "Subpkg"
     ├── [Kptfile]  Kptfile Subpkg
     └── [f2.yaml]  Deployment bar
 `, b.String()) {
 		return
 	}
 }
-
-func TestTreeCommand_stdin(t *testing.T) {
-	// fmt the files
-	b := &bytes.Buffer{}
-	r := GetTreeRunner("")
-	r.Command.SetArgs([]string{"-"})
-	r.Command.SetIn(bytes.NewBufferString(`apiVersion: extensions/v1
-kind: Deployment
-metadata:
-  labels:
-    app: nginx2
-  name: foo3
-  namespace: default
-  annotations:
-    app: nginx2
-    config.kubernetes.io/path: f1.yaml
-spec:
-  replicas: 1
----
-apiVersion: extensions/v1
-kind: Deployment
-metadata:
-  labels:
-    app: nginx2
-  name: foo3
-  namespace: default
-  annotations:
-    app: nginx2
-    config.kubernetes.io/path: f1.yaml
-spec:
-  replicas: 1
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: nginx2
-  name: foo3
-  namespace: default
-  annotations:
-    app: nginx2
-    config.kubernetes.io/path: f1.yaml
-spec:
-  replicas: 1
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: nginx2
-  name: foo2
-  namespace: default2
-  annotations:
-    app: nginx2
-    config.kubernetes.io/path: f1.yaml
-spec:
-  replicas: 1
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: nginx3
-  name: foo
-  namespace: default
-  annotations:
-    app: nginx3
-    config.kubernetes.io/path: f1.yaml
-spec:
-  replicas: 1
----
-kind: Deployment
-metadata:
-  labels:
-    app: nginx
-  annotations:
-    app: nginx
-    config.kubernetes.io/path: bar-package/f2.yaml
-  name: bar
-spec:
-  replicas: 3
----
-kind: Service
-metadata:
-  name: foo
-  namespace: default
-  annotations:
-    app: nginx
-    config.kubernetes.io/path: f1.yaml
-spec:
-  selector:
-    app: nginx
-`))
-	r.Command.SetOut(b)
-	if !assert.NoError(t, r.Command.Execute()) {
-		return
-	}
-
-	if !assert.Equal(t, `.
-├── [f1.yaml]  Deployment default/foo
-├── [f1.yaml]  Service default/foo
-├── [f1.yaml]  Deployment default/foo3
-├── [f1.yaml]  Deployment default/foo3
-├── [f1.yaml]  Deployment default/foo3
-├── [f1.yaml]  Deployment default2/foo2
-└── bar-package
-    └── [f2.yaml]  Deployment bar
-`, b.String()) {
-		return
-	}
-}