Merge pull request #916 from technosophos/feat/901-hooks

feat(tiller): support hooks for install
pull/936/head
Matt Butcher 8 years ago committed by GitHub
commit e91bbceff2

@ -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{}

@ -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
}

@ -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,15 +319,51 @@ 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)
}
r.Info.Status.Code = release.Status_DEPLOYED
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)
}
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) {
if req.Name == "" {
log.Printf("uninstall: Release not found: %s", req.Name)
@ -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}
b := bytes.NewBuffer([]byte(rel.Manifest))
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)
}

@ -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}}"]

@ -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 {

@ -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…
Cancel
Save