From d14c01fb5dc0731efbfce526874718deed47344a Mon Sep 17 00:00:00 2001 From: Ying Chun Guo Date: Fri, 28 Feb 2020 18:02:51 +0800 Subject: [PATCH] Allow configuration sink prefixes (#676) --- CHANGELOG.adoc | 4 ++ docs/README.md | 16 +++++- pkg/kn/commands/flags/sink.go | 12 +++++ pkg/kn/commands/types.go | 9 ++++ pkg/kn/core/root.go | 20 +++++++- test/e2e/sinkprefix_test.go | 92 +++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 test/e2e/sinkprefix_test.go diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 037d98a21..01540f740 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] diff --git a/docs/README.md b/docs/README.md index fdfeb8be1..57e777e90 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,7 +38,7 @@ You'll need a `kubectl`-style config file to connect to your cluster. ## 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/pkg/kn/commands/flags/sink.go b/pkg/kn/commands/flags/sink.go index 2a6ce98f3..4bc673e47 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/types.go b/pkg/kn/commands/types.go index 196e49dfc..1ddaee181 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 4f5700cd3..d85813650 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/test/e2e/sinkprefix_test.go b/test/e2e/sinkprefix_test.go new file mode 100644 index 000000000..32d09b444 --- /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) +}