diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 00000000..b3fa74dd --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,124 @@ +package cmd + +import ( + "fmt" + + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/ory/viper" + "github.com/spf13/cobra" + + bosonFunc "github.com/boson-project/func" +) + +func init() { + root.AddCommand(configCmd) + configCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)") +} + +var configCmd = &cobra.Command{ + Use: "config", + Short: "Configure a function", + Long: `Configure a function + +Interactive propmt that allows configuration of Volume mounts and Environment variables for a function +project present in the current directory or from the directory specified with --path. +`, + SuggestFor: []string{"cfg", "cofnig"}, + PreRunE: bindEnv("path"), + RunE: runConfigCmd, +} + +func runConfigCmd(cmd *cobra.Command, args []string) (err error) { + + function, err := initConfigCommand(args) + if err != nil { + return + } + + var qs = []*survey.Question{ + { + Name: "selectedConfig", + Prompt: &survey.Select{ + Message: "What do you want to configure?", + Options: []string{"Environment values", "Volumes"}, + Default: "Environment values", + }, + }, + { + Name: "selectedOperation", + Prompt: &survey.Select{ + Message: "What operation do you want to perform?", + Options: []string{"Add", "Remove", "List"}, + Default: "List", + }, + }, + } + + answers := struct { + SelectedConfig string + SelectedOperation string + }{} + + err = survey.Ask(qs, &answers) + if err != nil { + if err == terminal.InterruptErr { + return nil + } + return + } + + switch answers.SelectedOperation { + case "Add": + if answers.SelectedConfig == "Volumes" { + err = runAddVolumesPrompt(cmd.Context(), function) + } + case "Remove": + if answers.SelectedConfig == "Volumes" { + err = runRemoveVolumesPrompt(function) + } + case "List": + if answers.SelectedConfig == "Volumes" { + listVolumes(function) + } + } + + return +} + +// CLI Configuration (parameters) +// ------------------------------ + +type configCmdConfig struct { + Name string + Path string + Verbose bool +} + +func newConfigCmdConfig(args []string) configCmdConfig { + var name string + if len(args) > 0 { + name = args[0] + } + return configCmdConfig{ + Name: deriveName(name, viper.GetString("path")), + Path: viper.GetString("path"), + } + +} + +func initConfigCommand(args []string) (bosonFunc.Function, error) { + config := newConfigCmdConfig(args) + + function, err := bosonFunc.NewFunction(config.Path) + if err != nil { + return bosonFunc.Function{}, err + } + + // Check if the Function has been initialized + if !function.Initialized() { + return bosonFunc.Function{}, fmt.Errorf("the given path '%v' does not contain an initialized function", config.Path) + } + + return function, nil +} diff --git a/cmd/config_volumes.go b/cmd/config_volumes.go new file mode 100644 index 00000000..4655afd5 --- /dev/null +++ b/cmd/config_volumes.go @@ -0,0 +1,247 @@ +package cmd + +import ( + "context" + "fmt" + "strings" + + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/spf13/cobra" + + bosonFunc "github.com/boson-project/func" + "github.com/boson-project/func/k8s" +) + +func init() { + configCmd.AddCommand(configVolumesCmd) + configVolumesCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)") + configVolumesCmd.AddCommand(configVolumesAddCmd) + configVolumesAddCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)") + configVolumesCmd.AddCommand(configVolumesRemoveCmd) + configVolumesRemoveCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)") +} + +var configVolumesCmd = &cobra.Command{ + Use: "volumes", + Short: "List and manage configured volumes for a function", + Long: `List and manage configured volumes for a function + +Prints configured Volume mounts for a function project present in +the current directory or from the directory specified with --path. +`, + SuggestFor: []string{"volums", "volume", "vols"}, + PreRunE: bindEnv("path"), + RunE: func(cmd *cobra.Command, args []string) (err error) { + function, err := initConfigCommand(args) + if err != nil { + return + } + + listVolumes(function) + + return + }, +} + +var configVolumesAddCmd = &cobra.Command{ + Use: "add", + Short: "Add volume to the function configuration", + Long: `Add volume to the function configuration + +Interactive prompt to add Secrets and ConfigMaps as Volume mounts to the function project +in the current directory or from the directory specified with --path. +`, + SuggestFor: []string{"ad", "create", "insert", "append"}, + //ValidArgsFunction: CompleteFunctionList, + PreRunE: bindEnv("path"), + RunE: func(cmd *cobra.Command, args []string) (err error) { + function, err := initConfigCommand(args) + if err != nil { + return + } + + return runAddVolumesPrompt(cmd.Context(), function) + }, +} + +var configVolumesRemoveCmd = &cobra.Command{ + Use: "remove", + Short: "Remove volume from the function configuration", + Long: `Remove volume from the function configuration + +Interactive prompt to remove Volume mounts from the function project +in the current directory or from the directory specified with --path. +`, + SuggestFor: []string{"del", "delete", "rmeove"}, + PreRunE: bindEnv("path"), + RunE: func(cmd *cobra.Command, args []string) (err error) { + function, err := initConfigCommand(args) + if err != nil { + return + } + + return runRemoveVolumesPrompt(function) + }, +} + +func listVolumes(f bosonFunc.Function) { + if len(f.Volumes) == 0 { + fmt.Println("There aren't any configured Volume mounts") + return + } + + fmt.Println("Configured Volumes mounts:") + for _, v := range f.Volumes { + fmt.Println(" - ", v.String()) + } +} + +func runAddVolumesPrompt(ctx context.Context, f bosonFunc.Function) (err error) { + + secrets, err := k8s.ListSecretsNames(ctx, f.Namespace) + if err != nil { + return + } + configMaps, err := k8s.ListConfigMapsNames(ctx, f.Namespace) + if err != nil { + return + } + + // SECTION - select resource type to be mounted + options := []string{} + selectedOption := "" + const optionConfigMap = "ConfigMap" + const optionSecret = "Secret" + + if len(configMaps) > 0 { + options = append(options, optionConfigMap) + } + if len(secrets) > 0 { + options = append(options, optionSecret) + } + + switch len(options) { + case 0: + fmt.Printf("There aren't any Secrets or ConfiMaps in the namespace \"%s\"\n", f.Namespace) + return + case 1: + selectedOption = options[0] + case 2: + err = survey.AskOne(&survey.Select{ + Message: "What do you want to mount as a Volume?", + Options: options, + }, &selectedOption) + if err != nil { + if err == terminal.InterruptErr { + return nil + } + return + } + } + + // SECTION - select the specific resource to be mounted + optionsResoures := []string{} + resourceType := "" + switch selectedOption { + case optionConfigMap: + resourceType = optionConfigMap + optionsResoures = configMaps + case optionSecret: + resourceType = optionSecret + optionsResoures = secrets + } + + selectedResource := "" + err = survey.AskOne(&survey.Select{ + Message: fmt.Sprintf("Which \"%s\" do you want to mount?", resourceType), + Options: optionsResoures, + }, &selectedResource) + if err != nil { + if err == terminal.InterruptErr { + return nil + } + return + } + + // SECTION - specify mount Path of the Volume + + path := "" + err = survey.AskOne(&survey.Input{ + Message: fmt.Sprintf("Please specify the path where the %s should be mounted:", resourceType), + }, &path, survey.WithValidator(func(val interface{}) error { + if str, ok := val.(string); !ok || len(str) <= 0 || !strings.HasPrefix(str, "/") { + return fmt.Errorf("The input must be non-empty absolute path.") + } + return nil + })) + if err != nil { + if err == terminal.InterruptErr { + return nil + } + return + } + + // we have all necessary information -> let's store the new Volume + newVolume := bosonFunc.Volume{Path: &path} + switch selectedOption { + case optionConfigMap: + newVolume.ConfigMap = &selectedResource + case optionSecret: + newVolume.Secret = &selectedResource + } + + f.Volumes = append(f.Volumes, newVolume) + + err = f.WriteConfig() + if err == nil { + fmt.Println("Volume was added to the function configuration") + } + + return +} + +func runRemoveVolumesPrompt(f bosonFunc.Function) (err error) { + if len(f.Volumes) == 0 { + fmt.Println("There aren't any configured Volume mounts") + return + } + + options := []string{} + for _, v := range f.Volumes { + options = append(options, v.String()) + } + + selectedVolume := "" + prompt := &survey.Select{ + Message: "Which Volume do you want to remove?", + Options: options, + } + err = survey.AskOne(prompt, &selectedVolume) + if err != nil { + if err == terminal.InterruptErr { + return nil + } + return + } + + var newVolumes bosonFunc.Volumes + removed := false + for i, v := range f.Volumes { + if v.String() == selectedVolume { + newVolumes = append(f.Volumes[:i], f.Volumes[i+1:]...) + removed = true + break + } + } + + if removed { + f.Volumes = newVolumes + err = f.WriteConfig() + if err == nil { + fmt.Println("Volume was removed from the function configuration") + } + } + + return +} diff --git a/config.go b/config.go index 3e245832..8bdab13f 100644 --- a/config.go +++ b/config.go @@ -22,6 +22,16 @@ type Volume struct { Path *string `yaml:"path"` } +func (v Volume) String() string { + if v.ConfigMap != nil { + return fmt.Sprintf("ConfigMap \"%s\" mounted at path: \"%s\"", *v.ConfigMap, *v.Path) + } else if v.Secret != nil { + return fmt.Sprintf("Secret \"%s\" mounted at path: \"%s\"", *v.Secret, *v.Path) + } + + return "" +} + type Envs []Env type Env struct { Name *string `yaml:"name,omitempty"` diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 6df1a302..40e9a15d 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -159,3 +159,43 @@ kn func emit --path /path/to/fn -i fn.test # Send a CloudEvent to an arbitrary endpoint kn func emit --sink "http://my.event.broker.com" ``` + +## `config` + +Invokes interactive prompt that manages configuration of the Function project in the current directory. +The user may specify a path to the project directory using the `--path` or `-p` flag. This command operates on configuration +specified in `func.yaml` configuration file. +Users need to deploy or update the function with `func deploy` in order to apply the updated configuration to the deployed function. + +This command has subcommands `envs` and `volumes` to manage directly the specific resouces: Environment variables and Volumes. +These subcommands has commands `add` and `remove` to add and remove specified resouces. + +Invokes top level interactive prompt that allows choosing the resouce and operation: +```console +func config [-p ] +``` + +Example: +```console +func config +? What do you want to configure? Volumes +? What operation do you want to perform? List +Configured Volumes mounts: + - Secret "mysecret" mounted at path: "/workspace/secret" + - ConfigMap "mycm" mounted at path: "/workspace/configmap" +``` + +This command lists configured Volumes: +```console +func config volumes [-p ] +``` + +Invokes interactive prompt that allows addind Volumes to the function configuration +```console +func config volumes add [-p ] +``` + +Invokes interactive prompt that allows removing Volumes from the function configuration +```console +func config volumes remove [-p ] +``` \ No newline at end of file diff --git a/go.mod b/go.mod index c383cdde..c68fa809 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/boson-project/func go 1.15 require ( + github.com/AlecAivazis/survey/v2 v2.2.12 github.com/buildpacks/pack v0.18.0 github.com/cloudevents/sdk-go/v2 v2.2.0 github.com/containers/image/v5 v5.10.5 diff --git a/go.sum b/go.sum index 44ee41d2..bf1c848a 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= +github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8= +github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI= github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v42.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -96,6 +98,7 @@ github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3H github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -544,6 +547,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0= github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -585,6 +589,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -604,6 +609,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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= @@ -649,6 +655,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 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/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M= @@ -912,6 +919,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1018,6 +1026,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1160,6 +1169,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/k8s/client.go b/k8s/client.go new file mode 100644 index 00000000..6a2d2c4b --- /dev/null +++ b/k8s/client.go @@ -0,0 +1,37 @@ +package k8s + +import ( + "fmt" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + + +func NewKubernetesClientset(namespace string) (*kubernetes.Clientset, error) { + + restConfig, err := GetClientConfig().ClientConfig() + if err != nil { + return nil, fmt.Errorf("failed to create new kubernetes client: %v", err) + } + + return kubernetes.NewForConfig(restConfig) +} + +func GetNamespace(defaultNamespace string) (namespace string, err error) { + namespace = defaultNamespace + + if defaultNamespace == "" { + namespace, _, err = GetClientConfig().Namespace() + if err != nil { + return + } + } + return +} + +func GetClientConfig() clientcmd.ClientConfig { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + &clientcmd.ConfigOverrides{}) +} diff --git a/k8s/configmaps.go b/k8s/configmaps.go new file mode 100644 index 00000000..56830af5 --- /dev/null +++ b/k8s/configmaps.go @@ -0,0 +1,47 @@ +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetConfigMap(ctx context.Context, name, namespaceOverride string) (*corev1.ConfigMap, error) { + + namespace, err := GetNamespace(namespaceOverride) + if err != nil { + return nil, err + } + + client, err := NewKubernetesClientset(namespace) + if err != nil { + return nil, err + } + + return client.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{}) +} + +func ListConfigMapsNames(ctx context.Context, namespaceOverride string) (names []string, err error) { + + namespace, err := GetNamespace(namespaceOverride) + if err != nil { + return + } + + client, err := NewKubernetesClientset(namespace) + if err != nil { + return + } + + cms, err := client.CoreV1().ConfigMaps(namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return + } + + for _, cm := range cms.Items { + names = append(names, cm.Name) + } + + return +} diff --git a/k8s/secrets.go b/k8s/secrets.go new file mode 100644 index 00000000..6d03c432 --- /dev/null +++ b/k8s/secrets.go @@ -0,0 +1,47 @@ +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetSecret(ctx context.Context, name, namespaceOverride string) (*corev1.Secret, error) { + + namespace, err := GetNamespace(namespaceOverride) + if err != nil { + return nil, err + } + + client, err := NewKubernetesClientset(namespace) + if err != nil { + return nil, err + } + + return client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) +} + +func ListSecretsNames(ctx context.Context, namespaceOverride string) (names []string, err error) { + + namespace, err := GetNamespace(namespaceOverride) + if err != nil { + return + } + + client, err := NewKubernetesClientset(namespace) + if err != nil { + return + } + + secrets, err := client.CoreV1().Secrets(namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return + } + + for _, s := range secrets.Items { + names = append(names, s.Name) + } + + return +} diff --git a/knative/client.go b/knative/client.go index ce72cb7d..cdeed75b 100644 --- a/knative/client.go +++ b/knative/client.go @@ -4,12 +4,12 @@ import ( "fmt" "time" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" clienteventingv1beta1 "knative.dev/client/pkg/eventing/v1beta1" clientservingv1 "knative.dev/client/pkg/serving/v1" eventingv1beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1" servingv1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1" + + "github.com/boson-project/func/k8s" ) const ( @@ -18,7 +18,7 @@ const ( func NewServingClient(namespace string) (clientservingv1.KnServingClient, error) { - restConfig, err := getClientConfig().ClientConfig() + restConfig, err := k8s.GetClientConfig().ClientConfig() if err != nil { return nil, fmt.Errorf("failed to create new serving client: %v", err) } @@ -35,7 +35,7 @@ func NewServingClient(namespace string) (clientservingv1.KnServingClient, error) func NewEventingClient(namespace string) (clienteventingv1beta1.KnEventingClient, error) { - restConfig, err := getClientConfig().ClientConfig() + restConfig, err := k8s.GetClientConfig().ClientConfig() if err != nil { return nil, fmt.Errorf("failed to create new serving client: %v", err) } @@ -49,31 +49,3 @@ func NewEventingClient(namespace string) (clienteventingv1beta1.KnEventingClient return client, nil } - -func NewKubernetesClientset(namespace string) (*kubernetes.Clientset, error) { - - restConfig, err := getClientConfig().ClientConfig() - if err != nil { - return nil, fmt.Errorf("failed to create new kubernetes client: %v", err) - } - - return kubernetes.NewForConfig(restConfig) -} - -func GetNamespace(defaultNamespace string) (namespace string, err error) { - namespace = defaultNamespace - - if defaultNamespace == "" { - namespace, _, err = getClientConfig().Namespace() - if err != nil { - return - } - } - return -} - -func getClientConfig() clientcmd.ClientConfig { - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - clientcmd.NewDefaultClientConfigLoadingRules(), - &clientcmd.ConfigOverrides{}) -} diff --git a/knative/deployer.go b/knative/deployer.go index 32d9786e..6d450620 100644 --- a/knative/deployer.go +++ b/knative/deployer.go @@ -20,6 +20,7 @@ import ( v1 "knative.dev/serving/pkg/apis/serving/v1" fn "github.com/boson-project/func" + "github.com/boson-project/func/k8s" ) type Deployer struct { @@ -32,7 +33,7 @@ type Deployer struct { func NewDeployer(namespaceOverride string) (deployer *Deployer, err error) { deployer = &Deployer{} - namespace, err := GetNamespace(namespaceOverride) + namespace, err := k8s.GetNamespace(namespaceOverride) if err != nil { return } @@ -495,21 +496,17 @@ func processVolumes(volumes fn.Volumes, referencedSecrets, referencedConfigMaps // checkSecretsConfigMapsArePresent returns error if Secrets or ConfigMaps // referenced in input sets are not deployed on the cluster in the specified namespace func checkSecretsConfigMapsArePresent(ctx context.Context, namespace string, referencedSecrets, referencedConfigMaps *sets.String) error { - client, err := NewKubernetesClientset(namespace) - if err != nil { - return err - } errMsg := "" for s := range *referencedSecrets { - _, err := client.CoreV1().Secrets(namespace).Get(ctx, s, metav1.GetOptions{}) + _, err := k8s.GetSecret(ctx, s, namespace) if err != nil { errMsg += fmt.Sprintf(" referenced Secret \"%s\" is not present in namespace \"%s\"\n", s, namespace) } } for cm := range *referencedConfigMaps { - _, err := client.CoreV1().ConfigMaps(namespace).Get(ctx, cm, metav1.GetOptions{}) + _, err := k8s.GetConfigMap(ctx, cm, namespace) if err != nil { errMsg += fmt.Sprintf(" referenced ConfigMap \"%s\" is not present in namespace \"%s\"\n", cm, namespace) } diff --git a/knative/describer.go b/knative/describer.go index da0e1106..3a8bc91c 100644 --- a/knative/describer.go +++ b/knative/describer.go @@ -8,6 +8,7 @@ import ( "knative.dev/eventing/pkg/apis/eventing/v1beta1" bosonFunc "github.com/boson-project/func" + "github.com/boson-project/func/k8s" ) type Describer struct { @@ -17,7 +18,7 @@ type Describer struct { func NewDescriber(namespaceOverride string) (describer *Describer, err error) { describer = &Describer{} - namespace, err := GetNamespace(namespaceOverride) + namespace, err := k8s.GetNamespace(namespaceOverride) if err != nil { return } diff --git a/knative/lister.go b/knative/lister.go index 0300f9b4..ae845eeb 100644 --- a/knative/lister.go +++ b/knative/lister.go @@ -7,6 +7,7 @@ import ( "knative.dev/pkg/apis" bosonFunc "github.com/boson-project/func" + "github.com/boson-project/func/k8s" ) const ( @@ -22,7 +23,7 @@ type Lister struct { func NewLister(namespaceOverride string) (l *Lister, err error) { l = &Lister{} - namespace, err := GetNamespace(namespaceOverride) + namespace, err := k8s.GetNamespace(namespaceOverride) if err != nil { return } diff --git a/knative/remover.go b/knative/remover.go index ae1b2cff..6583ce93 100644 --- a/knative/remover.go +++ b/knative/remover.go @@ -4,13 +4,15 @@ import ( "context" "fmt" "time" + + "github.com/boson-project/func/k8s" ) const RemoveTimeout = 120 * time.Second func NewRemover(namespaceOverride string) (remover *Remover, err error) { remover = &Remover{} - namespace, err := GetNamespace(namespaceOverride) + namespace, err := k8s.GetNamespace(namespaceOverride) if err != nil { return }