Skip to content

Commit

Permalink
Repair 'helm init --wait': context deadline exceeded
Browse files Browse the repository at this point in the history
Wait now works that way:
* Connect to kubernetes api and wait till tiller pod appeared:
    * Poll every 0.5s for tiller pod name.
    * Timeout is 5 minutes.
* Create connection over grpc and execute PingTiller.

Also:
* `setupConnection` function is not taking arguments now to be used for `helm init --wait`.
* Hook for cmd renamed to `setupConnectionCobraPreRunHook`.
* PingTiller grpc method is now generated from `tiller.proto`.
* `github.com/golang/protobuf` in glide has been updated to the commit where generated comment `DO NOT EDIT` is in the right place.

Accepted PR #3238 problems:
* PingTiller method adds directly into generated tiller.pb.go file (with `DO NOT EDIT` comment). Thus after running `make protoc` PingTiller disappears from `tiller.pb.go` file leading to `make build` errors.
* Also this code is not working because client object doesn't have information about TillerHost so pingTiller uses default host and port and always returns error `Error: could not ping Tiller: context deadline exceeded`.

fixes #3379
  • Loading branch information
alexey-igrychev committed Feb 19, 2018
1 parent b6ab6a2 commit 87d03e7
Show file tree
Hide file tree
Showing 28 changed files with 304 additions and 156 deletions.
4 changes: 2 additions & 2 deletions _proto/hapi/chart/metadata.proto
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,6 @@ message Metadata {
// made available for inspection by other applications.
map<string,string> annotations = 16;

// KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
string kubeVersion = 17;
// KubeVersion is a SemVer constraints on what version of Kubernetes is required.
string kubeVersion = 17;
}
10 changes: 10 additions & 0 deletions _proto/hapi/services/tiller.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ service ReleaseService {
// RunReleaseTest executes the tests defined of a named release
rpc RunReleaseTest(TestReleaseRequest) returns (stream TestReleaseResponse) {
}

// PingTiller sends a test/pingTiller signal to Tiller to ensure that it's up
rpc PingTiller(PingTillerRequest) returns (PingTillerResponse) {
}
}

// ListReleasesRequest requests a list of releases.
Expand Down Expand Up @@ -335,3 +339,9 @@ message TestReleaseResponse {
hapi.release.TestRun.Status status = 2;

}

message PingTillerRequest {
}

message PingTillerResponse {
}
2 changes: 1 addition & 1 deletion cmd/helm/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
SuggestFor: []string{"remove", "rm"},
Short: "given a release name, delete the release from Kubernetes",
Long: deleteDesc,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("command 'delete' requires a release name")
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "get [flags] RELEASE_NAME",
Short: "download a named release",
Long: getHelp,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/get_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "hooks [flags] RELEASE_NAME",
Short: "download all hooks for a named release",
Long: getHooksHelp,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/get_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "manifest [flags] RELEASE_NAME",
Short: "download the manifest for a named release",
Long: getManifestHelp,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/get_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "values [flags] RELEASE_NAME",
Short: "download the values file for a named release",
Long: getValuesHelp,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
Expand Down
23 changes: 22 additions & 1 deletion cmd/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,28 @@ func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
return cmd
}

func setupConnection(c *cobra.Command, args []string) error {
func waitTillerPodAndSetupConnection(waitTimeout int) error {
_, client, err := getKubeClient(settings.KubeContext)
if err != nil {
return err
}

if portforwarder.WaitTillerPod(client.CoreV1(), settings.TillerNamespace, waitTimeout) {
if err = setupConnection(); err != nil {
return err
}
} else {
return fmt.Errorf("tiller pod not found, polling deadline exceeded")
}

return nil
}

func setupConnectionCobraPreRunHook(c *cobra.Command, args []string) error {
return setupConnection()
}

func setupConnection() error {
if settings.TillerHost == "" {
config, client, err := getKubeClient(settings.KubeContext)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
Long: historyHelp,
Short: "fetch release history",
Aliases: []string{"hist"},
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
switch {
case len(args) == 0:
Expand Down
18 changes: 11 additions & 7 deletions cmd/helm/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo"
)
Expand Down Expand Up @@ -80,13 +79,13 @@ type initCmd struct {
forceUpgrade bool
skipRefresh bool
out io.Writer
client helm.Interface
home helmpath.Home
opts installer.Options
kubeClient kubernetes.Interface
serviceAccount string
maxHistory int
wait bool
waitTimeout int
}

func newInitCmd(out io.Writer) *cobra.Command {
Expand All @@ -102,7 +101,6 @@ func newInitCmd(out io.Writer) *cobra.Command {
}
i.namespace = settings.TillerNamespace
i.home = settings.Home
i.client = ensureHelmClient(i.client)

return i.run()
},
Expand All @@ -117,6 +115,7 @@ func newInitCmd(out io.Writer) *cobra.Command {
f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote")
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache")
f.BoolVar(&i.wait, "wait", false, "block until Tiller is running and ready to receive requests")
f.IntVar(&i.waitTimeout, "wait-timeout", 300, "time in seconds to wait for tiller pod readiness")

f.BoolVar(&tlsEnable, "tiller-tls", false, "install Tiller with TLS enabled")
f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install Tiller with TLS enabled and to verify remote certificates")
Expand Down Expand Up @@ -298,7 +297,7 @@ func (i *initCmd) run() error {
if err := installer.Upgrade(i.kubeClient, &i.opts); err != nil {
return fmt.Errorf("error when upgrading: %s", err)
}
if err := i.ping(); err != nil {
if err := i.ensureTillerReady(); err != nil {
return err
}
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been upgraded to the current version.")
Expand All @@ -307,7 +306,7 @@ func (i *initCmd) run() error {
"(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)")
}
} else {
if err := i.ping(); err != nil {
if err := i.ensureTillerReady(); err != nil {
return err
}
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been installed into your Kubernetes Cluster.")
Expand All @@ -320,9 +319,14 @@ func (i *initCmd) run() error {
return nil
}

func (i *initCmd) ping() error {
func (i *initCmd) ensureTillerReady() error {
if i.wait {
if err := i.client.PingTiller(); err != nil {
if err := waitTillerPodAndSetupConnection(i.waitTimeout); err != nil {
return err
}

client := newClient()
if err := client.PingTiller(); err != nil {
return fmt.Errorf("could not ping Tiller: %s", err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
Use: "install [CHART]",
Short: "install a chart archive",
Long: installDesc,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "chart name"); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
Short: "list releases",
Long: listHelp,
Aliases: []string{"ls"},
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
list.filter = strings.Join(args, " ")
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/load_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
if _, err := processParent(cmd, args); err != nil {
return err
}
return setupConnection(cmd, args)
return setupConnectionCobraPreRunHook(cmd, args)
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/release_testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
Use: "test [RELEASE]",
Short: "test a release",
Long: releaseTestDesc,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "release name"); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
Short: "uninstalls Tiller from a cluster",
Long: resetDesc,
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := setupConnection(cmd, args); !d.force && err != nil {
if err := setupConnectionCobraPreRunHook(cmd, args); !d.force && err != nil {
return err
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/rollback.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
Use: "rollback [flags] [RELEASE] [REVISION]",
Short: "roll back a release to a previous revision",
Long: rollbackDesc,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "release name", "revision number"); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "status [flags] RELEASE_NAME",
Short: "displays the status of the named release",
Long: statusHelp,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
Use: "upgrade [RELEASE] [CHART]",
Short: "upgrade a release",
Long: upgradeDesc,
PreRunE: setupConnection,
PreRunE: setupConnectionCobraPreRunHook,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func newVersionCmd(c helm.Interface, out io.Writer) *cobra.Command {
if version.showServer {
// We do this manually instead of in PreRun because we only
// need a tunnel if server version is requested.
setupConnection(cmd, args)
setupConnectionCobraPreRunHook(cmd, args)
}
version.client = ensureHelmClient(version.client)
return version.run()
Expand Down
3 changes: 2 additions & 1 deletion docs/helm/helm_init.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ helm init
--tls-ca-cert string path to CA root certificate
--upgrade upgrade if Tiller is already installed
--wait block until Tiller is running and ready to receive requests
--wait-timeout int time in seconds to wait for tiller pod readiness (default 300)
```

### Options inherited from parent commands
Expand All @@ -69,4 +70,4 @@ helm init
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.

###### Auto generated by spf13/cobra on 25-Jan-2018
###### Auto generated by spf13/cobra on 16-Feb-2018
6 changes: 3 additions & 3 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import:
version: ~1.3.1
- package: github.com/technosophos/moniker
- package: github.com/golang/protobuf
version: 4bd1920723d7b7c925de087aa32e2187708897f7
version: 47eb67eaf5cab63c58956d4d4ce86b03ad5eaa03
subpackages:
- proto
- ptypes/any
Expand Down
10 changes: 6 additions & 4 deletions pkg/helm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func (h *Client) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-ch
// PingTiller pings the Tiller pod and ensure's that it is up and running
func (h *Client) PingTiller() error {
ctx := NewContext()
return h.ping(ctx)
return h.pingTiller(ctx)
}

// connect returns a gRPC connection to Tiller or error. The gRPC dial options
Expand Down Expand Up @@ -474,14 +474,16 @@ func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan
return ch, errc
}

// Executes tiller.Ping RPC.
func (h *Client) ping(ctx context.Context) error {
// Executes tiller.PingTiller RPC.
func (h *Client) pingTiller(ctx context.Context) error {
c, err := h.connect(ctx)
if err != nil {
return err
}
defer c.Close()

rlc := rls.NewReleaseServiceClient(c)
return rlc.PingTiller(ctx)
_, err = rlc.PingTiller(ctx, &rls.PingTillerRequest{})

return err
}
33 changes: 33 additions & 0 deletions pkg/helm/portforwarder/portforwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/client-go/rest"

"k8s.io/helm/pkg/kube"
"time"
)

var (
Expand All @@ -53,6 +54,38 @@ func getTillerPodName(client corev1.PodsGetter, namespace string) (string, error
return pod.ObjectMeta.GetName(), nil
}

func WaitTillerPod(client corev1.PodsGetter, namespace string, waitTimeout int) bool {
deadlinePollingChan := time.NewTimer(time.Duration(waitTimeout) * time.Second).C
checkTillerPodTicker := time.NewTicker(500 * time.Millisecond)
doneChan := make(chan bool)

go func() {
for range checkTillerPodTicker.C {
_, err := getTillerPodName(client, namespace)
if err == nil {
doneChan <- true
break
}
}
}()

podExists := false
loop:
for {
select {
case <-deadlinePollingChan:
break loop
case <-doneChan:
podExists = true
break loop
}
}

checkTillerPodTicker.Stop()

return podExists
}

func getFirstRunningPod(client corev1.PodsGetter, namespace string, selector labels.Selector) (*v1.Pod, error) {
options := metav1.ListOptions{LabelSelector: selector.String()}
pods, err := client.Pods(namespace).List(options)
Expand Down
Loading

0 comments on commit 87d03e7

Please sign in to comment.