Repository: knative/client (go)

Issue:
Add a flag to set RunAsUser 
<!-- If you need to report a security issue with Knative, send an email to knative-security@googlegroups.com. -->
## In what area(s)?

<!-- Remove the '> ' to select -->

<!--
Classifications:
> /kind good-first-issue
/kind feature
> /kind proposal
> /kind doc
> /kind cleanup
-->

## Describe the feature:

RunAsUser is a must for clusters which has pod security policies to check for non root users. Currently kn cli doesn't have an option to set the user running the container. 
It will be a handy feature for clusters which have psp , afaik most organizations have it.

```
kn service create --image=foo --run-as-user 1001
```


Pull request:
Add --user flag for service create and update
<!--
Request Prow to automatically lint any go code in this PR:

/lint
-->

Fixes #678 

## Proposed Changes

* Add run as user flag
* add tests for create and update

<!--
Release Note:

In the following cases, add a short description of PR to the unreleased section in CHANGELOG.adoc:

- 🎁 Add new feature
- 🐛 Fix bug
- 🧽 Update or clean up current behaviour
- 🗑️ Remove feature or internal logic

See other entries in CHANGELOG.adoc as an example.

PLEASE DON'T ADD THAT LINE HERE IN THE PULL-REQUEST DESCRIPTION BUT DIRECTLY IN CHANGELOG.ADOC AND ADD CHANGELOG.ADOC AS PART OF YOUR PULL-REQUEST.
-->


Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 037d98a2..d97c1ba2 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -20,6 +20,10 @@
 |===
 | | Description | PR
 
+| 🧽
+| Allow configuration sink prefixes
+| https://github.com/knative/client/pull/571[#571]
+
 | 🧽
 | Support multiple revisions on `kn revision delete`
 | https://github.com/knative/client/pull/657[#657]
@@ -55,6 +59,10 @@
 | 🧽
 | Add `--wait` and `--no-wait` to service delete operation. Change service delete to wait by default.   
 | https://github.com/knative/client/pull/682[#682]
+
+| 🎁
+| Add `--user` flag for specifying the user id to run the container
+| https://github.com/knative/client/pull/679[#679]
 |===
 
 ## v0.12.0 (2020-01-29)
diff --git a/docs/README.md b/docs/README.md
index 7d346cf7..57e777e9 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -31,14 +31,14 @@ You'll need a `kubectl`-style config file to connect to your cluster.
  * Instructions for Google [GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl)
  * Instructions for Amazon [EKS](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html)
  * Instructions for IBM [IKS](https://cloud.ibm.com/docs/containers?topic=containers-getting-started)
- * Instructions for Red Hat [OpenShift](https://docs.openshift.com/container-platform/4.1/cli_reference/administrator-cli-commands.html#create-kubeconfig).
- * Or contact your cluster administrator.
+ * Instructions for Red Hat [OpenShift](https://docs.openshift.com/container-platform/4.1/cli_reference/administrator-cli-commands.html#create-kubeconfig)
+ * Or contact your cluster administrator
 
 `kn` will pick up your `kubectl` config file in the default location of `$HOME/.kube/config`. You can specify an alternate kubeconfig connection file with `--kubeconfig`, or the env var `$KUBECONFIG`, for any command.
 
 ## Kn Config
 
-There are a set of configuration parameters you can setup to better customize `kn`. In particular, you can specify where your `kn` plugins are located and how they are found. The `kn` configuration file is meant to capture these configuration options. Let's explore this file's location, and the options you are able to change with it.
+There are a set of configuration parameters you can setup to better customize `kn`. For example, you can specify where your `kn` plugins are located and how they are found, and you can specify the prefix for your addressable `sink` objects. The `kn` configuration file is meant to capture these configuration options. Let's explore this file's location, and the options you are able to change with it.
 
 ### Location
 
@@ -46,18 +46,30 @@ The default location `kn` looks for config is under the home directory of the us
 
 ### Options
 
-There are two options you can specify in the `kn` config file and they are related to how `kn` locates plugins.
+Below are the options you can specify in the `kn` config file.
 
 1. `pluginsDir` which is the same as the persistent flag `--plugins-dir` and specifies the kn plugins directory. It defaults to: `~/.kn/plugins`. By using the persistent flag (when you issue a command) or by specifying the value in the `kn` config, a user can select which directory to find `kn` plugins. It can be any directory that is visible to the user.
 
 2. `lookupPluginsInPath` which is the same as the persistent flag `--lookup-plugins-in-path` and specficies if `kn` should look for plugins anywhere in the specified `PATH` environment variable. This is a boolean configuration option and the default value is `false`.
 
+3. `sink` defines your prefix to refer to Kubernetes addressable resources. To configure a sink prefix, define following in the config file:
+    1. `prefix`: Prefix you want to describe your sink as. `service` or `svc` (`serving.knative.dev/v1`) and `broker` (`eventing.knative.dev/v1alpha1`) are predefined prefixes in `kn`. These predefined prefixes can be overridden by values in configuration file. 
+    2. `group`: The APIGroup of Kubernetes resource.
+    3. `version`: The version of Kubernetes resources.
+    4. `resource`: The plural name of Kubernetes resources (for example: services). 
+
 For example, the following `kn` config will look for `kn` plugins in the user's `PATH` and also execute plugin in `~/.kn/plugins`.
+It also defines a sink prefix `myprefix` which refers to `brokers` in `eventing.knative.dev/v1alpha1`. With this configuration, you can use `myprefix:default` to describe a Broker `default` in `kn` command line.
 
 ```bash
 cat ~/.kn/config.yaml
 lookupPluginsInPath: true
 pluginsdir: ~/.kn/plugins
+sink:
+- prefix: myprefix
+  group: eventing.knative.dev
+  version: v1alpha1
+  resource: brokers
 ```
 ----------------------------------------------------------
 
diff --git a/docs/cmd/kn_service_create.md b/docs/cmd/kn_service_create.md
index ce4f4a99..041a0767 100644
--- a/docs/cmd/kn_service_create.md
+++ b/docs/cmd/kn_service_create.md
@@ -70,6 +70,7 @@ kn service create NAME --image IMAGE [flags]
       --requests-memory string    The requested memory (e.g., 64Mi).
       --revision-name string      The revision name to set. Must start with the service name and a dash as a prefix. Empty revision name will result in the server generating a name for the revision. Accepts golang templates, allowing {{.Service}} for the service name, {{.Generation}} for the generation, and {{.Random [n]}} for n random consonants. (default "{{.Service}}-{{.Random 5}}-{{.Generation}}")
       --service-account string    Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
+      --user int                  The user ID to run the container (e.g., 1001).
       --volume stringArray        Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
       --wait-timeout int          Seconds to wait before giving up on waiting for service to be ready. (default 600)
 ```
diff --git a/docs/cmd/kn_service_update.md b/docs/cmd/kn_service_update.md
index 0792fa97..2898a2d7 100644
--- a/docs/cmd/kn_service_update.md
+++ b/docs/cmd/kn_service_update.md
@@ -68,6 +68,7 @@ kn service update NAME [flags]
       --tag strings               Set tag (format: --tag revisionRef=tagName) where revisionRef can be a revision or '@latest' string representing latest ready revision. This flag can be specified multiple times.
       --traffic strings           Set traffic distribution (format: --traffic revisionRef=percent) where revisionRef can be a revision or a tag or '@latest' string representing latest ready revision. This flag can be given multiple times with percent summing up to 100%.
       --untag strings             Untag revision (format: --untag tagName). This flag can be specified multiple times.
+      --user int                  The user ID to run the container (e.g., 1001).
       --volume stringArray        Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
       --wait-timeout int          Seconds to wait before giving up on waiting for service to be ready. (default 600)
 ```
diff --git a/pkg/kn/commands/flags/sink.go b/pkg/kn/commands/flags/sink.go
index 2a6ce98f..4bc673e4 100644
--- a/pkg/kn/commands/flags/sink.go
+++ b/pkg/kn/commands/flags/sink.go
@@ -25,6 +25,7 @@ import (
 	duckv1 "knative.dev/pkg/apis/duck/v1"
 
 	clientdynamic "knative.dev/client/pkg/dynamic"
+	"knative.dev/client/pkg/kn/commands"
 )
 
 type SinkFlags struct {
@@ -55,6 +56,17 @@ var SinkPrefixes = map[string]schema.GroupVersionResource{
 	},
 }
 
+func ConfigSinkPrefixes(prefixes []commands.SinkPrefixConfig) {
+	for _, p := range prefixes {
+		//user configration might override the default configuration
+		SinkPrefixes[p.Prefix] = schema.GroupVersionResource{
+			Resource: p.Resource,
+			Group:    p.Group,
+			Version:  p.Version,
+		}
+	}
+}
+
 // ResolveSink returns the Destination referred to by the flags in the acceptor.
 // It validates that any object the user is referring to exists.
 func (i *SinkFlags) ResolveSink(knclient clientdynamic.KnDynamicClient, namespace string) (*duckv1.Destination, error) {
diff --git a/pkg/kn/commands/service/configuration_edit_flags.go b/pkg/kn/commands/service/configuration_edit_flags.go
index 8731eec0..127a342a 100644
--- a/pkg/kn/commands/service/configuration_edit_flags.go
+++ b/pkg/kn/commands/service/configuration_edit_flags.go
@@ -52,6 +52,7 @@ type ConfigurationEditFlags struct {
 	ServiceAccountName         string
 	ImagePullSecrets           string
 	Annotations                []string
+	User                       int64
 
 	// Preferences about how to do the action.
 	LockToDigest         bool
@@ -189,6 +190,8 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
 		"",
 		"Image pull secret to set. An empty argument (\"\") clears the pull secret. The referenced secret must exist in the service's namespace.")
 	p.markFlagMakesRevision("pull-secret")
+	command.Flags().Int64VarP(&p.User, "user", "", 0, "The user ID to run the container (e.g., 1001).")
+	p.markFlagMakesRevision("user")
 }
 
 // AddUpdateFlags adds the flags specific to update.
@@ -396,6 +399,10 @@ func (p *ConfigurationEditFlags) Apply(
 		servinglib.UpdateImagePullSecrets(template, p.ImagePullSecrets)
 	}
 
+	if cmd.Flags().Changed("user") {
+		servinglib.UpdateUser(template, p.User)
+	}
+
 	return nil
 }
 
diff --git a/pkg/kn/commands/types.go b/pkg/kn/commands/types.go
index 196e49df..1ddaee18 100644
--- a/pkg/kn/commands/types.go
+++ b/pkg/kn/commands/types.go
@@ -48,6 +48,15 @@ var Cfg Config = Config{
 type Config struct {
 	PluginsDir    string
 	LookupPlugins *bool
+	SinkPrefixes  []SinkPrefixConfig
+}
+
+// SinkPrefixConfig is the struct of sink prefix config in kn config
+type SinkPrefixConfig struct {
+	Prefix   string
+	Resource string
+	Group    string
+	Version  string
 }
 
 // KnParams for creating commands. Useful for inserting mocks for testing.
diff --git a/pkg/kn/core/root.go b/pkg/kn/core/root.go
index 4f5700cd..d8581365 100644
--- a/pkg/kn/core/root.go
+++ b/pkg/kn/core/root.go
@@ -31,6 +31,7 @@ import (
 	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
 	"knative.dev/client/pkg/kn/commands"
 	"knative.dev/client/pkg/kn/commands/completion"
+	cmdflags "knative.dev/client/pkg/kn/commands/flags"
 	"knative.dev/client/pkg/kn/commands/plugin"
 	"knative.dev/client/pkg/kn/commands/revision"
 	"knative.dev/client/pkg/kn/commands/route"
@@ -126,7 +127,10 @@ func NewKnCommand(params ...commands.KnParams) *cobra.Command {
 		SilenceErrors: true,
 
 		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-			initConfigFlags()
+			err := initConfigFlags()
+			if err != nil {
+				return err
+			}
 			return flags.ReconcileBoolFlags(cmd.Flags())
 		},
 	}
@@ -225,7 +229,7 @@ func initConfig() {
 	}
 }
 
-func initConfigFlags() {
+func initConfigFlags() error {
 	if viper.IsSet("plugins-dir") {
 		commands.Cfg.PluginsDir = viper.GetString("plugins-dir")
 	}
@@ -234,6 +238,18 @@ func initConfigFlags() {
 	var aBool bool
 	aBool = viper.GetBool("lookup-plugins")
 	commands.Cfg.LookupPlugins = &aBool
+
+	// set the Cfg.SinkPrefixes from viper if sink is configured
+	if viper.IsSet("sink") {
+		err := viper.UnmarshalKey("sink", &commands.Cfg.SinkPrefixes)
+		if err != nil {
+			return fmt.Errorf("unable to parse sink prefixes configuration in file %s because of %v",
+				viper.ConfigFileUsed(), err)
+		}
+		cmdflags.ConfigSinkPrefixes(commands.Cfg.SinkPrefixes)
+	}
+
+	return nil
 }
 
 func extractKnPluginFlags(args []string) (string, bool, error) {
diff --git a/pkg/serving/config_changes.go b/pkg/serving/config_changes.go
index a265243b..7afad5c2 100644
--- a/pkg/serving/config_changes.go
+++ b/pkg/serving/config_changes.go
@@ -337,6 +337,18 @@ func UpdateContainerPort(template *servingv1.RevisionTemplateSpec, port int32) e
 	return nil
 }
 
+// UpdateRunAsUser updates container with a given user id
+func UpdateUser(template *servingv1.RevisionTemplateSpec, user int64) error {
+	container, err := ContainerOfRevisionTemplate(template)
+	if err != nil {
+		return err
+	}
+	container.SecurityContext = &corev1.SecurityContext{
+		RunAsUser: &user,
+	}
+	return nil
+}
+
 // UpdateResources updates resources as requested
 func UpdateResources(template *servingv1.RevisionTemplateSpec, requestsResourceList corev1.ResourceList, limitsResourceList corev1.ResourceList) error {
 	container, err := ContainerOfRevisionTemplate(template)


Test patch:
diff --git a/pkg/kn/commands/service/create_mock_test.go b/pkg/kn/commands/service/create_mock_test.go
index 395f654f..37500b55 100644
--- a/pkg/kn/commands/service/create_mock_test.go
+++ b/pkg/kn/commands/service/create_mock_test.go
@@ -32,6 +32,7 @@ import (
 	"knative.dev/client/pkg/wait"
 
 	"knative.dev/client/pkg/util"
+	"knative.dev/pkg/ptr"
 )
 
 func TestServiceCreateImageMock(t *testing.T) {
@@ -385,6 +386,29 @@ func TestServiceCreateWithMountSecret(t *testing.T) {
 	r.Validate()
 }
 
+func TestServiceCreateWithUser(t *testing.T) {
+	client := knclient.NewMockKnServiceClient(t)
+
+	r := client.Recorder()
+	r.GetService("foo", nil, errors.NewNotFound(servingv1.Resource("service"), "foo"))
+
+	service := getService("foo")
+
+	template := &service.Spec.Template
+	template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
+		RunAsUser: ptr.Int64(int64(1001)),
+	}
+	template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
+	template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
+	r.CreateService(service, nil)
+
+	output, err := executeServiceCommand(client, "create", "foo", "--image", "gcr.io/foo/bar:baz", "--user", "1001", "--no-wait", "--revision-name=")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, "created", "foo", "default"))
+
+	r.Validate()
+}
+
 func getService(name string) *servingv1.Service {
 	service := &servingv1.Service{
 		ObjectMeta: metav1.ObjectMeta{
diff --git a/pkg/kn/commands/service/service_update_mock_test.go b/pkg/kn/commands/service/service_update_mock_test.go
index 1f9688e8..1c216a71 100644
--- a/pkg/kn/commands/service/service_update_mock_test.go
+++ b/pkg/kn/commands/service/service_update_mock_test.go
@@ -26,6 +26,7 @@ import (
 	clientserving "knative.dev/client/pkg/serving"
 	clientservingv1 "knative.dev/client/pkg/serving/v1"
 	"knative.dev/client/pkg/util"
+	"knative.dev/pkg/ptr"
 )
 
 func TestServiceUpdateEnvMock(t *testing.T) {
@@ -1427,3 +1428,48 @@ func TestServiceUpdateWithRemovingMount(t *testing.T) {
 
 	r.Validate()
 }
+
+func TestServiceUpdateUser(t *testing.T) {
+	client := clientservingv1.NewMockKnServiceClient(t)
+	svcName := "svc1"
+	newService := getService(svcName)
+	template := &newService.Spec.Template
+	template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
+	template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
+		RunAsUser: ptr.Int64(int64(1001)),
+	}
+	template.ObjectMeta.Annotations = map[string]string{
+		clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
+	}
+
+	updatedService := getService(svcName)
+	template = &updatedService.Spec.Template
+	template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
+	template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
+		RunAsUser: ptr.Int64(int64(1002)),
+	}
+	template.ObjectMeta.Annotations = map[string]string{
+		clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
+	}
+
+	r := client.Recorder()
+	recordServiceUpdateWithSuccess(r, svcName, newService, updatedService)
+
+	output, err := executeServiceCommand(client,
+		"create", svcName, "--image", "gcr.io/foo/bar:baz",
+		"--user", "1001",
+		"--no-wait", "--revision-name=",
+	)
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, "created", svcName, "default"))
+
+	output, err = executeServiceCommand(client,
+		"update", svcName,
+		"--user", "1002",
+		"--no-wait", "--revision-name=",
+	)
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, "updated", svcName, "default"))
+
+	r.Validate()
+}
diff --git a/pkg/serving/config_changes_test.go b/pkg/serving/config_changes_test.go
index e43c27d0..f17fd170 100644
--- a/pkg/serving/config_changes_test.go
+++ b/pkg/serving/config_changes_test.go
@@ -300,6 +300,10 @@ func checkPortUpdate(t *testing.T, template *servingv1.RevisionTemplateSpec, por
 	}
 }
 
+func checkUserUpdate(t *testing.T, template *servingv1.RevisionTemplateSpec, user *int64) {
+	assert.DeepEqual(t, template.Spec.Containers[0].SecurityContext.RunAsUser, user)
+}
+
 func TestUpdateEnvVarsBoth(t *testing.T) {
 	template, container := getRevisionTemplate()
 	container.Env = []corev1.EnvVar{
@@ -649,6 +653,20 @@ func TestGenerateVolumeName(t *testing.T) {
 	}
 }
 
+func TestUpdateUser(t *testing.T) {
+	template, _ := getRevisionTemplate()
+	err := UpdateUser(template, int64(1001))
+	assert.NilError(t, err)
+
+	checkUserUpdate(t, template, ptr.Int64(int64(1001)))
+
+	template.Spec.Containers[0].SecurityContext.RunAsUser = ptr.Int64(int64(1002))
+	err = UpdateUser(template, int64(1002))
+	assert.NilError(t, err)
+
+	checkUserUpdate(t, template, ptr.Int64(int64(1002)))
+}
+
 //
 // =========================================================================================================
 
diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh
index 552985ce..f5d02397 100755
--- a/test/e2e-tests.sh
+++ b/test/e2e-tests.sh
@@ -41,5 +41,5 @@ initialize $@
 
 header "Running tests for Knative Serving $KNATIVE_SERVING_VERSION and Eventing $KNATIVE_EVENTING_VERSION"
 
-go_test_e2e -timeout=30m ./test/e2e || fail_test
+go_test_e2e -timeout=45m ./test/e2e || fail_test
 success
diff --git a/test/e2e/sinkprefix_test.go b/test/e2e/sinkprefix_test.go
new file mode 100644
index 00000000..32d09b44
--- /dev/null
+++ b/test/e2e/sinkprefix_test.go
@@ -0,0 +1,92 @@
+// Copyright 2020 The Knative Authors
+
+// 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 im
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build e2e
+
+package e2e
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"gotest.tools/assert"
+	"knative.dev/client/pkg/util"
+)
+
+const (
+	KnConfigContent string = `sink:
+- group: serving.knative.dev
+  prefix: hello
+  resource: services
+  version: v1`
+)
+
+type sinkprefixTestConfig struct {
+	knConfigDir  string
+	knConfigPath string
+}
+
+func (tc *sinkprefixTestConfig) setup() error {
+	var err error
+	tc.knConfigDir, err = ioutil.TempDir("", "kn1-config")
+	if err != nil {
+		return err
+	}
+	tc.knConfigPath, err = createPluginFile("config.yaml", KnConfigContent, tc.knConfigDir, FileModeReadWrite)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (tc *sinkprefixTestConfig) teardown() {
+	os.RemoveAll(tc.knConfigDir)
+}
+
+func TestSinkPrefixConfig(t *testing.T) {
+	t.Parallel()
+	test, err := NewE2eTest()
+	assert.NilError(t, err)
+	defer func() {
+		assert.NilError(t, test.Teardown())
+	}()
+
+	r := NewKnRunResultCollector(t)
+	defer r.DumpIfFailed()
+
+	tc := sinkprefixTestConfig{}
+	assert.NilError(t, tc.setup())
+	defer tc.teardown()
+
+	t.Log("Creating a testservice")
+	test.serviceCreate(t, r, "testsvc0")
+	t.Log("create cronJob sources with a sink to hello:testsvc0")
+	test.cronJobSourceCreateWithConfig(t, r, "testcronjobsource0", "* * * * */1", "ping", "hello:testsvc0", tc.knConfigPath)
+
+	jpSinkRefNameInSpec := "jsonpath={.spec.sink.ref.name}"
+	out, err := test.getResourceFieldsWithJSONPath("cronjobsource", "testcronjobsource0", jpSinkRefNameInSpec)
+	assert.NilError(t, err)
+	assert.Equal(t, out, "testsvc0")
+
+	t.Log("delete cronJob sources")
+	test.cronJobSourceDelete(t, r, "testcronjobsource0")
+}
+
+func (test *e2eTest) cronJobSourceCreateWithConfig(t *testing.T, r *KnRunResultCollector, sourceName string, schedule string, data string, sink string, config string) {
+	out := test.kn.Run("source", "cronjob", "create", sourceName,
+		"--schedule", schedule, "--data", data, "--sink", sink, "--config", config)
+	assert.Check(t, util.ContainsAllIgnoreCase(out.Stdout, "cronjob", "source", sourceName, "created", "namespace", test.kn.namespace))
+	r.AssertNoError(out)
+}
diff --git a/test/e2e/trigger_test.go b/test/e2e/trigger_test.go
index 07f31eba..6e2df487 100644
--- a/test/e2e/trigger_test.go
+++ b/test/e2e/trigger_test.go
@@ -40,6 +40,8 @@ func TestBrokerTrigger(t *testing.T) {
 
 	err = test.lableNamespaceForDefaultBroker(t)
 	assert.NilError(t, err)
+	defer test.unlableNamespaceForDefaultBroker(t)
+
 	test.serviceCreate(t, r, "sinksvc0")
 	test.serviceCreate(t, r, "sinksvc1")
 
@@ -47,6 +49,8 @@ func TestBrokerTrigger(t *testing.T) {
 	test.triggerCreate(t, r, "trigger1", "sinksvc0", []string{"a=b"})
 	test.triggerCreate(t, r, "trigger2", "sinksvc1", []string{"type=knative.dev.bar", "source=cronjob"})
 	test.verifyTriggerList(t, r, "trigger1", "trigger2")
+	test.triggerDelete(t, r, "trigger1")
+	test.triggerDelete(t, r, "trigger2")
 
 	t.Log("create a trigger and delete it")
 	test.triggerCreate(t, r, "deltrigger", "sinksvc0", []string{"a=b"})
@@ -58,11 +62,19 @@ func TestBrokerTrigger(t *testing.T) {
 	test.verifyTriggerDescribe(t, r, "updtrigger", "default", "sinksvc0", []string{"a", "b"})
 	test.triggerUpdate(t, r, "updtrigger", "type=knative.dev.bar", "sinksvc1")
 	test.verifyTriggerDescribe(t, r, "updtrigger", "default", "sinksvc1", []string{"a", "b", "type", "knative.dev.bar"})
+	test.triggerDelete(t, r, "updtrigger")
 
 	t.Log("create trigger with error return")
 	test.triggerCreateMissingSink(t, r, "errtrigger", "notfound")
 }
 
+func (test *e2eTest) unlableNamespaceForDefaultBroker(t *testing.T) {
+	_, err := kubectl{}.Run("label", "namespace", test.kn.namespace, "knative-eventing-injection-")
+	if err != nil {
+		t.Fatalf("Error executing 'kubectl label namespace %s knative-eventing-injection-'. Error: %s", test.kn.namespace, err.Error())
+	}
+}
+
 func (test *e2eTest) lableNamespaceForDefaultBroker(t *testing.T) error {
 	_, err := kubectl{}.Run("label", "namespace", test.kn.namespace, "knative-eventing-injection=enabled")
 	if err != nil {
diff --git a/test/presubmit-tests.sh b/test/presubmit-tests.sh
index 408275bd..e6372883 100755
--- a/test/presubmit-tests.sh
+++ b/test/presubmit-tests.sh
@@ -32,6 +32,9 @@
 # Serving / Eventing of this specific version.
 
 export DISABLE_MD_LINTING=1
+# TODO: Enable markdown link check as https://github.com/raviqqe/liche/issues/33 is fixed
+export DISABLE_MD_LINK_CHECK=1
+export PRESUBMIT_TEST_FAIL_FAST=1
 
 export GO111MODULE=on
 export KNATIVE_SERVING_VERSION=${KNATIVE_SERVING_VERSION:-latest}