Skip to content
This repository has been archived by the owner on May 6, 2022. It is now read-only.

MVP Plugins for Service-Catalog Interaction #1621

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d0f3881
kubectl plugin create-service-broker
Jun 5, 2017
2642f9d
kubectl plugin create-service-instance
Jun 5, 2017
833f379
kubectl plugin bind-service
Jun 5, 2017
807c7b3
Add plugins to Makefile and fix lint issues
Jul 11, 2017
a7b5086
Fix some more "verify" issues
Jul 11, 2017
bf4dedd
rebase based on name change
MHBauer Sep 1, 2017
8d1cf3e
use standard build
MHBauer Sep 1, 2017
a81afa9
remove hard coded test url
MHBauer Sep 1, 2017
8113214
Update plugins to use v1beta1
jberkhahn Nov 3, 2017
412c1b2
Add relist broker plugin
jberkhahn Nov 3, 2017
74e55b4
remove use of kubectl proxy for api calls
juanvallejo Nov 13, 2017
a84e54f
acknowledge value of KUBECTL_PLUGINS_CURRENT_NAMESPACE
juanvallejo Nov 13, 2017
dd3eb7d
use KUBECONFIG to create clients
juanvallejo Nov 13, 2017
ffbbf61
handle global config flags
juanvallejo Nov 13, 2017
2a41027
Refactor relist-service-broker to work with changes
jberkhahn Nov 15, 2017
9d3e5bd
Fix bugs in plugin output
jberkhahn Nov 16, 2017
0586082
Add plugin for clusterservicebroker interaction
jberkhahn Nov 16, 2017
fe0b140
MVP plugins for service-catalog interaction
jberkhahn Dec 15, 2017
f8e9aa0
Appease golinter
jberkhahn Dec 15, 2017
3ca40a8
Formatting changes
jberkhahn Dec 18, 2017
59c3bc4
update color utils to be platform independent
jberkhahn Jan 16, 2018
fa9fbce
refactor plugin tests to use generated fakes
jberkhahn Jan 16, 2018
fba2ea0
fix dependencies for color change
jberkhahn Jan 16, 2018
961883c
Find home dir in a cross-platform compatible way
jberkhahn Jan 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 54 additions & 1 deletion Makefile
Expand Up @@ -110,7 +110,8 @@ NON_VENDOR_DIRS = $(shell $(DOCKER_CMD) glide nv)
#########################################################################
build: .init .generate_files \
$(BINDIR)/service-catalog \
$(BINDIR)/user-broker
$(BINDIR)/user-broker \
plugins
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space issue


user-broker: $(BINDIR)/user-broker
$(BINDIR)/user-broker: .init contrib/cmd/user-broker \
Expand Down Expand Up @@ -385,3 +386,55 @@ release-push-%:
$(MAKE) clean-bin
$(MAKE) ARCH=$* build
$(MAKE) ARCH=$* push


# kubectl plugin stuff
######################
PLUGIN_EXES=binding broker class instance plan

plugins: .init .generate_files \
$(BINDIR)/binding/binding \
$(BINDIR)/broker/broker \
$(BINDIR)/class/class \
$(BINDIR)/instance/instance \
$(BINDIR)/plan/plan

$(BINDIR)/binding/binding: \
plugin/cmd/kubectl/binding/binding.go \
plugin/cmd/kubectl/binding/plugin.yaml
rm -rf $(BINDIR)/binding
$(DOCKER_CMD) $(GO_BUILD) -o $@ $<
$(DOCKER_CMD) cp plugin/cmd/kubectl/binding/*yaml \
$(BINDIR)/binding/

$(BINDIR)/broker/broker: \
plugin/cmd/kubectl/broker/broker.go \
plugin/cmd/kubectl/broker/plugin.yaml
rm -rf $(BINDIR)/broker
$(DOCKER_CMD) $(GO_BUILD) -o $@ $<
$(DOCKER_CMD) cp plugin/cmd/kubectl/broker/*yaml \
$(BINDIR)/broker/

$(BINDIR)/class/class: \
plugin/cmd/kubectl/class/class.go \
plugin/cmd/kubectl/class/plugin.yaml
rm -rf $(BINDIR)/class
$(DOCKER_CMD) $(GO_BUILD) -o $@ $<
$(DOCKER_CMD) cp plugin/cmd/kubectl/class/*yaml \
$(BINDIR)/class/

$(BINDIR)/instance/instance: \
plugin/cmd/kubectl/instance/instance.go \
plugin/cmd/kubectl/instance/plugin.yaml
rm -rf $(BINDIR)/instance
$(DOCKER_CMD) $(GO_BUILD) -o $@ $<
$(DOCKER_CMD) cp plugin/cmd/kubectl/instance/*yaml \
$(BINDIR)/instance/

$(BINDIR)/plan/plan: \
plugin/cmd/kubectl/plan/plan.go \
plugin/cmd/kubectl/plan/plugin.yaml
rm -rf $(BINDIR)/plan
$(DOCKER_CMD) $(GO_BUILD) -o $@ $<
$(DOCKER_CMD) cp plugin/cmd/kubectl/plan/*yaml \
$(BINDIR)/plan/
86 changes: 86 additions & 0 deletions plugin/cmd/kubectl/binding/binding.go
@@ -0,0 +1,86 @@
/*
Copyright 2016 The Kubernetes Authors.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2017


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 main

import (
"fmt"
"os"

"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/client"
"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/utils"
)

const usage = `Usage:
kubectl plugin binding SUBCOMMAND

Available subcommands:
list
get
`

const listUsage = `Usage:
kubectl plugin binding list NAMESPACE
`

const getUsage = `Usage:
kubectl plugin binding get NAMESPACE INSTANCENAME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing, but INSTANCENAMEwould be easier to read asINSTANCE_NAME`. Same for other 2-word thingies.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not -n, --namespace= instead of an arg?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it depends on whether its a required arg or not. As of now its required. I know some other kubectl commands use --arg for required fields, but personally, I find that very odd.

Having said all of that, I think we do need to look ahead and think about how we're going to deal with cluster and NS scoped resources. That's when an optional --namespace might come into play.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if it's required for the processing, it should not be required for users to provide. In kubectl every command that is namespaced defaults to the current "in use" namespace if you don't provide it explicitly, and plugins should do the same for consistency.

The KUBECTL_PLUGINS_CURRENT_NAMESPACE will give you the namespace you must consider in the call, ready to use, with all the processing related to config file and env var precedence processed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point - I forgot that kubectl has the notion of the current/default namespace. I would,however, prefer to use kubectl's and not create a new plugin-specific current/default namespace. To me plugins are extending kubectl, not an alternative to it, so whatever defaults kubectl has should apply to plugins as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly! So just use the value in KUBECTL_PLUGINS_CURRENT_NAMESPACE and you get what kubectl considered as the namespace for the current call, with defaults, flags and env vars already processed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. Isn't KUBECTL_PLUGINS_CURRENT_NAMESPACE a plugin thing instead of the default kubectl namespace? Shouldn't we grab it from the current context or something like that?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is what plugins must consider as the namespace to use in the current call. If you need a namespace, just use the value set in that env var as is and you're good.

kubectl does all its namespace processing (take the default namespace, kubeconfig, what user provided through -n, etc), processes the precedence logic, and sets the result in that env var. This way plugin writers don't have to duplicate all this logic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not suggesting we duplicate it, we should just use it. Part of what @jberkhahn is doing is creating a shared library kind of thing and I would think the getting the current NS would be a util in there that both kubectl and plugins can leverage. IOW, the "current namespace" should yield the same answer for kubectl as it does for the plugin.

`

func main() {
if len(os.Args) < 2 {
utils.Exit1(usage)
}

client, err := client.NewClient()
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to initialize service catalog client (%s)", err))
}
if os.Args[1] == "list" {
if len(os.Args) != 3 {
utils.Exit1(listUsage)
}
namespace := os.Args[2]
bindings, err := client.ListBindings(namespace)
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to list bindings in namespace %s (%s)", namespace, err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use %q for the first one so its quoted and its clear that the word isn't part of a sentence but rather something special.

}

table := utils.NewTable("BINDING NAME", "NAMESPACE", "INSTANCE NAME")
for _, v := range bindings.Items {
table.AddRow(v.Name, v.Namespace, v.Spec.ServiceInstanceRef.Name)
err = table.Print()
}
if err != nil {
utils.Exit1(fmt.Sprintf("Error printing result (%s)", err))
}
} else if os.Args[1] == "get" {
if len(os.Args) != 4 {
utils.Exit1(getUsage)
}
namespace := os.Args[2]
bindingName := os.Args[3]
binding, err := client.GetBinding(bindingName, namespace)
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to find binding %s in namespae %s (%s)", bindingName, namespace, err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%q for the first 2 %s's

}
table := utils.NewTable("BINDINGNAME", "NAMESPACE", "INSTANCE NAME")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add space before NAME, in BINDINGNAME to be consistent with instance name

table.AddRow(binding.Name, binding.Namespace, binding.Spec.ServiceInstanceRef.Name)
err = table.Print()
} else {
utils.Exit1(usage)
}
}
10 changes: 10 additions & 0 deletions plugin/cmd/kubectl/binding/plugin.yaml
@@ -0,0 +1,10 @@
name: "binding"
shortDesc: "This command interacts with Service Bindings"
command: "./binding"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave off the "./" since you don't now how they'll specify the path to the exe (if I'm understanding how this is being used)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, that needs to be there for kubectl to know how to invoke the executable - it will need to be different for these to work on windows, but that has other issues.

tree:
- name: "list"
shortDesc: "Lists all Service Bindings in a particular namespace"
command: "./binding list"
Copy link
Contributor

@duglin duglin Dec 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/.\///

- name: "get"
shortDesc: "Gets a particular Service Bindings in a particular namespace"
command: "./binding get"
77 changes: 77 additions & 0 deletions plugin/cmd/kubectl/broker/broker.go
@@ -0,0 +1,77 @@
/*
Copyright 2016 The Kubernetes 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 implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"
"os"

"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/client"
"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/utils"
)

const usage = `Usage:
kubectl plugin broker SUBCOMMAND

Available subcommands:
list
get
`

const getUsage = `Usage:
kubectl plugin broker get BROKERNAME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BROKER_NAME

`

func main() {
if len(os.Args) < 2 {
utils.Exit1(usage)
}

client, err := client.NewClient()
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to initialize service catalog client (%s)", err))
}
if os.Args[1] == "list" {
brokers, err := client.ListBrokers()
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to list brokers (%s)", err))
}

table := utils.NewTable("BROKER NAME", "NAMESPACE", "URL")
for _, v := range brokers.Items {
table.AddRow(v.Name, v.Namespace, v.Spec.URL)
err = table.Print()
}
if err != nil {
utils.Exit1(fmt.Sprintf("Error printing result (%s)", err))
}
} else if os.Args[1] == "get" {
if len(os.Args) != 3 {
utils.Exit1(getUsage)
}
brokerName := os.Args[2]
broker, err := client.GetBroker(brokerName)
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to find broker %s (%s)", brokerName, err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%q

}
table := utils.NewTable("BROKER NAME", "NAMESPACE", "URL")
table.AddRow(broker.Name, broker.Namespace, broker.Spec.URL)
err = table.Print()
} else {
utils.Exit1(usage)
}
}
10 changes: 10 additions & 0 deletions plugin/cmd/kubectl/broker/plugin.yaml
@@ -0,0 +1,10 @@
name: "broker"
shortDesc: "This command interacts with Cluster Service Brokers"
command: "./broker"
tree:
- name: "list"
shortDesc: "Lists all Cluster Service Brokers"
command: "./broker list"
- name: "get"
shortDesc: "Gets a particular Cluster Service Broker"
command: "./broker get"
77 changes: 77 additions & 0 deletions plugin/cmd/kubectl/class/class.go
@@ -0,0 +1,77 @@
/*
Copyright 2016 The Kubernetes 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 implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"
"os"

"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/client"
"github.com/kubernetes-incubator/service-catalog/plugin/pkg/kubectl/utils"
)

const usage = `Usage:
kubectl plugin class SUBCOMMAND

Available subcommands:
list
get
`

const getUsage = `Usage:
kubectl plugin class get CLASSNAME
`

func main() {
if len(os.Args) < 2 {
utils.Exit1(usage)
}

client, err := client.NewClient()
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to initialize service catalog client (%s)", err))
}
if os.Args[1] == "list" {
classes, err := client.ListClasses()
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to list classes (%s)", err))
}

table := utils.NewTable("CLASS NAME", "NAMESPACE", "BROKER NAME")
for _, v := range classes.Items {
table.AddRow(v.Name, v.Namespace, v.Spec.ClusterServiceBrokerName)
err = table.Print()
}
if err != nil {
utils.Exit1(fmt.Sprintf("Error printing result (%s)", err))
}
} else if os.Args[1] == "get" {
if len(os.Args) != 3 {
utils.Exit1(getUsage)
}
className := os.Args[2]
class, err := client.GetClass(className)
if err != nil {
utils.Exit1(fmt.Sprintf("Unable to find class %s (%s)", className, err))
}
table := utils.NewTable("CLASS NAME", "NAMESPACE", "BROKER NAME")
table.AddRow(class.Name, class.Namespace, class.Spec.ClusterServiceBrokerName)
err = table.Print()
} else {
utils.Exit1(usage)
}
}
10 changes: 10 additions & 0 deletions plugin/cmd/kubectl/class/plugin.yaml
@@ -0,0 +1,10 @@
name: "class"
shortDesc: "This command interacts with Cluster Service Classes"
command: "./class"
tree:
- name: "list"
shortDesc: "Lists all Cluster Service Classes"
command: "./class list"
- name: "get"
shortDesc: "Gets a particular Cluster Service Class"
command: "./class get"