From 56f519ab3880e9a64c939c2cbfad1f7a5fbb4ad6 Mon Sep 17 00:00:00 2001 From: fibonacci1729 Date: Wed, 15 Jun 2016 16:46:10 -0600 Subject: [PATCH] ref(pkg/helm): move pkg/helmx to pkg/helm & remove feature toggle --- pkg/helm/client.go | 128 +++++++++++++++++++++++++++++++ pkg/helm/compat.go | 57 ++++++++++++++ pkg/helm/option.go | 183 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 pkg/helm/client.go create mode 100644 pkg/helm/compat.go create mode 100644 pkg/helm/option.go diff --git a/pkg/helm/client.go b/pkg/helm/client.go new file mode 100644 index 000000000..fae1d6a31 --- /dev/null +++ b/pkg/helm/client.go @@ -0,0 +1,128 @@ +package helm + +import ( + "google.golang.org/grpc" + "k8s.io/helm/pkg/chartutil" + rls "k8s.io/helm/pkg/proto/hapi/services" + "os" +) + +const ( + // $HELM_HOST envvar + HelmHostEnvVar = "HELM_HOST" + + // $HELM_HOME envvar + HelmHomeEnvVar = "HELM_HOME" + + // Default tiller server host address. + DefaultHelmHost = ":44134" + + // Default $HELM_HOME envvar value + DefaultHelmHome = "$HOME/.helm" +) + +// Helm client manages client side of the helm-tiller protocol +type Client struct { + opts options +} + +func NewClient(opts ...Option) *Client { + return new(Client).Init().Option(opts...) +} + +// Configure the helm client with the provided options +func (h *Client) Option(opts ...Option) *Client { + for _, opt := range opts { + opt(&h.opts) + } + return h +} + +// Initializes the helm client with default options +func (h *Client) Init() *Client { + return h.Option(HelmHost(DefaultHelmHost)). + Option(HelmHome(os.ExpandEnv(DefaultHelmHome))) +} + +// ListReleases lists the current releases. +func (h *Client) ListReleases(opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + return h.opts.rpcListReleases(rls.NewReleaseServiceClient(c), opts...) +} + +// InstallRelease installs a new chart and returns the release response. +func (h *Client) InstallRelease(chStr string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + chart, err := chartutil.Load(chStr) + if err != nil { + return nil, err + } + + return h.opts.rpcInstallRelease(chart, rls.NewReleaseServiceClient(c), opts...) +} + +// UninstallRelease uninstalls a named release and returns the response. +// +// Note: there aren't currently any supported DeleteOptions, but they are +// kept in the API signature as a placeholder for future additions. +func (h *Client) DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + return h.opts.rpcDeleteRelease(rlsName, rls.NewReleaseServiceClient(c), opts...) +} + +// UpdateRelease updates a release to a new/different chart. +// +// Note: there aren't currently any supported UpdateOptions, but they +// are kept in the API signature as a placeholder for future additions. +func (h *Client) UpdateRelease(rlsName string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + return h.opts.rpcUpdateRelease(rlsName, rls.NewReleaseServiceClient(c), opts...) +} + +// ReleaseStatus returns the given release's status. +// +// Note: there aren't currently any supported StatusOptions, +// but they are kept in the API signature as a placeholder for future additions. +func (h *Client) ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + return h.opts.rpcGetReleaseStatus(rlsName, rls.NewReleaseServiceClient(c), opts...) +} + +// ReleaseContent returns the configuration for a given release. +// +// Note: there aren't currently any supported ContentOptions, but +// they are kept in the API signature as a placeholder for future additions. +func (h *Client) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) { + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + + return h.opts.rpcGetReleaseContent(rlsName, rls.NewReleaseServiceClient(c), opts...) +} diff --git a/pkg/helm/compat.go b/pkg/helm/compat.go new file mode 100644 index 000000000..c33214c30 --- /dev/null +++ b/pkg/helm/compat.go @@ -0,0 +1,57 @@ +package helm + +import ( + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +// These APIs are a temporary abstraction layer that captures the interaction between the current cmd/helm and old +// pkg/helm implementations. Post refactor the cmd/helm package will use the APIs exposed on helm.Client directly. + +var Config struct { + ServAddr string +} + +// Soon to be deprecated helm ListReleases API. +func ListReleases(limit int, offset string, sort rls.ListSort_SortBy, order rls.ListSort_SortOrder, filter string) (*rls.ListReleasesResponse, error) { + opts := []ReleaseListOption{ + ReleaseListLimit(limit), + ReleaseListOffset(offset), + ReleaseListFilter(filter), + ReleaseListSort(int32(sort)), + ReleaseListOrder(int32(order)), + } + return NewClient(HelmHost(Config.ServAddr)).ListReleases(opts...) +} + +// Soon to be deprecated helm GetReleaseStatus API. +func GetReleaseStatus(rlsName string) (*rls.GetReleaseStatusResponse, error) { + return NewClient(HelmHost(Config.ServAddr)).ReleaseStatus(rlsName) +} + +// Soon to be deprecated helm GetReleaseContent API. +func GetReleaseContent(rlsName string) (*rls.GetReleaseContentResponse, error) { + return NewClient(HelmHost(Config.ServAddr)).ReleaseContent(rlsName) +} + +// Soon to be deprecated helm UpdateRelease API. +func UpdateRelease(rlsName string) (*rls.UpdateReleaseResponse, error) { + return NewClient(HelmHost(Config.ServAddr)).UpdateRelease(rlsName) +} + +// Soon to be deprecated helm InstallRelease API. +func InstallRelease(vals []byte, rlsName, chStr string, dryRun bool) (*rls.InstallReleaseResponse, error) { + client := NewClient(HelmHost(Config.ServAddr)) + if dryRun { + client.Option(DryRun()) + } + return client.InstallRelease(chStr, ValueOverrides(vals), ReleaseName(rlsName)) +} + +// Soon to be deprecated helm UninstallRelease API. +func UninstallRelease(rlsName string, dryRun bool) (*rls.UninstallReleaseResponse, error) { + client := NewClient(HelmHost(Config.ServAddr)) + if dryRun { + client.Option(DryRun()) + } + return client.DeleteRelease(rlsName) +} diff --git a/pkg/helm/option.go b/pkg/helm/option.go new file mode 100644 index 000000000..906b27c93 --- /dev/null +++ b/pkg/helm/option.go @@ -0,0 +1,183 @@ +package helm + +import ( + "fmt" + "golang.org/x/net/context" + cpb "k8s.io/helm/pkg/proto/hapi/chart" + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +// Option allows specifying various settings configurable by +// the helm client user for overriding the defaults used when +// issuing rpc's to the Tiller release server. +type Option func(*options) + +// options specify optional settings used by the helm client. +type options struct { + // value of helm host override + home string + // value of helm home override + host string + // name of chart + chart string + // if set dry-run helm client calls + dryRun bool + // release list options are applied directly to the list releases request + listReq rls.ListReleasesRequest + // release install options are applied directly to the install release request + instReq rls.InstallReleaseRequest +} + +// DryRun returns an Option which instructs the helm client to dry-run tiller rpcs. +func DryRun() Option { + return func(opts *options) { + opts.dryRun = true + } +} + +// HelmHome specifies the location of helm home, (default = "$HOME/.helm"). +func HelmHome(home string) Option { + return func(opts *options) { + opts.home = home + } +} + +// HelmHost specifies the host address of the Tiller release server, (default = ":44134"). +func HelmHost(host string) Option { + return func(opts *options) { + opts.host = host + } +} + +// ReleaseListOption allows specifying various settings +// configurable by the helm client user for overriding +// the defaults used when running the `helm list` command. +type ReleaseListOption func(*options) + +// ReleaseListOffset specifies the offset into a list of releases. +func ReleaseListOffset(offset string) ReleaseListOption { + return func(opts *options) { + opts.listReq.Offset = offset + } +} + +// ReleaseListFilter specifies a filter to apply a list of releases. +func ReleaseListFilter(filter string) ReleaseListOption { + return func(opts *options) { + opts.listReq.Filter = filter + } +} + +// ReleaseListLimit set an upper bound on the number of releases returned. +func ReleaseListLimit(limit int) ReleaseListOption { + return func(opts *options) { + opts.listReq.Limit = int64(limit) + } +} + +// ReleaseListOrder specifies how to order a list of releases. +func ReleaseListOrder(order int32) ReleaseListOption { + return func(opts *options) { + opts.listReq.SortOrder = rls.ListSort_SortOrder(order) + } +} + +// ReleaseListSort specifies how to sort a release list. +func ReleaseListSort(sort int32) ReleaseListOption { + return func(opts *options) { + opts.listReq.SortBy = rls.ListSort_SortBy(sort) + } +} + +// InstallOption allows specifying various settings +// configurable by the helm client user for overriding +// the defaults used when running the `helm install` command. +type InstallOption func(*options) + +// ValueOverrides specifies a list of values to include when installing. +func ValueOverrides(raw []byte) InstallOption { + return func(opts *options) { + opts.instReq.Values = &cpb.Config{Raw: string(raw)} + } +} + +// ReleaseName specifies the name of the release when installing. +func ReleaseName(name string) InstallOption { + return func(opts *options) { + opts.instReq.Name = name + } +} + +// ContentOption -- TODO +type ContentOption func(*options) + +// StatusOption -- TODO +type StatusOption func(*options) + +// DeleteOption -- TODO +type DeleteOption func(*options) + +// UpdateOption -- TODO +type UpdateOption func(*options) + +// RPC helpers defined on `options` type. Note: These actually execute the +// the corresponding tiller RPC. There is no particular reason why these +// are APIs are hung off `options`, they are internal to pkg/helm to remain +// malleable. + +// Executes tiller.ListReleases RPC. +func (o *options) rpcListReleases(rlc rls.ReleaseServiceClient, opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) { + // apply release list options + for _, opt := range opts { + opt(o) + } + s, err := rlc.ListReleases(context.TODO(), &o.listReq) + if err != nil { + return nil, err + } + + return s.Recv() +} + +// Executes tiller.InstallRelease RPC. +func (o *options) rpcInstallRelease(chr *cpb.Chart, rlc rls.ReleaseServiceClient, opts ...InstallOption) (*rls.InstallReleaseResponse, error) { + // apply the install options + for _, opt := range opts { + opt(o) + } + o.instReq.Chart = chr + o.instReq.DryRun = o.dryRun + + return rlc.InstallRelease(context.TODO(), &o.instReq) +} + +// Executes tiller.UninstallRelease RPC. +func (o *options) rpcDeleteRelease(rlsName string, rlc rls.ReleaseServiceClient, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) { + if o.dryRun { + // In the dry run case, just see if the release exists + r, err := o.rpcGetReleaseContent(rlsName, rlc) + if err != nil { + return &rls.UninstallReleaseResponse{}, err + } + return &rls.UninstallReleaseResponse{Release: r.Release}, nil + } + + return rlc.UninstallRelease(context.TODO(), &rls.UninstallReleaseRequest{Name: rlsName}) +} + +// Executes tiller.UpdateRelease RPC. +func (o *options) rpcUpdateRelease(rlsName string, rlc rls.ReleaseServiceClient, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { + return nil, fmt.Errorf("helm: UpdateRelease: not implemented") +} + +// Executes tiller.GetReleaseStatus RPC. +func (o *options) rpcGetReleaseStatus(rlsName string, rlc rls.ReleaseServiceClient, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) { + req := &rls.GetReleaseStatusRequest{Name: rlsName} + return rlc.GetReleaseStatus(context.TODO(), req) +} + +// Executes tiller.GetReleaseContent. +func (o *options) rpcGetReleaseContent(rlsName string, rlc rls.ReleaseServiceClient, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) { + req := &rls.GetReleaseContentRequest{Name: rlsName} + return rlc.GetReleaseContent(context.TODO(), req) +}