From d07b95d0ae2ea564d45cbcbcf83dd1acdba5deb3 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 5 Dec 2018 22:53:40 -0700 Subject: [PATCH] feat: add pkg/action to encapsulate action logic Signed-off-by: Matt Butcher --- cmd/helm/helm.go | 23 ++++++++ cmd/helm/list.go | 11 +++- pkg/action/action.go | 33 +++++++++++ pkg/action/doc.go | 6 ++ pkg/action/list.go | 128 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 pkg/action/action.go create mode 100644 pkg/action/doc.go create mode 100644 pkg/action/list.go diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 7471dbaf8..02e5bd994 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -89,6 +89,29 @@ func newClient(allNamespaces bool) helm.Interface { ) } +func newActionConfig(allNamespaces bool) *action.Configuration { + kc := kube.New(kubeConfig()) + kc.Log = logf + + clientset, err := kc.KubernetesClientSet() + if err != nil { + // TODO return error + log.Fatal(err) + } + var namespace string + if !allNamespaces { + namespace = getNamespace() + } + // TODO add other backends + d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace)) + d.Log = logf + + c := &action.Configuration{ + KubeClient: helm.KubeClient(kc), + Storage: storage + } +} + func kubeConfig() genericclioptions.RESTClientGetter { configOnce.Do(func() { config = kube.GetConfig(settings.KubeConfig, settings.KubeContext, settings.Namespace) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 1285412e4..72fbd8c98 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -25,9 +25,11 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" "k8s.io/helm/pkg/hapi" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/storage" ) var listHelp = ` @@ -93,7 +95,14 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { o.filter = strings.Join(args, " ") } o.client = ensureHelmClient(o.client, o.allNamespaces) - return o.run(out) + + lister := action.NewList(action.Configuration{ + Releases: storage.Init(driver.ConfigMap), + KubeClient: newClient(o.all), + }) + //return o.run(out) + _, err := lister.Run() + return err }, } diff --git a/pkg/action/action.go b/pkg/action/action.go new file mode 100644 index 000000000..11f3ef86c --- /dev/null +++ b/pkg/action/action.go @@ -0,0 +1,33 @@ +package action + +import ( + "k8s.io/client-go/discovery" + + "k8s.io/helm/pkg/storage" + "k8s.io/helm/pkg/tiller/environment" +) + +// Action describes a top-level Helm action. +// +// When implementing an action, the following guidelines should be observed: +// - Constructors should take all REQUIRED fields +// - Exported properties should hold all OPTIONAL fields +// +// When an error occurs, the result of 'Run()' should be targeted +// toward a user, but not assume a particular user interface (e.g. don't +// make reference to a command line flag). +type Action interface { + Run() error +} + +type Configuration struct { + //engine Engine + discovery discovery.DiscoveryInterface + + // Releases stores records of releases. + Releases *storage.Storage + // KubeClient is a Kubernetes API client. + KubeClient environment.KubeClient + + Log func(string, ...interface{}) +} diff --git a/pkg/action/doc.go b/pkg/action/doc.go new file mode 100644 index 000000000..7c7b9da23 --- /dev/null +++ b/pkg/action/doc.go @@ -0,0 +1,6 @@ +// Package action contains the logic for each action that Helm can perform. +// +// This is a library for calling top-level Helm actions like 'install', +// 'upgrade', or 'list'. Actions approximately match the command line +// invocations that the Helm client uses. +package action diff --git a/pkg/action/list.go b/pkg/action/list.go new file mode 100644 index 000000000..11f3e5306 --- /dev/null +++ b/pkg/action/list.go @@ -0,0 +1,128 @@ +package action + +import ( + "errors" + "regexp" + "sort" + + "k8s.io/helm/pkg/hapi/release" + "k8s.io/helm/pkg/releaseutil" +) + +type ListStates uint + +const ( + // ListDeployed filters on status "deployed" + ListDeployed ListStates = 1 << iota + // ListUninstalled filters on status "uninstalled" + ListUninstalled + // ListUninstalling filters on status "uninstalling" (uninstall in progress) + ListUninstalling + // ListPendingInstall filters on status "pending" (deployment in progress) + ListPendingInstall + // ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress) + ListPendingUpgrade + // ListPendingRollback filters on status "pending_rollback" (rollback in progres) + ListPendingRollback + // ListSuperseded filters on status "superseded" (historical release version that is no longer deployed) + ListSuperseded + // ListFailed filters on status "failed" (release version not deployed because of error) + ListFailed + // ListUnknown filters on an unknown status + ListUnknown +) + +type ByDate struct{} +type ByNameAsc struct{} +type ByNameDesc struct{} + +// ReleaseSorter is a sorter for releases +type ReleaseSorter interface { + SetList([]*release.Release) + Len() int + Less(i, j int) bool + Swap(i, j int) +} + +// List is the action for listing releases. +// +// It provides, for example, the implementation of 'helm list'. +type List struct { + // All ignores the limit/offset + All bool + // AllNamespaces searches across namespaces + AllNamespaces bool + // Sort indicates the sort to use + // + // see pkg/releaseutil for several useful sorters + Sort ReleaseSorter + // StateMask accepts a bitmask of states for items to show. + // The default is ListDeployed + StateMask ListStates + // Limit is the number of items to return per Run() + Limit int + // Offset is the starting index for the Run() call + Offset int + // Filter is a filter that is applied to the results + Filter string + + cfg *Configuration +} + +// NewList constructs a new *List +func NewList(cfg *Configuration) *List { + return &List{ + StateMask: ListDeployed | ListFailed, + } +} + +func (a *List) Run() ([]*release.Release, error) { + return []*release.Release{}, errors.New("not implemented") +} + +func (a *List) listReleases() ([]*release.Release, error) { + rels, err := a.cfg.Releases.ListReleases() + if err != nil { + return rels, err + } + + // TODO: add the rest of the filters here + + // Run filter + rels, err = a.filterReleases(rels) + if err != nil { + return rels, err + } + + // Run sort + rels = a.sort(rels) + + return rels, nil +} + +func (a *List) filterReleases(rels []*release.Release) ([]*release.Release, error) { + if a.Filter == "" { + return rels, nil + } + preg, err := regexp.Compile(a.Filter) + if err != nil { + return rels, err + } + matches := []*release.Release{} + for _, r := range rels { + if preg.MatchString(r.Name) { + matches = append(matches, r) + } + } + return matches, nil +} + +func (a *List) sort(rels []*release.Release) []*release.Release { + if a.Sort == nil { + releaseutil.SortByName(rels) + return rels + } + a.Sort.SetList(rels) + sort.Sort(a.Sort) + return rels +}