diff --git a/README.md b/README.md index bbb18d69f..2c35deae0 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ You can reach the Helm community and developers via the following channels: - [Kubernetes Slack](https://slack.k8s.io): #helm - Mailing List: https://groups.google.com/forum/#!forum/kubernetes-sig-apps -- Developer Call: Thursdays at 9:30-10:00 Pacific. https://engineyard.zoom.us/j/366425549 +- Developer Call: Thursdays at 9:30-10:00 Pacific. [https://zoom.us/j/4526666954](https://zoom.us/j/4526666954) ### Code of conduct diff --git a/_proto/hapi/release/log.proto b/_proto/hapi/release/log.proto new file mode 100644 index 000000000..6f5aab057 --- /dev/null +++ b/_proto/hapi/release/log.proto @@ -0,0 +1,49 @@ + +// Copyright 2016 The Kubernetes Authors All rights reserved. +// +// 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. + +syntax = "proto3"; + +package hapi.release; + +import "google/protobuf/timestamp.proto"; + +option go_package = "release"; + +message Log { + // Allows filtering by log event source + enum Source { + HOOK = 0; + TEST = 1; + POD = 2; + SYSTEM = 3; + } + + // Syslog log levels + enum LogLevel { + EMERG = 0; + ALERT = 1; + CRIT = 2; + ERR = 3; + WARNING = 4; + NOTICE = 5; + INFO = 6; + DEBUG = 7; + } + + Source source = 1; + string release = 2; + string log = 3; + google.protobuf.Timestamp timestamp = 4; +} diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index 068763511..4e6076650 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -194,10 +194,7 @@ message GetReleaseLogsRequest { } message GetReleaseLogsResponse { - // Source is the name of the release that generated the log - string source = 1; - // Log is a single log line - string log = 2; + hapi.release.Log log = 1; } // UpdateReleaseRequest updates a release. diff --git a/cmd/helm/dependency_update.go b/cmd/helm/dependency_update.go index bd2183cff..1838acb0a 100644 --- a/cmd/helm/dependency_update.go +++ b/cmd/helm/dependency_update.go @@ -100,7 +100,7 @@ func (d *dependencyUpdateCmd) run() error { if d.verify { man.Verify = downloader.VerifyIfPossible } - if settings.FlagDebug { + if settings.Debug { man.Debug = true } return man.Update() diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 113cf6b14..ced336ac4 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -105,7 +105,7 @@ func newRootCmd(out io.Writer) *cobra.Command { settings.Home = helmpath.Home(helmHomeTemp) p.StringVar(&settings.TillerHost, "host", helm_env.DefaultHelmHost(), "address of tiller. Overrides $HELM_HOST") p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use") - p.BoolVar(&settings.FlagDebug, "debug", false, "enable verbose output") + p.BoolVar(&settings.Debug, "debug", false, "enable verbose output") p.StringVar(&settings.TillerNamespace, "tiller-namespace", tiller_env.GetTillerNamespace(), "namespace of tiller") if os.Getenv(helm_env.PluginDisableEnvVar) != "1" { @@ -190,15 +190,12 @@ func setupConnection(c *cobra.Command, args []string) error { } settings.TillerHost = fmt.Sprintf("localhost:%d", tunnel.Local) - if settings.FlagDebug { - fmt.Printf("Created tunnel using local port: '%d'\n", tunnel.Local) - } + debug("Created tunnel using local port: '%d'\n", tunnel.Local) } // Set up the gRPC config. - if settings.FlagDebug { - fmt.Printf("SERVER: %q\n", settings.TillerHost) - } + debug("SERVER: %q\n", settings.TillerHost) + // Plugin support. return nil } diff --git a/cmd/helm/home.go b/cmd/helm/home.go index 6f2647db0..25809b521 100644 --- a/cmd/helm/home.go +++ b/cmd/helm/home.go @@ -36,7 +36,7 @@ func newHomeCmd(out io.Writer) *cobra.Command { Run: func(cmd *cobra.Command, args []string) { h := settings.Home fmt.Fprintf(out, "%s\n", h) - if settings.FlagDebug { + if settings.Debug { fmt.Fprintf(out, "Repository: %s\n", h.Repository()) fmt.Fprintf(out, "RepositoryFile: %s\n", h.RepositoryFile()) fmt.Fprintf(out, "Cache: %s\n", h.Cache()) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index f08dc1ed5..a2bec7a65 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -155,7 +155,7 @@ func (i *initCmd) run() error { i.opts.UseCanary = i.canary i.opts.ImageSpec = i.image - if settings.FlagDebug { + if settings.Debug { writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error { w := i.out if !first { diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 03fc844f0..4d82c297b 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -140,11 +140,11 @@ func TestInitCmd_dryRun(t *testing.T) { if err != nil { t.Fatal(err) } - dbg := settings.FlagDebug - settings.FlagDebug = true + dbg := settings.Debug + settings.Debug = true defer func() { os.Remove(home) - settings.FlagDebug = dbg + settings.Debug = dbg }() var buf bytes.Buffer diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 4d451d6d3..36ce27d4f 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -195,9 +195,7 @@ func streamLogs(client helm.Interface, rlsName string, done <-chan struct{}) err } func (i *installCmd) run() error { - if settings.FlagDebug { - fmt.Fprintf(i.out, "CHART PATH: %s\n", i.chartPath) - } + debug("CHART PATH: %s\n", i.chartPath) if i.namespace == "" { i.namespace = defaultNamespace() @@ -324,14 +322,14 @@ func (i *installCmd) vals() ([]byte, error) { return yaml.Marshal(base) } -// printRelease prints info about a release if the flagDebug is true. +// printRelease prints info about a release if the Debug is true. func (i *installCmd) printRelease(rel *release.Release) { if rel == nil { return } // TODO: Switch to text/template like everything else. fmt.Fprintf(i.out, "NAME: %s\n", rel.Name) - if settings.FlagDebug { + if settings.Debug { printRelease(i.out, rel) } } @@ -390,11 +388,9 @@ func locateChartPath(name, version string, verify bool, keyring string) (string, if err != nil { return filename, err } - if settings.FlagDebug { - fmt.Printf("Fetched %s to %s\n", name, filename) - } + debug("Fetched %s to %s\n", name, filename) return lname, nil - } else if settings.FlagDebug { + } else if settings.Debug { return filename, err } diff --git a/cmd/helm/package.go b/cmd/helm/package.go index 89ec2b18e..a2da2c338 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -119,9 +119,7 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { if err := setVersion(ch, p.version); err != nil { return err } - if settings.FlagDebug { - fmt.Fprintf(p.out, "Setting version to %s", p.version) - } + debug("Setting version to %s", p.version) } if filepath.Base(path) != ch.Metadata.Name { @@ -145,8 +143,8 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { } name, err := chartutil.Save(ch, dest) - if err == nil && settings.FlagDebug { - fmt.Fprintf(p.out, "Saved %s to current directory\n", name) + if err == nil { + debug("Saved %s to current directory\n", name) } // Save to $HELM_HOME/local directory. This is second, because we don't want @@ -155,9 +153,8 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { lr := p.home.LocalRepository() if err := repo.AddChartToLocalRepo(ch, lr); err != nil { return err - } else if settings.FlagDebug { - fmt.Fprintf(p.out, "Saved %s to %s\n", name, lr) } + debug("Saved %s to %s\n", name, lr) } if p.sign { @@ -194,9 +191,7 @@ func (p *packageCmd) clearsign(filename string) error { return err } - if settings.FlagDebug { - fmt.Fprintln(p.out, sig) - } + debug(sig) return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) } diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index e56c3b229..7ab4d21fa 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -59,7 +59,7 @@ func (pcmd *pluginInstallCmd) complete(args []string) error { } func (pcmd *pluginInstallCmd) run() error { - installer.Debug = settings.FlagDebug + installer.Debug = settings.Debug i, err := installer.NewForSource(pcmd.source, pcmd.version, pcmd.home) if err != nil { diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index cd0e3da74..095de7063 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -162,9 +162,9 @@ func TestSetupEnv(t *testing.T) { settings.Home = helmpath.Home("testdata/helmhome") base := filepath.Join(settings.Home.Plugins(), name) settings.PlugDirs = settings.Home.Plugins() - settings.FlagDebug = true + settings.Debug = true defer func() { - settings.FlagDebug = false + settings.Debug = false }() plugin.SetupPluginEnv(settings, name, base) diff --git a/cmd/helm/printer.go b/cmd/helm/printer.go index a8b04dfda..ebb24bf7d 100644 --- a/cmd/helm/printer.go +++ b/cmd/helm/printer.go @@ -75,7 +75,7 @@ func tpl(t string, vals map[string]interface{}, out io.Writer) error { } func debug(format string, args ...interface{}) { - if settings.FlagDebug { + if settings.Debug { format = fmt.Sprintf("[debug] %s\n", format) fmt.Printf(format, args...) } diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index bc4dc2971..ea8d667a1 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -185,7 +185,7 @@ func (u *upgradeCmd) run() error { return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) } - if settings.FlagDebug { + if settings.Debug { printRelease(u.out, resp.Release) } diff --git a/cmd/helm/version.go b/cmd/helm/version.go index bba79a8c1..64c66b348 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "io" - "os" "github.com/spf13/cobra" "google.golang.org/grpc" @@ -105,9 +104,7 @@ func (v *versionCmd) run() error { if grpc.Code(err) == codes.Unimplemented { return errors.New("server is too old to know its version") } - if settings.FlagDebug { - fmt.Fprintln(os.Stderr, err) - } + debug("%s", err) return errors.New("cannot connect to Tiller") } fmt.Fprintf(v.out, "Server: %s\n", formatVersion(resp.Version, v.short)) diff --git a/docs/developers.md b/docs/developers.md index 4594e0c89..ec17d2fa2 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -56,7 +56,7 @@ Helm and Tiller communicate using gRPC. To get started with gRPC, you will need - Run Helm's `make bootstrap` to generate the `protoc-gen-go` plugin and place it in `bin/`. -Note that you need to be on protobuf 3.x (`protoc --version`). The +Note that you need to be on protobuf 3.2.0 (`protoc --version`). The version of `protoc-gen-go` is tied to the version of gRPC used in Kubernetes. So the plugin is maintained locally. diff --git a/docs/plugins.md b/docs/plugins.md index 99f150874..72e78c25e 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -4,6 +4,8 @@ Helm 2.1.0 introduced the concept of a client-side Helm _plugin_. A plugin is a tool that can be accessed through the `helm` CLI, but which is not part of the built-in Helm codebase. +Existing plugins can be found on [related](related.md#helm-plugins) section or by searching [Github](https://github.com/search?q=topic%3Ahelm-plugin&type=Repositories). + This guide explains how to use and create plugins. ## An Overview @@ -170,4 +172,3 @@ these flags are _not_ passed on to the plugin. Plugins _should_ display help text and then exit for `-h` and `--help`. In all other cases, plugins may use flags as appropriate. - diff --git a/glide.lock b/glide.lock index e24c5b5ce..0dd280f98 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 59f320c07649cfd057b84f1044074670230fa3ca27b32632eb77a16a972adc8e -updated: 2017-04-20T16:44:51.583176673-07:00 +updated: 2017-04-24T14:43:55.783514424-07:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 0fe071b79..02f9097d2 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -403,18 +403,20 @@ func (h *Client) logs(ctx context.Context, req *rls.GetReleaseLogsRequest, done go func() { defer close(out) defer c.Close() - select { - case rs := s.Recv(): - if err == io.EOF { - return - } - if err != nil { - fmt.Println("gRPC error streaming logs: ", grpc.ErrorDesc(err)) + for { + select { + case rs := s.Recv(): + if err == io.EOF { + return + } + if err != nil { + fmt.Println("gRPC error streaming logs: ", grpc.ErrorDesc(err)) + return + } + out <- rs + case <-done: return } - out <- rs - case <-done: - return } }() diff --git a/pkg/helm/environment/environment.go b/pkg/helm/environment/environment.go index 6a24a624c..22fb43ae7 100644 --- a/pkg/helm/environment/environment.go +++ b/pkg/helm/environment/environment.go @@ -52,5 +52,5 @@ type EnvSettings struct { TillerNamespace string Home helmpath.Home PlugDirs string - FlagDebug bool + Debug bool } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 6bcb5a186..8bde6ad24 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -195,7 +195,7 @@ func SetupPluginEnv(settings helm_env.EnvSettings, os.Setenv(key, val) } - if settings.FlagDebug { + if settings.Debug { os.Setenv("HELM_DEBUG", "1") } } diff --git a/pkg/tiller/logdistributor/log_streamer.go b/pkg/tiller/logdistributor/log_streamer.go new file mode 100644 index 000000000..bb4c90b0c --- /dev/null +++ b/pkg/tiller/logdistributor/log_streamer.go @@ -0,0 +1,48 @@ +package logdistributor + +import "fmt" + +type Log struct { + Log string +} + +type Subscription struct { + c chan<- *Log +} + +type Listener struct { + subs map[*Subscription]bool +} + +type Distributor struct { + listeners map[string]*Listener +} + +func (l *Listener) subscribe(c chan<- *Log) *Subscription { + sub := &Subscription{c} + l.subs[sub] = true + return sub +} + +func (d *Distributor) Subscribe() { + +} + +func (l *Listener) unsubscribe(sub *Subscription) { + delete(l.subs, sub) +} + +func (l *Listener) writeLog(log *Log) error { + for _, s := range l.subs { + s.c <- log + } + return nil +} + +func (d *Distributor) WriteLog(log *Log, release string) error { + l := d.listeners[release] + if l == nil { + return fmt.Errorf("No listeners configured for %s", release) + } + return l.writeLog(log) +} diff --git a/pkg/tiller/logdistributor/log_streamer_test.go b/pkg/tiller/logdistributor/log_streamer_test.go new file mode 100644 index 000000000..b12dfcb76 --- /dev/null +++ b/pkg/tiller/logdistributor/log_streamer_test.go @@ -0,0 +1,42 @@ +package logdistributor + +import ( + "testing" + "fmt" +) + +func TestDistributor_WriteLog(t *testing.T) { + d := &Distributor{} + l := &Log{Log: "Test log"} + d.WriteLog(l, "testrelease") + + if len(d.listeners) != 1 { + t.Errorf("Invalid number of listeners present: %d (expecting 1)", len(d.listeners)) + } +} + +func BenchmarkDistributor_WriteLog(b *testing.B) { +} + +func ExampleDistributor_WriteLog() { + sub := &Subscription{} + c := make(chan *Log) + sub.c = c + + go func(){ + for l := range c { + fmt.Println(l.Log) + } + + for { + select { + case l := <-c: + fmt.Println(l.Log) + } + } + }() + + sub.c <- &Log{Log: "Test log!"} + // Output: Test log! +} +