pull lister into client library

This commit is contained in:
Luke K 2020-04-27 15:02:36 +00:00
parent 08c986011a
commit 0022829269
No known key found for this signature in database
GPG Key ID: 4896F75BAF2E1966
7 changed files with 125 additions and 50 deletions

View File

@ -10,7 +10,7 @@ import (
"strings"
"golang.org/x/net/publicsuffix"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v2"
)
// Client for a given Service Function.
@ -28,6 +28,7 @@ type Client struct {
updater Updater // Updates a deployed Service Function
runner Runner // Runs the function locally
remover Remover // Removes remote services
lister Lister // Lists remote services
}
// ConfigFileName is an optional file checked for in the function root.
@ -95,6 +96,12 @@ type Remover interface {
Remove(name string) error
}
// Lister of deployed services.
type Lister interface {
// List the service functions currently deployed.
List() ([]string, error)
}
// Option defines a function which when passed to the Client constructor optionally
// mutates private members at time of instantiation.
type Option func(*Client)
@ -182,6 +189,13 @@ func WithRemover(r Remover) Option {
}
}
// WithLister provides the concrete implementation of a lister.
func WithLister(l Lister) Option {
return func(c *Client) {
c.lister = l
}
}
// New client for a function service rooted at the given directory (default .) or
// that explicitly set via the option. Will fail if the directory already contains
// config files or other non-hidden files.
@ -341,6 +355,12 @@ func (c *Client) Run() error {
return c.runner.Run(c.root)
}
// List currently deployed service functions.
func (c *Client) List() ([]string, error) {
// delegate to concrete implementation of lister entirely.
return c.lister.List()
}
// Remove a function from remote, bringing the service funciton
// to the same state as if it had been created --local only.
// Name is the presently configured client's name, which was

View File

@ -483,38 +483,6 @@ func TestRemove(t *testing.T) {
}
}
// TestRemoveExplicit ensures that a call to remove an explicit name, which
// may differ from the service function the client is associated wtith, is
// respected and passed along to the concrete remover implementation.
func TestRemoveExplicit(t *testing.T) {
var (
root = "./testdata/example.com/admin"
name = "www.example.com" // Differs from that derived from root.
remover = mock.NewRemover()
)
// Create the test function root
os.MkdirAll(root, 0700)
defer os.RemoveAll(root)
client, err := client.New(root,
client.WithRemover(remover))
if err != nil {
t.Fatal(err)
}
remover.RemoveFn = func(name2 string) error {
if name2 != name {
t.Fatalf("remover expected name '%v' got '%v'", name, name2)
}
return nil
}
// Call remove with an explicit name which differs from that associated
// to the current client instance.
if err := client.Remove(name); err != nil {
t.Fatal(err)
}
}
// TestWithName ensures that an explicitly passed name is used in leau of the
// path derived name when provide, and persists through instantiations.
// This also ensures that an initialized service function's name persists if
@ -570,3 +538,24 @@ func TestWithName(t *testing.T) {
}
}
// TestList ensures that the client invokes the configured lister.
func TestList(t *testing.T) {
var lister = mock.NewLister()
client, err := client.New("",
client.WithLister(lister), // lists deployed service functions.
)
if err != nil {
t.Fatal(err)
}
_, err = client.List()
if err != nil {
t.Fatal(err)
}
if !lister.ListInvoked {
t.Fatal("list did not invoke lister implementation")
}
}

17
client/mock/lister.go Normal file
View File

@ -0,0 +1,17 @@
package mock
type Lister struct {
ListInvoked bool
ListFn func() ([]string, error)
}
func NewLister() *Lister {
return &Lister{
ListFn: func() ([]string, error) { return []string{}, nil },
}
}
func (l *Lister) List() ([]string, error) {
l.ListInvoked = true
return l.ListFn()
}

View File

@ -2,11 +2,11 @@ package cmd
import (
"fmt"
"github.com/lkingland/faas/client"
"github.com/lkingland/faas/knative"
"github.com/ory/viper"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
servingv1client "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1"
)
const (
@ -21,31 +21,38 @@ func init() {
var listCmd = &cobra.Command{
Use: "list",
Short: "Lists deployed Service Function",
Long: `Lists deployed Service Function`,
Short: "Lists deployed Service Functions",
Long: `Lists deployed Service Functions`,
SuggestFor: []string{"ls"},
RunE: list,
}
func list(cmd *cobra.Command, args []string) (err error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
config, err := clientConfig.ClientConfig()
var (
namespace = viper.GetString(nsFlag)
verbose = viper.GetBool("verbose")
)
lister, err := knative.NewLister(namespace)
if err != nil {
return
}
client, err := servingv1client.NewForConfig(config)
lister.Verbose = verbose
client, err := client.New(".",
client.WithVerbose(verbose),
client.WithLister(lister),
)
if err != nil {
return
}
opts := metav1.ListOptions{LabelSelector: "bosonFunction"}
ns := viper.GetString(nsFlag)
lst, err := client.Services(ns).List(opts)
names, err := client.List()
if err != nil {
return
}
for _, service := range lst.Items {
fmt.Printf("%s/%s", service.Namespace, service.Name)
for _, name := range names {
fmt.Printf("%s\n", name)
}
return nil
return
}

View File

@ -8,7 +8,7 @@ import (
// Version
// Printed on subcommand `version` or flag `--version`
const Version = "v0.0.14"
const Version = "v0.0.15"
func init() {
root.AddCommand(versionCmd)

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/spf13/cobra v1.0.0
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd
gomodules.xyz/jsonpatch/v2 v2.1.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gopkg.in/yaml.v2 v2.2.8
k8s.io/apimachinery v0.17.4
k8s.io/client-go v0.17.4
knative.dev/pkg v0.0.0-20200414233146-0eed424fa4ee // indirect

42
knative/lister.go Normal file
View File

@ -0,0 +1,42 @@
package knative
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
servingv1client "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1"
)
const labelSelector = "bosonFunction"
type Lister struct {
Verbose bool
namespace string
client *servingv1client.ServingV1Client
}
func NewLister(namespace string) (l *Lister, err error) {
l = &Lister{}
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
config, err := clientConfig.ClientConfig()
if err != nil {
return
}
l.client, err = servingv1client.NewForConfig(config)
if err != nil {
return
}
return
}
func (l *Lister) List() (names []string, err error) {
opts := metav1.ListOptions{LabelSelector: "bosonFunction"}
lst, err := l.client.Services(l.namespace).List(opts)
if err != nil {
return
}
for _, service := range lst.Items {
names = append(names, service.Name)
}
return
}