mirror of https://github.com/helm/helm
Merge pull request #837 from fibonacci1729/ref/pkg-helm-new
ref(pkg/helm) replace 'pkg/helm' with 'pkg/helmx'pull/839/head
commit
36bf76548b
@ -1,43 +1,128 @@
|
|||||||
package helm
|
package helm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"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"
|
||||||
|
|
||||||
"k8s.io/helm/pkg/proto/hapi/services"
|
// Default tiller server host address.
|
||||||
|
DefaultHelmHost = ":44134"
|
||||||
|
|
||||||
|
// Default $HELM_HOME envvar value
|
||||||
|
DefaultHelmHome = "$HOME/.helm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
// Helm client manages client side of the helm-tiller protocol
|
||||||
cfg *config
|
type Client struct {
|
||||||
conn *grpc.ClientConn
|
opts options
|
||||||
impl services.ReleaseServiceClient
|
}
|
||||||
|
|
||||||
|
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...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) dial() (err error) {
|
// InstallRelease installs a new chart and returns the release response.
|
||||||
c.conn, err = grpc.Dial(c.cfg.ServAddr, c.cfg.DialOpts()...)
|
func (h *Client) InstallRelease(chStr string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
|
||||||
c.impl = services.NewReleaseServiceClient(c.conn)
|
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
|
||||||
return
|
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...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) install(req *services.InstallReleaseRequest) (res *services.InstallReleaseResponse, err error) {
|
// UninstallRelease uninstalls a named release and returns the response.
|
||||||
if err = c.dial(); err != nil {
|
//
|
||||||
return
|
// 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()
|
defer c.Close()
|
||||||
|
|
||||||
return c.impl.InstallRelease(context.TODO(), req, c.cfg.CallOpts()...)
|
return h.opts.rpcUpdateRelease(rlsName, rls.NewReleaseServiceClient(c), opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) uninstall(req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) {
|
// ReleaseStatus returns the given release's status.
|
||||||
if err := c.dial(); err != nil {
|
//
|
||||||
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
return c.impl.UninstallRelease(context.TODO(), req, c.cfg.CallOpts()...)
|
return h.opts.rpcGetReleaseStatus(rlsName, rls.NewReleaseServiceClient(c), opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Close() error {
|
// ReleaseContent returns the configuration for a given release.
|
||||||
return c.conn.Close()
|
//
|
||||||
|
// 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...)
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
ServAddr string
|
|
||||||
Insecure bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *config) DialOpts() (opts []grpc.DialOption) {
|
|
||||||
if cfg.Insecure {
|
|
||||||
opts = append(opts, grpc.WithInsecure())
|
|
||||||
} else {
|
|
||||||
// TODO: handle transport credentials
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *config) CallOpts() (opts []grpc.CallOption) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *config) client() *client {
|
|
||||||
return &client{cfg: cfg}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
chartutil "k8s.io/helm/pkg/chart"
|
|
||||||
chartpbs "k8s.io/helm/pkg/proto/hapi/chart"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChartToProto converts a chart to its Protobuf struct representation.
|
|
||||||
func ChartToProto(ch *chartutil.Chart) (chpb *chartpbs.Chart, err error) {
|
|
||||||
chpb = new(chartpbs.Chart)
|
|
||||||
|
|
||||||
chpb.Metadata, err = MetadataToProto(ch)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
chpb.Templates, err = TemplatesToProto(ch)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
chpb.Values, err = ValuesToProto(ch)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
chs, err := WalkChartFile(ch)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dep := range chs.deps {
|
|
||||||
chdep, err := ChartToProto(dep.File())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chpb.Dependencies = append(chpb.Dependencies, chdep)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MetadataToProto converts Chart.yaml data into protocol buffere Metadata.
|
|
||||||
func MetadataToProto(ch *chartutil.Chart) (*chartpbs.Metadata, error) {
|
|
||||||
if ch == nil {
|
|
||||||
return nil, ErrMissingChart
|
|
||||||
}
|
|
||||||
|
|
||||||
chfi := ch.Chartfile()
|
|
||||||
|
|
||||||
md := &chartpbs.Metadata{
|
|
||||||
Name: chfi.Name,
|
|
||||||
Home: chfi.Home,
|
|
||||||
Version: chfi.Version,
|
|
||||||
Description: chfi.Description,
|
|
||||||
}
|
|
||||||
|
|
||||||
md.Sources = make([]string, len(chfi.Source))
|
|
||||||
copy(md.Sources, chfi.Source)
|
|
||||||
|
|
||||||
md.Keywords = make([]string, len(chfi.Keywords))
|
|
||||||
copy(md.Keywords, chfi.Keywords)
|
|
||||||
|
|
||||||
for _, maintainer := range chfi.Maintainers {
|
|
||||||
md.Maintainers = append(md.Maintainers, &chartpbs.Maintainer{
|
|
||||||
Name: maintainer.Name,
|
|
||||||
Email: maintainer.Email,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return md, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplatesToProto converts chart templates to their protobuf representation.
|
|
||||||
func TemplatesToProto(ch *chartutil.Chart) (tpls []*chartpbs.Template, err error) {
|
|
||||||
if ch == nil {
|
|
||||||
return nil, ErrMissingChart
|
|
||||||
}
|
|
||||||
|
|
||||||
members, err := ch.LoadTemplates()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var tpl *chartpbs.Template
|
|
||||||
|
|
||||||
for _, member := range members {
|
|
||||||
tpl = &chartpbs.Template{
|
|
||||||
Name: member.Path,
|
|
||||||
Data: make([]byte, len(member.Content)),
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(tpl.Data, member.Content)
|
|
||||||
|
|
||||||
tpls = append(tpls, tpl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// OverridesToProto converts arbitrary TOML override data to Config data.
|
|
||||||
func OverridesToProto(values []byte) *chartpbs.Config {
|
|
||||||
return &chartpbs.Config{
|
|
||||||
Raw: string(values),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValuesToProto converts a chart's values.toml data to protobuf.
|
|
||||||
func ValuesToProto(ch *chartutil.Chart) (*chartpbs.Config, error) {
|
|
||||||
if ch == nil {
|
|
||||||
return nil, ErrMissingChart
|
|
||||||
}
|
|
||||||
|
|
||||||
vals, err := ch.LoadValues()
|
|
||||||
if err != nil {
|
|
||||||
//return nil, ErrMissingValues
|
|
||||||
vals = map[string]interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err = vals.Encode(&buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfgVals := new(chartpbs.Config)
|
|
||||||
cfgVals.Raw = buf.String()
|
|
||||||
|
|
||||||
return cfgVals, nil
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
chartutil "k8s.io/helm/pkg/chart"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInstallReleaseOverrides(t *testing.T) {
|
|
||||||
// FIXME: This can't currently run unless a Tiller server is running, simply
|
|
||||||
// because --dry-run still uses the server. There's already a WIP for a
|
|
||||||
// testing harness, so this can be ported when that is done.
|
|
||||||
t.Skip()
|
|
||||||
|
|
||||||
vals := `name = "mariner"`
|
|
||||||
ch := "./testdata/albatross"
|
|
||||||
ir, err := InstallRelease([]byte(vals), "foo", ch, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to release: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ir.Release.Manifest) == 0 {
|
|
||||||
t.Fatalf("Expected a manifest.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the result and see if the override worked
|
|
||||||
d := map[string]interface{}{}
|
|
||||||
if err := yaml.Unmarshal([]byte(ir.Release.Manifest), d); err != nil {
|
|
||||||
t.Fatalf("Failed to unmarshal manifest: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d["name"] != "mariner" {
|
|
||||||
t.Errorf("Unexpected name %q", d["name"])
|
|
||||||
}
|
|
||||||
|
|
||||||
if d["home"] != "nest" {
|
|
||||||
t.Errorf("Unexpected home %q", d["home"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOverridesToProto(t *testing.T) {
|
|
||||||
override := []byte(`test = "foo"`)
|
|
||||||
c := OverridesToProto(override)
|
|
||||||
if c.Raw != string(override) {
|
|
||||||
t.Errorf("Expected %q to match %q", c.Raw, override)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChartToProto(t *testing.T) {
|
|
||||||
c, err := chartutil.LoadDir("./testdata/albatross")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to load testdata chart: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := ChartToProto(c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to conver chart to proto: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Metadata.Name != c.Chartfile().Name {
|
|
||||||
t.Errorf("Expected names to match.")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ErrNotImplemented indicates that this API is not implemented.
|
|
||||||
ErrNotImplemented = Error("helm api not implemented")
|
|
||||||
// ErrInvalidSrvAddr indicates an invalid address to the Tiller server.
|
|
||||||
ErrInvalidSrvAddr = Error("invalid tiller address")
|
|
||||||
// ErrMissingTpls indicates that the templates are missing from a chart.
|
|
||||||
ErrMissingTpls = Error("missing chart templates")
|
|
||||||
// ErrMissingChart indicates that the Chart.yaml data is missing.
|
|
||||||
ErrMissingChart = Error("missing chart metadata")
|
|
||||||
// ErrMissingValues indicates that the config values.yaml data is missing.
|
|
||||||
ErrMissingValues = Error("missing chart values")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error represents a Helm client error.
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
// Error returns a string representation of this error.
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
chartutil "k8s.io/helm/pkg/chart"
|
|
||||||
"k8s.io/helm/pkg/proto/hapi/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config defines a gRPC client's configuration.
|
|
||||||
var Config = &config{
|
|
||||||
ServAddr: ":44134",
|
|
||||||
Insecure: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListReleases lists the current releases.
|
|
||||||
func ListReleases(limit int, offset string, sort services.ListSort_SortBy, order services.ListSort_SortOrder, filter string) (*services.ListReleasesResponse, error) {
|
|
||||||
c := Config.client()
|
|
||||||
if err := c.dial(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
req := &services.ListReleasesRequest{
|
|
||||||
Limit: int64(limit),
|
|
||||||
Offset: offset,
|
|
||||||
SortBy: sort,
|
|
||||||
SortOrder: order,
|
|
||||||
Filter: filter,
|
|
||||||
}
|
|
||||||
cli, err := c.impl.ListReleases(context.TODO(), req, c.cfg.CallOpts()...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli.Recv()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReleaseStatus returns the given release's status.
|
|
||||||
func GetReleaseStatus(name string) (*services.GetReleaseStatusResponse, error) {
|
|
||||||
c := Config.client()
|
|
||||||
if err := c.dial(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
req := &services.GetReleaseStatusRequest{Name: name}
|
|
||||||
return c.impl.GetReleaseStatus(context.TODO(), req, c.cfg.CallOpts()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReleaseContent returns the configuration for a given release.
|
|
||||||
func GetReleaseContent(name string) (*services.GetReleaseContentResponse, error) {
|
|
||||||
c := Config.client()
|
|
||||||
if err := c.dial(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
req := &services.GetReleaseContentRequest{Name: name}
|
|
||||||
return c.impl.GetReleaseContent(context.TODO(), req, c.cfg.CallOpts()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRelease updates a release to a new/different chart.
|
|
||||||
// TODO: This must take more than just name for an arg.
|
|
||||||
func UpdateRelease(name string) (*services.UpdateReleaseResponse, error) {
|
|
||||||
return nil, ErrNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
// UninstallRelease uninstalls a named release and returns the response.
|
|
||||||
func UninstallRelease(name string, dryRun bool) (*services.UninstallReleaseResponse, error) {
|
|
||||||
|
|
||||||
if dryRun {
|
|
||||||
// In the dry run case, just see if the release exists.
|
|
||||||
res, err := GetReleaseContent(name)
|
|
||||||
if err != nil {
|
|
||||||
return &services.UninstallReleaseResponse{}, err
|
|
||||||
}
|
|
||||||
return &services.UninstallReleaseResponse{Release: res.Release}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
u := &services.UninstallReleaseRequest{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
return Config.client().uninstall(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstallRelease installs a new chart and returns the release response.
|
|
||||||
func InstallRelease(rawVals []byte, name string, chStr string, dryRun bool) (*services.InstallReleaseResponse, error) {
|
|
||||||
chfi, err := chartutil.LoadChart(chStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chpb, err := ChartToProto(chfi)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
vals := OverridesToProto(rawVals)
|
|
||||||
|
|
||||||
return Config.client().install(&services.InstallReleaseRequest{
|
|
||||||
Chart: chpb,
|
|
||||||
Values: vals,
|
|
||||||
DryRun: dryRun,
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package helmx
|
package helm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1 +0,0 @@
|
|||||||
name = "foo"
|
|
@ -1,4 +0,0 @@
|
|||||||
name: albatross
|
|
||||||
description: testing chart
|
|
||||||
version: 0.1.0
|
|
||||||
home: "https://k8s.io/helm"
|
|
@ -1,3 +0,0 @@
|
|||||||
# Test data. Not a valid Kubernetes manifest
|
|
||||||
name: {{.name}}
|
|
||||||
home: {{.home}}
|
|
@ -1,2 +0,0 @@
|
|||||||
name = "albatross"
|
|
||||||
home = "nest"
|
|
@ -1,115 +0,0 @@
|
|||||||
package helm
|
|
||||||
|
|
||||||
import (
|
|
||||||
chartutil "k8s.io/helm/pkg/chart"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// TODO - we should probably consolidate
|
|
||||||
// most of the code in this package, that
|
|
||||||
// is specific to charts, into chartutil.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Walk a chart's dependency tree, returning
|
|
||||||
// a pointer to the root chart.
|
|
||||||
//
|
|
||||||
// The following is an example chart dependency
|
|
||||||
// hierarchy and the structure of a chartObj
|
|
||||||
// post traversal. (note some chart files are
|
|
||||||
// omitted for brevity),
|
|
||||||
//
|
|
||||||
// mychart/
|
|
||||||
// charts/
|
|
||||||
// chart_A/
|
|
||||||
// charts/
|
|
||||||
// chart_B/
|
|
||||||
// chart_C/
|
|
||||||
// charts/
|
|
||||||
// chart_F/
|
|
||||||
// chart_D/
|
|
||||||
// charts/
|
|
||||||
// chart_E/
|
|
||||||
// chart_F/
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// chart: mychart (deps = 2)
|
|
||||||
// |
|
|
||||||
// |----> chart_A (deps = 2)
|
|
||||||
// |
|
|
||||||
// |--------> chart_B (deps = 0)
|
|
||||||
// |
|
|
||||||
// |--------> chart_C (deps = 1)
|
|
||||||
// |
|
|
||||||
// |------------> chart_F (deps = 0)
|
|
||||||
// |
|
|
||||||
// |----> chart_D (deps = 2)
|
|
||||||
// |
|
|
||||||
// |--------> chart_E (deps = 0)
|
|
||||||
// |
|
|
||||||
// |--------> chart_F (deps = 0)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
// WalkChartFile walks a chart and returns a *chartObj.
|
|
||||||
//
|
|
||||||
// FIXME: Why does an exported function return an unexported struct whose only
|
|
||||||
// exported method is to return the object passed into this method?
|
|
||||||
func WalkChartFile(chfi *chartutil.Chart) (*chartObj, error) {
|
|
||||||
root := &chartObj{file: chfi}
|
|
||||||
err := root.walkChartDeps(chfi)
|
|
||||||
|
|
||||||
return root, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type chartObj struct {
|
|
||||||
file *chartutil.Chart
|
|
||||||
deps []*chartObj
|
|
||||||
}
|
|
||||||
|
|
||||||
// File returns the *chartutil.Chart associated with this *chartObj.
|
|
||||||
func (chd *chartObj) File() *chartutil.Chart {
|
|
||||||
return chd.file
|
|
||||||
}
|
|
||||||
|
|
||||||
func (chs *chartObj) walkChartDeps(chfi *chartutil.Chart) error {
|
|
||||||
if hasDeps(chfi) {
|
|
||||||
names, err := chfi.ChartDepNames()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(names) > 0 {
|
|
||||||
chs.deps = append(chs.deps, resolveChartDeps(names)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveChartDeps(names []string) (deps []*chartObj) {
|
|
||||||
for _, name := range names {
|
|
||||||
chfi, err := chartutil.LoadDir(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
chs := &chartObj{file: chfi}
|
|
||||||
err = chs.walkChartDeps(chfi)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deps = append(deps, chs)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasDeps(chfi *chartutil.Chart) bool {
|
|
||||||
names, err := chfi.ChartDepNames()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return chfi.ChartsDir() != "" && len(names) > 0
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package helmx
|
|
||||||
|
|
||||||
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...)
|
|
||||||
}
|
|
Loading…
Reference in new issue