mirror of
https://github.com/helm/helm.git
synced 2026-04-15 21:59:50 -04:00
Merge pull request #916 from technosophos/feat/901-hooks
feat(tiller): support hooks for install
This commit is contained in:
commit
e91bbceff2
17 changed files with 742 additions and 63 deletions
45
_proto/hapi/release/hook.proto
Normal file
45
_proto/hapi/release/hook.proto
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// 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";
|
||||
|
||||
// Hook defines a hook object.
|
||||
message Hook {
|
||||
enum Event {
|
||||
UNKNOWN = 0;
|
||||
PRE_INSTALL = 1;
|
||||
POST_INSTALL = 2;
|
||||
PRE_DELETE = 3;
|
||||
POST_DELETE = 4;
|
||||
PRE_UPGRADE = 5;
|
||||
POST_UPGRADE = 6;
|
||||
}
|
||||
string name = 1;
|
||||
// Kind is the Kubernetes kind.
|
||||
string kind = 2;
|
||||
// Path is the chart-relative path to the template.
|
||||
string path = 3;
|
||||
// Manifest is the manifest contents.
|
||||
string manifest = 4;
|
||||
// Events are the events that this hook fires on.
|
||||
repeated Event events = 5;
|
||||
// LastRun indicates the date/time this was last run.
|
||||
google.protobuf.Timestamp last_run = 6;
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ syntax = "proto3";
|
|||
|
||||
package hapi.release;
|
||||
|
||||
import "hapi/release/hook.proto";
|
||||
import "hapi/release/info.proto";
|
||||
import "hapi/chart/config.proto";
|
||||
import "hapi/chart/chart.proto";
|
||||
|
|
@ -40,4 +41,7 @@ message Release {
|
|||
|
||||
// Manifest is the string representation of the rendered template.
|
||||
string manifest = 5;
|
||||
|
||||
// Hooks are all of the hooks declared for this release.
|
||||
repeated hapi.release.Hook hooks = 6;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,13 @@ type KubeClient interface {
|
|||
// reader must contain a YAML stream (one or more YAML documents separated
|
||||
// by "\n---\n").
|
||||
Delete(namespace string, reader io.Reader) error
|
||||
|
||||
// Watch the resource in reader until it is "ready".
|
||||
//
|
||||
// For Jobs, "ready" means the job ran to completion (excited without error).
|
||||
// For all other kinds, it means the kind was created or modified without
|
||||
// error.
|
||||
WatchUntilReady(namespace string, reader io.Reader) error
|
||||
}
|
||||
|
||||
// PrintingKubeClient implements KubeClient, but simply prints the reader to
|
||||
|
|
@ -179,6 +186,12 @@ func (p *PrintingKubeClient) Delete(ns string, r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// WatchUntilReady implements KubeClient WatchUntilReady.
|
||||
func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader) error {
|
||||
_, err := io.Copy(p.Out, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// Environment provides the context for executing a client request.
|
||||
//
|
||||
// All services in a context are concurrency safe.
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ func (k *mockKubeClient) Create(ns string, r io.Reader) error {
|
|||
func (k *mockKubeClient) Delete(ns string, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Engine = &mockEngine{}
|
||||
var _ ReleaseStorage = &mockReleaseStorage{}
|
||||
|
|
|
|||
107
cmd/tiller/hooks.go
Normal file
107
cmd/tiller/hooks.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
// hookAnno is the label name for a hook
|
||||
const hookAnno = "helm.sh/hook"
|
||||
|
||||
const (
|
||||
preInstall = "pre-install"
|
||||
postInstall = "post-install"
|
||||
preDelete = "pre-delete"
|
||||
postDelete = "post-delete"
|
||||
preUpgrade = "pre-upgrade"
|
||||
postUpgrade = "post-upgrade"
|
||||
)
|
||||
|
||||
var events = map[string]release.Hook_Event{
|
||||
preInstall: release.Hook_PRE_INSTALL,
|
||||
postInstall: release.Hook_POST_INSTALL,
|
||||
preDelete: release.Hook_PRE_DELETE,
|
||||
postDelete: release.Hook_POST_DELETE,
|
||||
preUpgrade: release.Hook_PRE_UPGRADE,
|
||||
postUpgrade: release.Hook_POST_UPGRADE,
|
||||
}
|
||||
|
||||
type simpleHead struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Metadata *struct {
|
||||
Name string `json:"name"`
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// sortHooks takes a map of filename/YAML contents and sorts them into hook types.
|
||||
//
|
||||
// The resulting hooks struct will be populated with all of the generated hooks.
|
||||
// Any file that does not declare one of the hook types will be placed in the
|
||||
// 'generic' bucket.
|
||||
//
|
||||
// To determine hook type, this looks for a YAML structure like this:
|
||||
//
|
||||
// kind: SomeKind
|
||||
// metadata:
|
||||
// annotations:
|
||||
// helm.sh/hook: pre-install
|
||||
//
|
||||
// Where HOOK_NAME is one of the known hooks.
|
||||
//
|
||||
// If a file declares more than one hook, it will be copied into all of the applicable
|
||||
// hook buckets. (Note: label keys are not unique within the labels section).
|
||||
//
|
||||
// Files that do not parse into the expected format are simply placed into a map and
|
||||
// returned.
|
||||
func sortHooks(files map[string]string) (hs []*release.Hook, generic map[string]string) {
|
||||
hs = []*release.Hook{}
|
||||
generic = map[string]string{}
|
||||
|
||||
for n, c := range files {
|
||||
var sh simpleHead
|
||||
err := yaml.Unmarshal([]byte(c), &sh)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("YAML parse error on %s: %s (skipping)", n, err)
|
||||
}
|
||||
|
||||
if sh.Metadata == nil || sh.Metadata.Annotations == nil || len(sh.Metadata.Annotations) == 0 {
|
||||
generic[n] = c
|
||||
continue
|
||||
}
|
||||
|
||||
hookTypes, ok := sh.Metadata.Annotations[hookAnno]
|
||||
if !ok {
|
||||
generic[n] = c
|
||||
continue
|
||||
}
|
||||
h := &release.Hook{
|
||||
Name: sh.Metadata.Name,
|
||||
Kind: sh.Kind,
|
||||
Path: n,
|
||||
Manifest: c,
|
||||
Events: []release.Hook_Event{},
|
||||
}
|
||||
|
||||
isHook := false
|
||||
for _, hookType := range strings.Split(hookTypes, ",") {
|
||||
hookType = strings.ToLower(strings.TrimSpace(hookType))
|
||||
e, ok := events[hookType]
|
||||
if ok {
|
||||
isHook = true
|
||||
h.Events = append(h.Events, e)
|
||||
}
|
||||
}
|
||||
|
||||
if !isHook {
|
||||
log.Printf("info: skipping unknown hook: %q", hookTypes)
|
||||
continue
|
||||
}
|
||||
hs = append(hs, h)
|
||||
}
|
||||
return
|
||||
}
|
||||
121
cmd/tiller/hooks_test.go
Normal file
121
cmd/tiller/hooks_test.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestSortHooks(t *testing.T) {
|
||||
|
||||
data := []struct {
|
||||
name string
|
||||
path string
|
||||
kind string
|
||||
hooks []release.Hook_Event
|
||||
manifest string
|
||||
}{
|
||||
{
|
||||
name: "first",
|
||||
path: "one",
|
||||
kind: "Job",
|
||||
hooks: []release.Hook_Event{release.Hook_PRE_INSTALL},
|
||||
manifest: `apiVersion: v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: first
|
||||
labels:
|
||||
doesnt: matter
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "second",
|
||||
path: "two",
|
||||
kind: "ReplicaSet",
|
||||
hooks: []release.Hook_Event{release.Hook_POST_INSTALL},
|
||||
manifest: `kind: ReplicaSet
|
||||
metadata:
|
||||
name: second
|
||||
annotations:
|
||||
"helm.sh/hook": post-install
|
||||
`,
|
||||
}, {
|
||||
name: "third",
|
||||
path: "three",
|
||||
kind: "ReplicaSet",
|
||||
hooks: []release.Hook_Event{},
|
||||
manifest: `kind: ReplicaSet
|
||||
metadata:
|
||||
name: third
|
||||
annotations:
|
||||
"helm.sh/hook": no-such-hook
|
||||
`,
|
||||
}, {
|
||||
name: "fourth",
|
||||
path: "four",
|
||||
kind: "Pod",
|
||||
hooks: []release.Hook_Event{},
|
||||
manifest: `kind: Pod
|
||||
metadata:
|
||||
name: fourth
|
||||
annotations:
|
||||
nothing: here
|
||||
`,
|
||||
}, {
|
||||
name: "fifth",
|
||||
path: "five",
|
||||
kind: "ReplicaSet",
|
||||
hooks: []release.Hook_Event{release.Hook_POST_DELETE, release.Hook_POST_INSTALL},
|
||||
manifest: `kind: ReplicaSet
|
||||
metadata:
|
||||
name: fifth
|
||||
annotations:
|
||||
"helm.sh/hook": post-delete, post-install
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
manifests := make(map[string]string, len(data))
|
||||
for _, o := range data {
|
||||
manifests[o.path] = o.manifest
|
||||
}
|
||||
|
||||
hs, generic := sortHooks(manifests)
|
||||
|
||||
if len(generic) != 1 {
|
||||
t.Errorf("Expected 1 generic manifest, got %d", len(generic))
|
||||
}
|
||||
|
||||
if len(hs) != 3 {
|
||||
t.Errorf("Expected 3 hooks, got %d", len(hs))
|
||||
}
|
||||
|
||||
for _, out := range hs {
|
||||
found := false
|
||||
for _, expect := range data {
|
||||
if out.Path == expect.path {
|
||||
found = true
|
||||
if out.Path != expect.path {
|
||||
t.Errorf("Expected path %s, got %s", expect.path, out.Path)
|
||||
}
|
||||
if out.Name != expect.name {
|
||||
t.Errorf("Expected name %s, got %s", expect.name, out.Name)
|
||||
}
|
||||
if out.Kind != expect.kind {
|
||||
t.Errorf("Expected kind %s, got %s", expect.kind, out.Kind)
|
||||
}
|
||||
for i := 0; i < len(out.Events); i++ {
|
||||
if out.Events[i] != expect.hooks[i] {
|
||||
t.Errorf("Expected event %d, got %d", expect.hooks[i], out.Events[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Result not found: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -216,6 +216,16 @@ func (s *releaseServer) engine(ch *chart.Chart) environment.Engine {
|
|||
}
|
||||
|
||||
func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
|
||||
rel, err := s.prepareRelease(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.performRelease(rel, req)
|
||||
}
|
||||
|
||||
// prepareRelease builds a release for an install operation.
|
||||
func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) {
|
||||
if req.Chart == nil {
|
||||
return nil, errMissingChart
|
||||
}
|
||||
|
|
@ -237,9 +247,11 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hooks, manifests := sortHooks(files)
|
||||
|
||||
// Aggregate all non-hooks into one big doc.
|
||||
b := bytes.NewBuffer(nil)
|
||||
for name, file := range files {
|
||||
for name, file := range manifests {
|
||||
// Ignore templates that starts with underscore to handle them as partials
|
||||
if strings.HasPrefix(path.Base(name), "_") {
|
||||
continue
|
||||
|
|
@ -254,7 +266,7 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
|
|||
}
|
||||
|
||||
// Store a release.
|
||||
r := &release.Release{
|
||||
rel := &release.Release{
|
||||
Name: name,
|
||||
Chart: req.Chart,
|
||||
Config: req.Values,
|
||||
|
|
@ -264,22 +276,40 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
|
|||
Status: &release.Status{Code: release.Status_UNKNOWN},
|
||||
},
|
||||
Manifest: b.String(),
|
||||
Hooks: hooks,
|
||||
}
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// performRelease runs a release.
|
||||
func (s *releaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
|
||||
res := &services.InstallReleaseResponse{Release: r}
|
||||
|
||||
if req.DryRun {
|
||||
log.Printf("Dry run for %s", name)
|
||||
log.Printf("Dry run for %s", r.Name)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if err := s.env.KubeClient.Create(s.env.Namespace, b); err != nil {
|
||||
// pre-install hooks
|
||||
if err := s.execHook(r.Hooks, r.Name, preInstall); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// regular manifests
|
||||
kubeCli := s.env.KubeClient
|
||||
b := bytes.NewBufferString(r.Manifest)
|
||||
if err := kubeCli.Create(s.env.Namespace, b); err != nil {
|
||||
r.Info.Status.Code = release.Status_FAILED
|
||||
log.Printf("warning: Release %q failed: %s", name, err)
|
||||
log.Printf("warning: Release %q failed: %s", r.Name, err)
|
||||
if err := s.env.Releases.Create(r); err != nil {
|
||||
log.Printf("warning: Failed to record release %q: %s", name, err)
|
||||
log.Printf("warning: Failed to record release %q: %s", r.Name, err)
|
||||
}
|
||||
return res, fmt.Errorf("release %s failed: %s", name, err)
|
||||
return res, fmt.Errorf("release %s failed: %s", r.Name, err)
|
||||
}
|
||||
|
||||
// post-install hooks
|
||||
if err := s.execHook(r.Hooks, r.Name, postInstall); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// This is a tricky case. The release has been created, but the result
|
||||
|
|
@ -289,13 +319,49 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
|
|||
//
|
||||
// One possible strategy would be to do a timed retry to see if we can get
|
||||
// this stored in the future.
|
||||
r.Info.Status.Code = release.Status_DEPLOYED
|
||||
if err := s.env.Releases.Create(r); err != nil {
|
||||
log.Printf("warning: Failed to record release %q: %s", name, err)
|
||||
return res, nil
|
||||
log.Printf("warning: Failed to record release %q: %s", r.Name, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *releaseServer) execHook(hs []*release.Hook, name, hook string) error {
|
||||
kubeCli := s.env.KubeClient
|
||||
code, ok := events[hook]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown hook %q", hook)
|
||||
}
|
||||
|
||||
r.Info.Status.Code = release.Status_DEPLOYED
|
||||
return res, nil
|
||||
log.Printf("Executing %s hooks for %s", hook, name)
|
||||
for _, h := range hs {
|
||||
found := false
|
||||
for _, e := range h.Events {
|
||||
if e == code {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
// If this doesn't implement the hook, skip it.
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
b := bytes.NewBufferString(h.Manifest)
|
||||
if err := kubeCli.Create(s.env.Namespace, b); err != nil {
|
||||
log.Printf("wrning: Release %q pre-install %s failed: %s", name, h.Path, err)
|
||||
return err
|
||||
}
|
||||
// No way to rewind a bytes.Buffer()?
|
||||
b.Reset()
|
||||
b.WriteString(h.Manifest)
|
||||
if err := kubeCli.WatchUntilReady(s.env.Namespace, b); err != nil {
|
||||
log.Printf("warning: Release %q pre-install %s could not complete: %s", name, h.Path, err)
|
||||
return err
|
||||
}
|
||||
h.LastRun = timeconv.Now()
|
||||
}
|
||||
log.Printf("Hooks complete for %s %s", hook, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) {
|
||||
|
|
@ -313,20 +379,27 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
|
|||
log.Printf("uninstall: Deleting %s", req.Name)
|
||||
rel.Info.Status.Code = release.Status_DELETED
|
||||
rel.Info.Deleted = timeconv.Now()
|
||||
res := &services.UninstallReleaseResponse{Release: rel}
|
||||
|
||||
if err := s.execHook(rel.Hooks, rel.Name, preDelete); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer([]byte(rel.Manifest))
|
||||
|
||||
if err := s.env.KubeClient.Delete(s.env.Namespace, b); err != nil {
|
||||
log.Printf("uninstall: Failed deletion of %q: %s", req.Name, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.execHook(rel.Hooks, rel.Name, postDelete); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if err := s.env.Releases.Update(rel); err != nil {
|
||||
log.Printf("uninstall: Failed to store updated release: %s", err)
|
||||
}
|
||||
|
||||
res := services.UninstallReleaseResponse{Release: rel}
|
||||
return &res, nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// byName implements the sort.Interface for []*release.Release.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ import (
|
|||
"k8s.io/helm/pkg/timeconv"
|
||||
)
|
||||
|
||||
var manifestWithHook = `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-cm
|
||||
annotations:
|
||||
"helm.sh/hook": post-install,pre-delete
|
||||
data:
|
||||
name: value
|
||||
`
|
||||
|
||||
func rsFixture() *releaseServer {
|
||||
return &releaseServer{
|
||||
env: mockEnvironment(),
|
||||
|
|
@ -59,6 +69,18 @@ func releaseMock() *release.Release {
|
|||
},
|
||||
},
|
||||
Config: &chart.Config{Raw: `name = "value"`},
|
||||
Hooks: []*release.Hook{
|
||||
{
|
||||
Name: "test-cm",
|
||||
Kind: "ConfigMap",
|
||||
Path: "test-cm",
|
||||
Manifest: manifestWithHook,
|
||||
Events: []release.Hook_Event{
|
||||
release.Hook_POST_INSTALL,
|
||||
release.Hook_PRE_DELETE,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +93,7 @@ func TestInstallRelease(t *testing.T) {
|
|||
Metadata: &chart.Metadata{Name: "hello"},
|
||||
Templates: []*chart.Template{
|
||||
{Name: "hello", Data: []byte("hello: world")},
|
||||
{Name: "hooks", Data: []byte(manifestWithHook)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -89,6 +112,20 @@ func TestInstallRelease(t *testing.T) {
|
|||
|
||||
t.Logf("rel: %v", rel)
|
||||
|
||||
if len(rel.Hooks) != 1 {
|
||||
t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks))
|
||||
}
|
||||
if rel.Hooks[0].Manifest != manifestWithHook {
|
||||
t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest)
|
||||
}
|
||||
|
||||
if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL {
|
||||
t.Errorf("Expected event 0 is post install")
|
||||
}
|
||||
if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE {
|
||||
t.Errorf("Expected event 0 is pre-delete")
|
||||
}
|
||||
|
||||
if len(res.Release.Manifest) == 0 {
|
||||
t.Errorf("No manifest returned: %v", res.Release)
|
||||
}
|
||||
|
|
@ -115,6 +152,7 @@ func TestInstallReleaseDryRun(t *testing.T) {
|
|||
{Name: "empty", Data: []byte("")},
|
||||
{Name: "with-partials", Data: []byte("hello: {{ template \"partials/_planet\" . }}")},
|
||||
{Name: "partials/_planet", Data: []byte("Earth")},
|
||||
{Name: "hooks", Data: []byte(manifestWithHook)},
|
||||
},
|
||||
},
|
||||
DryRun: true,
|
||||
|
|
@ -150,6 +188,14 @@ func TestInstallReleaseDryRun(t *testing.T) {
|
|||
if _, err := rs.env.Releases.Read(res.Release.Name); err == nil {
|
||||
t.Errorf("Expected no stored release.")
|
||||
}
|
||||
|
||||
if l := len(res.Release.Hooks); l != 1 {
|
||||
t.Fatalf("Expected 1 hook, got %d", l)
|
||||
}
|
||||
|
||||
if res.Release.Hooks[0].LastRun != nil {
|
||||
t.Error("Expected hook to not be marked as run.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUninstallRelease(t *testing.T) {
|
||||
|
|
@ -163,6 +209,18 @@ func TestUninstallRelease(t *testing.T) {
|
|||
Code: release.Status_DEPLOYED,
|
||||
},
|
||||
},
|
||||
Hooks: []*release.Hook{
|
||||
{
|
||||
Name: "test-cm",
|
||||
Kind: "ConfigMap",
|
||||
Path: "test-cm",
|
||||
Manifest: manifestWithHook,
|
||||
Events: []release.Hook_Event{
|
||||
release.Hook_POST_INSTALL,
|
||||
release.Hook_PRE_DELETE,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
req := &services.UninstallReleaseRequest{
|
||||
|
|
@ -182,6 +240,10 @@ func TestUninstallRelease(t *testing.T) {
|
|||
t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code)
|
||||
}
|
||||
|
||||
if res.Release.Hooks[0].LastRun.Seconds == 0 {
|
||||
t.Error("Expected LastRun to be greater than zero.")
|
||||
}
|
||||
|
||||
if res.Release.Info.Deleted.Seconds <= 0 {
|
||||
t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds)
|
||||
}
|
||||
|
|
|
|||
31
docs/examples/nginx/templates/post-install-job.yaml
Normal file
31
docs/examples/nginx/templates/post-install-job.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "{{template "fullname" . }}"
|
||||
labels:
|
||||
heritage: {{.Release.Service | quote }}
|
||||
release: {{.Release.Name | quote }}
|
||||
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
|
||||
annotations:
|
||||
# This is what defines this resource as a hook. Without this line, the
|
||||
# job is considered part of the release.
|
||||
"helm.sh/hook": post-install
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
name: "{{template "fullname" . }}"
|
||||
labels:
|
||||
heritage: {{.Release.Service | quote }}
|
||||
release: {{.Release.Name | quote }}
|
||||
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
|
||||
spec:
|
||||
# This shows how to use a simple value. This will look for a passed-in value
|
||||
# called restartPolicy. If it is not found, it will use the default value.
|
||||
# {{default "Never" .restartPolicy}} is a slightly optimized version of the
|
||||
# more conventional syntax: {{.restartPolicy | default "Never"}}
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: {{template "fullname" .}}-job
|
||||
image: "alpine:3.3"
|
||||
# All we're going to do is sleep for a minute, then exit.
|
||||
command: ["/bin/sleep","{{default "10" .Values.sleepyTime}}"]
|
||||
14
docs/examples/nginx/templates/pre-install-secret.yaml
Normal file
14
docs/examples/nginx/templates/pre-install-secret.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# This shows a secret as a pre-install hook.
|
||||
# A pre-install hook is run before the rest of the chart is loaded.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{.Release.Name}}-secret"
|
||||
# This declares the resource to be a hook. By convention, we also name the
|
||||
# file "pre-install-XXX.yaml", but Helm itself doesn't care about file names.
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
type: Opaque
|
||||
data:
|
||||
password: {{ b64enc "secret" }}
|
||||
username: {{ b64enc "user1" }}
|
||||
|
|
@ -11,6 +11,9 @@ httpPort: 8888
|
|||
# Number of nginx instances to run
|
||||
replicaCount: 1
|
||||
|
||||
# Evaluated by the post-install hook
|
||||
sleepyTime: "10"
|
||||
|
||||
index: >-
|
||||
<h1>Hello</h1>
|
||||
<p>This is a test</p>
|
||||
|
|
|
|||
|
|
@ -19,12 +19,16 @@ package kube
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Client represents a client capable of communicating with the Kubernetes API.
|
||||
|
|
@ -59,6 +63,24 @@ func (c *Client) Delete(namespace string, reader io.Reader) error {
|
|||
return perform(c, namespace, reader, deleteResource)
|
||||
}
|
||||
|
||||
// WatchUntilReady watches the resource given in the reader, and waits until it is ready.
|
||||
//
|
||||
// This function is mainly for hook implementations. It watches for a resource to
|
||||
// hit a particular milestone. The milestone depends on the Kind.
|
||||
//
|
||||
// For most kinds, it checks to see if the resource is marked as Added or Modified
|
||||
// by the Kubernetes event stream. For some kinds, it does more:
|
||||
//
|
||||
// - Jobs: A job is marked "Ready" when it has successfully completed. This is
|
||||
// ascertained by watching the Status fields in a job's output.
|
||||
//
|
||||
// Handling for other kinds will be added as necessary.
|
||||
func (c *Client) WatchUntilReady(namespace string, reader io.Reader) error {
|
||||
// For jobs, there's also the option to do poll c.Jobs(namespace).Get():
|
||||
// https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300
|
||||
return perform(c, namespace, reader, watchUntilReady)
|
||||
}
|
||||
|
||||
const includeThirdPartyAPIs = false
|
||||
|
||||
func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc) error {
|
||||
|
|
@ -105,6 +127,69 @@ func deleteResource(info *resource.Info) error {
|
|||
return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name)
|
||||
}
|
||||
|
||||
func watchUntilReady(info *resource.Info) error {
|
||||
w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kind := info.Mapping.GroupVersionKind.Kind
|
||||
log.Printf("Watching for changes to %s %s", kind, info.Name)
|
||||
timeout := time.Minute * 5
|
||||
|
||||
// What we watch for depends on the Kind.
|
||||
// - For a Job, we watch for completion.
|
||||
// - For all else, we watch until Ready.
|
||||
// In the future, we might want to add some special logic for types
|
||||
// like Ingress, Volume, etc.
|
||||
|
||||
_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
|
||||
switch e.Type {
|
||||
case watch.Added, watch.Modified:
|
||||
// For things like a secret or a config map, this is the best indicator
|
||||
// we get. We care mostly about jobs, where what we want to see is
|
||||
// the status go into a good state. For other types, like ReplicaSet
|
||||
// we don't really do anything to support these as hooks.
|
||||
log.Printf("Add/Modify event for %s: %v", info.Name, e.Type)
|
||||
if kind == "Job" {
|
||||
return waitForJob(e, info.Name)
|
||||
}
|
||||
return true, nil
|
||||
case watch.Deleted:
|
||||
log.Printf("Deleted event for %s", info.Name)
|
||||
return true, nil
|
||||
case watch.Error:
|
||||
// Handle error and return with an error.
|
||||
log.Printf("Error event for %s", info.Name)
|
||||
return true, fmt.Errorf("Failed to deploy %s", info.Name)
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// waitForJob is a helper that waits for a job to complete.
|
||||
//
|
||||
// This operates on an event returned from a watcher.
|
||||
func waitForJob(e watch.Event, name string) (bool, error) {
|
||||
o, ok := e.Object.(*batch.Job)
|
||||
if !ok {
|
||||
return true, fmt.Errorf("Expected %s to be a *batch.Job, got %T", name, o)
|
||||
}
|
||||
|
||||
for _, c := range o.Status.Conditions {
|
||||
if c.Type == batch.JobComplete && c.Status == api.ConditionTrue {
|
||||
return true, nil
|
||||
} else if c.Type == batch.JobFailed && c.Status == api.ConditionTrue {
|
||||
return true, fmt.Errorf("Job failed: %s", c.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (c *Client) ensureNamespace(namespace string) error {
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
|
|
|
|||
125
pkg/proto/hapi/release/hook.pb.go
Normal file
125
pkg/proto/hapi/release/hook.pb.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// source: hapi/release/hook.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package release is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
hapi/release/hook.proto
|
||||
hapi/release/info.proto
|
||||
hapi/release/release.proto
|
||||
hapi/release/status.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Hook
|
||||
Info
|
||||
Release
|
||||
Status
|
||||
*/
|
||||
package release
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
const _ = proto.ProtoPackageIsVersion1
|
||||
|
||||
type Hook_Event int32
|
||||
|
||||
const (
|
||||
Hook_UNKNOWN Hook_Event = 0
|
||||
Hook_PRE_INSTALL Hook_Event = 1
|
||||
Hook_POST_INSTALL Hook_Event = 2
|
||||
Hook_PRE_DELETE Hook_Event = 3
|
||||
Hook_POST_DELETE Hook_Event = 4
|
||||
Hook_PRE_UPGRADE Hook_Event = 5
|
||||
Hook_POST_UPGRADE Hook_Event = 6
|
||||
)
|
||||
|
||||
var Hook_Event_name = map[int32]string{
|
||||
0: "UNKNOWN",
|
||||
1: "PRE_INSTALL",
|
||||
2: "POST_INSTALL",
|
||||
3: "PRE_DELETE",
|
||||
4: "POST_DELETE",
|
||||
5: "PRE_UPGRADE",
|
||||
6: "POST_UPGRADE",
|
||||
}
|
||||
var Hook_Event_value = map[string]int32{
|
||||
"UNKNOWN": 0,
|
||||
"PRE_INSTALL": 1,
|
||||
"POST_INSTALL": 2,
|
||||
"PRE_DELETE": 3,
|
||||
"POST_DELETE": 4,
|
||||
"PRE_UPGRADE": 5,
|
||||
"POST_UPGRADE": 6,
|
||||
}
|
||||
|
||||
func (x Hook_Event) String() string {
|
||||
return proto.EnumName(Hook_Event_name, int32(x))
|
||||
}
|
||||
func (Hook_Event) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
|
||||
|
||||
// Hook defines a hook object.
|
||||
type Hook struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
// Kind is the Kubernetes kind.
|
||||
Kind string `protobuf:"bytes,2,opt,name=kind" json:"kind,omitempty"`
|
||||
// Path is the chart-relative path to the template.
|
||||
Path string `protobuf:"bytes,3,opt,name=path" json:"path,omitempty"`
|
||||
// Manifest is the manifest contents.
|
||||
Manifest string `protobuf:"bytes,4,opt,name=manifest" json:"manifest,omitempty"`
|
||||
// Events are the events that this hook fires on.
|
||||
Events []Hook_Event `protobuf:"varint,5,rep,name=events,enum=hapi.release.Hook_Event" json:"events,omitempty"`
|
||||
// LastRun indicates the date/time this was last run.
|
||||
LastRun *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=last_run,json=lastRun" json:"last_run,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Hook) Reset() { *m = Hook{} }
|
||||
func (m *Hook) String() string { return proto.CompactTextString(m) }
|
||||
func (*Hook) ProtoMessage() {}
|
||||
func (*Hook) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Hook) GetLastRun() *google_protobuf.Timestamp {
|
||||
if m != nil {
|
||||
return m.LastRun
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Hook)(nil), "hapi.release.Hook")
|
||||
proto.RegisterEnum("hapi.release.Hook_Event", Hook_Event_name, Hook_Event_value)
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 300 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8f, 0x5f, 0x4f, 0xc2, 0x30,
|
||||
0x14, 0xc5, 0x05, 0xc6, 0x80, 0x0b, 0xc1, 0xa5, 0x2f, 0x36, 0xbc, 0x48, 0x78, 0xe2, 0xa9, 0x33,
|
||||
0x18, 0x3f, 0x00, 0x86, 0x46, 0x8d, 0x64, 0x90, 0x32, 0x62, 0xe2, 0x0b, 0x29, 0xb1, 0xc0, 0x02,
|
||||
0x6b, 0x17, 0xd6, 0xf9, 0xe2, 0xd7, 0xf3, 0x83, 0xb9, 0x76, 0x7f, 0xe2, 0xdb, 0xed, 0xef, 0x9c,
|
||||
0x7b, 0x7a, 0x0f, 0xdc, 0x9d, 0x78, 0x12, 0xf9, 0x57, 0x71, 0x11, 0x3c, 0x15, 0xfe, 0x49, 0xa9,
|
||||
0x33, 0x49, 0xae, 0x4a, 0x2b, 0x34, 0x30, 0x02, 0x29, 0x85, 0xd1, 0xfd, 0x51, 0xa9, 0xe3, 0x45,
|
||||
0xf8, 0x56, 0xdb, 0x67, 0x07, 0x5f, 0x47, 0xb1, 0x48, 0x35, 0x8f, 0x93, 0xc2, 0x3e, 0xf9, 0x6d,
|
||||
0x82, 0xf3, 0x9a, 0x6f, 0x23, 0x04, 0x8e, 0xe4, 0xb1, 0xc0, 0x8d, 0x71, 0x63, 0xda, 0x63, 0x76,
|
||||
0x36, 0xec, 0x1c, 0xc9, 0x2f, 0xdc, 0x2c, 0x98, 0x99, 0x0d, 0x4b, 0xb8, 0x3e, 0xe1, 0x56, 0xc1,
|
||||
0xcc, 0x8c, 0x46, 0xd0, 0x8d, 0xb9, 0x8c, 0x0e, 0x79, 0x32, 0x76, 0x2c, 0xaf, 0xdf, 0xe8, 0x01,
|
||||
0x5c, 0xf1, 0x2d, 0xa4, 0x4e, 0x71, 0x7b, 0xdc, 0x9a, 0x0e, 0x67, 0x98, 0xfc, 0x3f, 0x90, 0x98,
|
||||
0xbf, 0x09, 0x35, 0x06, 0x56, 0xfa, 0xd0, 0x13, 0x74, 0x2f, 0x3c, 0xd5, 0xbb, 0x6b, 0x26, 0xb1,
|
||||
0x9b, 0xa7, 0xf5, 0x67, 0x23, 0x52, 0xd4, 0x20, 0x55, 0x0d, 0x12, 0x56, 0x35, 0x58, 0xc7, 0x78,
|
||||
0x59, 0x26, 0x27, 0x3f, 0xd0, 0xb6, 0x39, 0xa8, 0x0f, 0x9d, 0x6d, 0xf0, 0x1e, 0xac, 0x3e, 0x02,
|
||||
0xef, 0x06, 0xdd, 0x42, 0x7f, 0xcd, 0xe8, 0xee, 0x2d, 0xd8, 0x84, 0xf3, 0xe5, 0xd2, 0x6b, 0x20,
|
||||
0x0f, 0x06, 0xeb, 0xd5, 0x26, 0xac, 0x49, 0x13, 0x0d, 0x01, 0x8c, 0x65, 0x41, 0x97, 0x34, 0xa4,
|
||||
0x5e, 0xcb, 0xae, 0x18, 0x47, 0x09, 0x9c, 0x2a, 0x63, 0xbb, 0x7e, 0x61, 0xf3, 0x05, 0xf5, 0xda,
|
||||
0x75, 0x46, 0x45, 0xdc, 0xe7, 0xde, 0x67, 0xa7, 0x6c, 0xb4, 0x77, 0xed, 0x91, 0x8f, 0x7f, 0x01,
|
||||
0x00, 0x00, 0xff, 0xff, 0x16, 0x64, 0x61, 0x76, 0xa2, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
|
@ -2,19 +2,6 @@
|
|||
// source: hapi/release/info.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package release is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
hapi/release/info.proto
|
||||
hapi/release/release.proto
|
||||
hapi/release/status.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Info
|
||||
Release
|
||||
Status
|
||||
*/
|
||||
package release
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
|
|
@ -27,10 +14,6 @@ var _ = proto.Marshal
|
|||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
const _ = proto.ProtoPackageIsVersion1
|
||||
|
||||
// Info describes release information.
|
||||
type Info struct {
|
||||
Status *Status `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"`
|
||||
|
|
@ -43,7 +26,7 @@ type Info struct {
|
|||
func (m *Info) Reset() { *m = Info{} }
|
||||
func (m *Info) String() string { return proto.CompactTextString(m) }
|
||||
func (*Info) ProtoMessage() {}
|
||||
func (*Info) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*Info) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
|
||||
|
||||
func (m *Info) GetStatus() *Status {
|
||||
if m != nil {
|
||||
|
|
@ -77,7 +60,7 @@ func init() {
|
|||
proto.RegisterType((*Info)(nil), "hapi.release.Info")
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
var fileDescriptor1 = []byte{
|
||||
// 208 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x48, 0x2c, 0xc8,
|
||||
0xd4, 0x2f, 0x4a, 0xcd, 0x49, 0x4d, 0x2c, 0x4e, 0xd5, 0xcf, 0xcc, 0x4b, 0xcb, 0xd7, 0x2b, 0x28,
|
||||
|
|
|
|||
|
|
@ -29,12 +29,14 @@ type Release struct {
|
|||
Config *hapi_chart.Config `protobuf:"bytes,4,opt,name=config" json:"config,omitempty"`
|
||||
// Manifest is the string representation of the rendered template.
|
||||
Manifest string `protobuf:"bytes,5,opt,name=manifest" json:"manifest,omitempty"`
|
||||
// Hooks are all of the hooks declared for this release.
|
||||
Hooks []*Hook `protobuf:"bytes,6,rep,name=hooks" json:"hooks,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Release) Reset() { *m = Release{} }
|
||||
func (m *Release) String() string { return proto.CompactTextString(m) }
|
||||
func (*Release) ProtoMessage() {}
|
||||
func (*Release) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
|
||||
func (*Release) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
|
||||
|
||||
func (m *Release) GetInfo() *Info {
|
||||
if m != nil {
|
||||
|
|
@ -57,23 +59,31 @@ func (m *Release) GetConfig() *hapi_chart.Config {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Release) GetHooks() []*Hook {
|
||||
if m != nil {
|
||||
return m.Hooks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Release)(nil), "hapi.release.Release")
|
||||
}
|
||||
|
||||
var fileDescriptor1 = []byte{
|
||||
// 200 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x8f, 0xbd, 0x0e, 0x82, 0x30,
|
||||
0x14, 0x85, 0x83, 0xf2, 0x23, 0xd5, 0xc5, 0x3b, 0x68, 0xc3, 0x64, 0x1c, 0xd4, 0x38, 0x94, 0x44,
|
||||
0xdf, 0x40, 0x27, 0xd7, 0x8e, 0x6e, 0x95, 0x14, 0x69, 0x22, 0x2d, 0x01, 0x9e, 0xcd, 0xe7, 0x93,
|
||||
0xf6, 0x56, 0x83, 0xcb, 0x85, 0xde, 0xef, 0xcb, 0xe9, 0x29, 0xc9, 0x2a, 0xd1, 0xa8, 0xbc, 0x95,
|
||||
0x2f, 0x29, 0x3a, 0xf9, 0xfd, 0xb2, 0xa6, 0x35, 0xbd, 0x81, 0x85, 0x65, 0xcc, 0xef, 0xb2, 0xf5,
|
||||
0x9f, 0xa9, 0x74, 0x69, 0x50, 0xf3, 0xa0, 0xa8, 0x44, 0xdb, 0xe7, 0x85, 0xd1, 0xa5, 0x7a, 0x7a,
|
||||
0xb0, 0x1a, 0x03, 0x3b, 0x71, 0xbf, 0x7d, 0x07, 0x24, 0xe1, 0x98, 0x03, 0x40, 0x42, 0x2d, 0x6a,
|
||||
0x49, 0x83, 0x4d, 0x70, 0x48, 0xb9, 0xfb, 0x87, 0x1d, 0x09, 0x6d, 0x3c, 0x9d, 0x0c, 0xbb, 0xf9,
|
||||
0x09, 0xd8, 0xb8, 0x06, 0xbb, 0x0d, 0x84, 0x3b, 0x0e, 0x7b, 0x12, 0xb9, 0x58, 0x3a, 0x75, 0xe2,
|
||||
0x12, 0x45, 0xbc, 0xe9, 0x6a, 0x27, 0x47, 0x0e, 0x47, 0x12, 0x63, 0x31, 0x1a, 0x8e, 0x23, 0xbd,
|
||||
0xe9, 0x08, 0xf7, 0x06, 0x64, 0x64, 0x56, 0x0b, 0xad, 0x4a, 0xd9, 0xf5, 0x34, 0x72, 0xa5, 0x7e,
|
||||
0xe7, 0x4b, 0x7a, 0x4f, 0x7c, 0x8d, 0x47, 0xec, 0x9e, 0x72, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff,
|
||||
0xd4, 0xf3, 0x60, 0x0b, 0x40, 0x01, 0x00, 0x00,
|
||||
var fileDescriptor2 = []byte{
|
||||
// 224 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0x3f, 0x4f, 0x85, 0x30,
|
||||
0x14, 0xc5, 0x83, 0xfc, 0x93, 0xab, 0x8b, 0x77, 0xd0, 0x86, 0x89, 0x38, 0x28, 0x71, 0x28, 0x89,
|
||||
0x7e, 0x03, 0x5d, 0x74, 0xed, 0xe8, 0x56, 0x49, 0x91, 0x46, 0x69, 0x09, 0xf0, 0x81, 0xfd, 0x28,
|
||||
0xd2, 0xde, 0xbe, 0x17, 0x78, 0x6f, 0xb9, 0x6d, 0xcf, 0xef, 0xe4, 0xf4, 0xb4, 0x50, 0xf6, 0x72,
|
||||
0xd4, 0xcd, 0xa4, 0x7e, 0x95, 0x9c, 0xd5, 0x61, 0xe5, 0xe3, 0x64, 0x17, 0x8b, 0xd7, 0x8e, 0xf1,
|
||||
0xa0, 0x95, 0x77, 0x3b, 0x67, 0x6f, 0xed, 0x0f, 0xd9, 0x4e, 0x80, 0x36, 0x9d, 0xdd, 0x81, 0xb6,
|
||||
0x97, 0xd3, 0xd2, 0xb4, 0xd6, 0x74, 0xfa, 0x3b, 0x80, 0xdb, 0x2d, 0x70, 0x93, 0xf4, 0xfb, 0xbf,
|
||||
0x08, 0x72, 0x41, 0x39, 0x88, 0x90, 0x18, 0x39, 0x28, 0x16, 0x55, 0x51, 0x5d, 0x08, 0xbf, 0xc7,
|
||||
0x07, 0x48, 0x5c, 0x3c, 0xbb, 0x58, 0xb5, 0xab, 0x67, 0xe4, 0xdb, 0x7e, 0xfc, 0x63, 0x25, 0xc2,
|
||||
0x73, 0x7c, 0x84, 0xd4, 0xc7, 0xb2, 0xd8, 0x1b, 0x6f, 0xc8, 0x48, 0x37, 0xbd, 0xb9, 0x29, 0x88,
|
||||
0xe3, 0x13, 0x64, 0x54, 0x8c, 0x25, 0xdb, 0xc8, 0xe0, 0xf4, 0x44, 0x04, 0x07, 0x96, 0x70, 0x39,
|
||||
0x48, 0xa3, 0x3b, 0x35, 0x2f, 0x2c, 0xf5, 0xa5, 0x8e, 0x67, 0xac, 0x21, 0x75, 0x1f, 0x32, 0xb3,
|
||||
0xac, 0x8a, 0xcf, 0x9b, 0xbd, 0xaf, 0x48, 0x90, 0xe1, 0xb5, 0xf8, 0xcc, 0x83, 0xfc, 0x95, 0xf9,
|
||||
0x47, 0xbf, 0xfc, 0x07, 0x00, 0x00, 0xff, 0xff, 0x96, 0x35, 0x9e, 0xef, 0x83, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ var Status_Code_value = map[string]int32{
|
|||
func (x Status_Code) String() string {
|
||||
return proto.EnumName(Status_Code_name, int32(x))
|
||||
}
|
||||
func (Status_Code) EnumDescriptor() ([]byte, []int) { return fileDescriptor2, []int{0, 0} }
|
||||
func (Status_Code) EnumDescriptor() ([]byte, []int) { return fileDescriptor3, []int{0, 0} }
|
||||
|
||||
// Status defines the status of a release.
|
||||
type Status struct {
|
||||
|
|
@ -58,7 +58,7 @@ type Status struct {
|
|||
func (m *Status) Reset() { *m = Status{} }
|
||||
func (m *Status) String() string { return proto.CompactTextString(m) }
|
||||
func (*Status) ProtoMessage() {}
|
||||
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
|
||||
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} }
|
||||
|
||||
func (m *Status) GetDetails() *google_protobuf1.Any {
|
||||
if m != nil {
|
||||
|
|
@ -72,7 +72,7 @@ func init() {
|
|||
proto.RegisterEnum("hapi.release.Status_Code", Status_Code_name, Status_Code_value)
|
||||
}
|
||||
|
||||
var fileDescriptor2 = []byte{
|
||||
var fileDescriptor3 = []byte{
|
||||
// 226 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0x48, 0x2c, 0xc8,
|
||||
0xd4, 0x2f, 0x4a, 0xcd, 0x49, 0x4d, 0x2c, 0x4e, 0xd5, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6,
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import fmt "fmt"
|
|||
import math "math"
|
||||
import hapi_chart3 "k8s.io/helm/pkg/proto/hapi/chart"
|
||||
import hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"
|
||||
import hapi_release3 "k8s.io/helm/pkg/proto/hapi/release"
|
||||
import hapi_release2 "k8s.io/helm/pkg/proto/hapi/release"
|
||||
import hapi_release1 "k8s.io/helm/pkg/proto/hapi/release"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
|
|
@ -141,7 +141,7 @@ type ListReleasesResponse struct {
|
|||
// Total is the total number of queryable releases.
|
||||
Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"`
|
||||
// Releases is the list of found release objects.
|
||||
Releases []*hapi_release2.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"`
|
||||
Releases []*hapi_release3.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} }
|
||||
|
|
@ -149,7 +149,7 @@ func (m *ListReleasesResponse) String() string { return proto.Compact
|
|||
func (*ListReleasesResponse) ProtoMessage() {}
|
||||
func (*ListReleasesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *ListReleasesResponse) GetReleases() []*hapi_release2.Release {
|
||||
func (m *ListReleasesResponse) GetReleases() []*hapi_release3.Release {
|
||||
if m != nil {
|
||||
return m.Releases
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ type GetReleaseStatusResponse struct {
|
|||
// Name is the name of the release.
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
// Info contains information about the release.
|
||||
Info *hapi_release1.Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
|
||||
Info *hapi_release2.Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetReleaseStatusResponse) Reset() { *m = GetReleaseStatusResponse{} }
|
||||
|
|
@ -180,7 +180,7 @@ func (m *GetReleaseStatusResponse) String() string { return proto.Com
|
|||
func (*GetReleaseStatusResponse) ProtoMessage() {}
|
||||
func (*GetReleaseStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func (m *GetReleaseStatusResponse) GetInfo() *hapi_release1.Info {
|
||||
func (m *GetReleaseStatusResponse) GetInfo() *hapi_release2.Info {
|
||||
if m != nil {
|
||||
return m.Info
|
||||
}
|
||||
|
|
@ -201,7 +201,7 @@ func (*GetReleaseContentRequest) Descriptor() ([]byte, []int) { return fileDescr
|
|||
// GetReleaseContentResponse is a response containing the contents of a release.
|
||||
type GetReleaseContentResponse struct {
|
||||
// The release content
|
||||
Release *hapi_release2.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetReleaseContentResponse) Reset() { *m = GetReleaseContentResponse{} }
|
||||
|
|
@ -209,7 +209,7 @@ func (m *GetReleaseContentResponse) String() string { return proto.Co
|
|||
func (*GetReleaseContentResponse) ProtoMessage() {}
|
||||
func (*GetReleaseContentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
func (m *GetReleaseContentResponse) GetRelease() *hapi_release2.Release {
|
||||
func (m *GetReleaseContentResponse) GetRelease() *hapi_release3.Release {
|
||||
if m != nil {
|
||||
return m.Release
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ func (m *InstallReleaseRequest) GetValues() *hapi_chart.Config {
|
|||
|
||||
// InstallReleaseResponse is the response from a release installation.
|
||||
type InstallReleaseResponse struct {
|
||||
Release *hapi_release2.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
}
|
||||
|
||||
func (m *InstallReleaseResponse) Reset() { *m = InstallReleaseResponse{} }
|
||||
|
|
@ -279,7 +279,7 @@ func (m *InstallReleaseResponse) String() string { return proto.Compa
|
|||
func (*InstallReleaseResponse) ProtoMessage() {}
|
||||
func (*InstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
|
||||
|
||||
func (m *InstallReleaseResponse) GetRelease() *hapi_release2.Release {
|
||||
func (m *InstallReleaseResponse) GetRelease() *hapi_release3.Release {
|
||||
if m != nil {
|
||||
return m.Release
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ func (*UninstallReleaseRequest) Descriptor() ([]byte, []int) { return fileDescri
|
|||
// UninstallReleaseResponse represents a successful response to an uninstall request.
|
||||
type UninstallReleaseResponse struct {
|
||||
// Release is the release that was marked deleted.
|
||||
Release *hapi_release2.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
|
||||
}
|
||||
|
||||
func (m *UninstallReleaseResponse) Reset() { *m = UninstallReleaseResponse{} }
|
||||
|
|
@ -308,7 +308,7 @@ func (m *UninstallReleaseResponse) String() string { return proto.Com
|
|||
func (*UninstallReleaseResponse) ProtoMessage() {}
|
||||
func (*UninstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
|
||||
|
||||
func (m *UninstallReleaseResponse) GetRelease() *hapi_release2.Release {
|
||||
func (m *UninstallReleaseResponse) GetRelease() *hapi_release3.Release {
|
||||
if m != nil {
|
||||
return m.Release
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue