Merge pull request #1274 from fibonacci1729/feat/history

feat(helm/cmd): support for retrieving release history
pull/1288/head
Brian 8 years ago committed by GitHub
commit c87a902b0c

@ -75,6 +75,9 @@ service ReleaseService {
rpc RollbackRelease(RollbackReleaseRequest) returns (RollbackReleaseResponse) {
}
// ReleaseHistory retrieves a releasse's history.
rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse) {
}
}
// ListReleasesRequest requests a list of releases.
@ -262,3 +265,16 @@ message GetVersionRequest {
message GetVersionResponse {
hapi.version.Version Version = 1;
}
// GetHistoryRequest requests a release's history.
message GetHistoryRequest {
// The name of the release.
string name = 1;
// The maximum number of releases to include.
int32 max = 2;
}
// GetHistoryResponse is received in response to a GetHistory rpc.
message GetHistoryResponse {
repeated hapi.release.Release releases = 1;
}

@ -93,6 +93,7 @@ func newRootCmd(out io.Writer) *cobra.Command {
newFetchCmd(out),
newGetCmd(nil, out),
newHomeCmd(out),
newHistoryCmd(nil, out),
newInitCmd(out),
newInspectCmd(nil, out),
newInstallCmd(nil, out),

@ -175,6 +175,10 @@ func (c *fakeReleaseClient) ReleaseContent(rlsName string, opts ...helm.ContentO
return resp, c.err
}
func (c *fakeReleaseClient) ReleaseHistory(rlsName string, opts ...helm.HistoryOption) (*rls.GetHistoryResponse, error) {
return &rls.GetHistoryResponse{Releases: c.rels}, c.err
}
func (c *fakeReleaseClient) Option(opt ...helm.Option) helm.Interface {
return c
}

@ -0,0 +1,107 @@
/*
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.
*/
package main
import (
"fmt"
"io"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/timeconv"
)
var historyHelp = `
History prints historical revisions for a given release.
A default maximum of 256 revisions will be returned. Setting '--max'
configures the maximum length of the revision list returned.
The historical release set is printed as a formatted table, e.g:
$ helm history angry-bird --max=4
REVISION UPDATED STATUS CHART
4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0
3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0
2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0
1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0
`
type historyCmd struct {
max int32
rls string
out io.Writer
helmc helm.Interface
}
func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
his := &historyCmd{out: w, helmc: c}
cmd := &cobra.Command{
Use: "history [flags] RELEASE_NAME",
Long: historyHelp,
Short: "fetch release history",
Aliases: []string{"hist"},
PersistentPreRunE: setupConnection,
RunE: func(cmd *cobra.Command, args []string) error {
switch {
case len(args) == 0:
return errReleaseRequired
case his.helmc == nil:
his.helmc = helm.NewClient(helm.Host(tillerHost))
}
his.rls = args[0]
return his.run()
},
}
cmd.Flags().Int32Var(&his.max, "max", 256, "maximum number of revision to include in history")
return cmd
}
func (cmd *historyCmd) run() error {
opts := []helm.HistoryOption{
helm.WithMaxHistory(cmd.max),
}
r, err := cmd.helmc.ReleaseHistory(cmd.rls, opts...)
if err != nil {
return prettyError(err)
}
if len(r.Releases) == 0 {
return nil
}
fmt.Fprintln(cmd.out, formatHistory(r.Releases))
return nil
}
func formatHistory(rls []*release.Release) string {
tbl := uitable.New()
tbl.MaxColWidth = 30
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART")
for _, r := range rls {
c := fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version)
t := timeconv.String(r.Info.LastDeployed)
s := r.Info.Status.Code.String()
v := r.Version
tbl.AddRow(v, t, s, c)
}
return tbl.String()
}

@ -0,0 +1,82 @@
/*
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.
*/
package main
import (
"bytes"
"regexp"
"testing"
rpb "k8s.io/helm/pkg/proto/hapi/release"
)
func TestHistoryCmd(t *testing.T) {
mk := func(name string, vers int32, code rpb.Status_Code) *rpb.Release {
return releaseMock(&releaseOptions{
name: name,
version: vers,
statusCode: code,
})
}
tests := []struct {
cmds string
desc string
args []string
resp []*rpb.Release
xout string
}{
{
cmds: "helm history RELEASE_NAME",
desc: "get history for release",
args: []string{"angry-bird"},
resp: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
mk("angry-bird", 2, rpb.Status_SUPERSEDED),
mk("angry-bird", 1, rpb.Status_SUPERSEDED),
},
xout: "REVISION\tUPDATED \tSTATUS \tCHART \n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\n",
},
{
cmds: "helm history --max=MAX RELEASE_NAME",
desc: "get history with max limit set",
args: []string{"--max=2", "angry-bird"},
resp: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
},
xout: "REVISION\tUPDATED \tSTATUS \tCHART \n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\n",
},
}
var buf bytes.Buffer
for _, tt := range tests {
frc := &fakeReleaseClient{rels: tt.resp}
cmd := newHistoryCmd(frc, &buf)
cmd.ParseFlags(tt.args)
if err := cmd.RunE(cmd, tt.args); err != nil {
t.Fatalf("%q\n\t%s: unexpected error: %v", tt.cmds, tt.desc, err)
}
re := regexp.MustCompile(tt.xout)
if !re.Match(buf.Bytes()) {
t.Fatalf("%q\n\t%s:\nexpected\n\t%q\nactual\n\t%q", tt.cmds, tt.desc, tt.xout, buf.String())
}
buf.Reset()
}
}

@ -0,0 +1,58 @@
/*
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.
*/
package main
import (
"sort"
"golang.org/x/net/context"
rpb "k8s.io/helm/pkg/proto/hapi/release"
tpb "k8s.io/helm/pkg/proto/hapi/services"
)
func (s *releaseServer) GetHistory(ctx context.Context, req *tpb.GetHistoryRequest) (*tpb.GetHistoryResponse, error) {
if !checkClientVersion(ctx) {
return nil, errIncompatibleVersion
}
h, err := s.env.Releases.History(req.Name)
if err != nil {
return nil, err
}
sort.Sort(sort.Reverse(byRev(h)))
var resp tpb.GetHistoryResponse
for i := 0; i < min(len(h), int(req.Max)); i++ {
resp.Releases = append(resp.Releases, h[i])
}
return &resp, nil
}
type byRev []*rpb.Release
func (s byRev) Len() int { return len(s) }
func (s byRev) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byRev) Less(i, j int) bool { return s[i].Version < s[j].Version }
func min(x, y int) int {
if x < y {
return x
}
return y
}

@ -0,0 +1,116 @@
/*
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.
*/
package main
import (
"reflect"
"testing"
"k8s.io/helm/pkg/helm"
rpb "k8s.io/helm/pkg/proto/hapi/release"
tpb "k8s.io/helm/pkg/proto/hapi/services"
)
func TestGetHistory_WithRevisions(t *testing.T) {
mk := func(name string, vers int32, code rpb.Status_Code) *rpb.Release {
return &rpb.Release{
Name: name,
Version: vers,
Info: &rpb.Info{Status: &rpb.Status{Code: code}},
}
}
// GetReleaseHistoryTests
tests := []struct {
desc string
req *tpb.GetHistoryRequest
res *tpb.GetHistoryResponse
}{
{
desc: "get release with history and default limit (max=256)",
req: &tpb.GetHistoryRequest{Name: "angry-bird", Max: 256},
res: &tpb.GetHistoryResponse{Releases: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
mk("angry-bird", 2, rpb.Status_SUPERSEDED),
mk("angry-bird", 1, rpb.Status_SUPERSEDED),
}},
},
{
desc: "get release with history using result limit (max=2)",
req: &tpb.GetHistoryRequest{Name: "angry-bird", Max: 2},
res: &tpb.GetHistoryResponse{Releases: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
}},
},
}
// test release history for release 'angry-bird'
hist := []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
mk("angry-bird", 2, rpb.Status_SUPERSEDED),
mk("angry-bird", 1, rpb.Status_SUPERSEDED),
}
srv := rsFixture()
for _, rls := range hist {
if err := srv.env.Releases.Create(rls); err != nil {
t.Fatalf("Failed to create release: %s", err)
}
}
// run tests
for _, tt := range tests {
res, err := srv.GetHistory(helm.NewContext(), tt.req)
if err != nil {
t.Fatalf("%s:\nFailed to get History of %q: %s", tt.desc, tt.req.Name, err)
}
if !reflect.DeepEqual(res, tt.res) {
t.Fatalf("%s:\nExpected:\n\t%+v\nActual\n\t%+v", tt.desc, tt.res, res)
}
}
}
func TestGetHistory_WithNoRevisions(t *testing.T) {
tests := []struct {
desc string
req *tpb.GetHistoryRequest
}{
{
desc: "get release with no history",
req: &tpb.GetHistoryRequest{Name: "sad-panda", Max: 256},
},
}
// create release 'sad-panda' with no revision history
rls := namedReleaseStub("sad-panda", rpb.Status_DEPLOYED)
srv := rsFixture()
srv.env.Releases.Create(rls)
for _, tt := range tests {
res, err := srv.GetHistory(helm.NewContext(), tt.req)
if err != nil {
t.Fatalf("%s:\nFailed to get History of %q: %s", tt.desc, tt.req.Name, err)
}
if len(res.Releases) > 1 {
t.Fatalf("%s:\nExpected zero items, got %d", tt.desc, len(res.Releases))
}
}
}

@ -225,6 +225,24 @@ func (h *Client) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.Get
return h.content(ctx, req)
}
// ReleaseHistory returns a release's revision history.
func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error) {
for _, opt := range opts {
opt(&h.opts)
}
req := &h.opts.histReq
req.Name = rlsName
ctx := NewContext()
if h.opts.before != nil {
if err := h.opts.before(ctx, req); err != nil {
return nil, err
}
}
return h.history(ctx, req)
}
// Executes tiller.ListReleases RPC.
func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
@ -325,3 +343,15 @@ func (h *Client) version(ctx context.Context, req *rls.GetVersionRequest) (*rls.
rlc := rls.NewReleaseServiceClient(c)
return rlc.GetVersion(ctx, req)
}
// Executes tiller.GetHistory RPC.
func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.GetHistoryResponse, error) {
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
if err != nil {
return nil, err
}
defer c.Close()
rlc := rls.NewReleaseServiceClient(c)
return rlc.GetHistory(ctx, req)
}

@ -29,5 +29,6 @@ type Interface interface {
UpdateRelease(rlsName, chStr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error)
RollbackRelease(rlsName string, opts ...RollbackOption) (*rls.RollbackReleaseResponse, error)
ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error)
ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error)
GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error)
}

@ -60,6 +60,8 @@ type options struct {
rollbackReq rls.RollbackReleaseRequest
// before intercepts client calls before sending
before func(context.Context, proto.Message) error
// release history options are applied directly to the get release history request
histReq rls.GetHistoryRequest
}
// Host specifies the host address of the Tiller release server, (default = ":44134").
@ -272,6 +274,18 @@ type UpdateOption func(*options)
// running the `helm rollback` command.
type RollbackOption func(*options)
// HistoryOption allows configuring optional request data for
// issuing a GetHistory rpc.
type HistoryOption func(*options)
// WithMaxHistory sets the max number of releases to return
// in a release history query.
func WithMaxHistory(max int32) HistoryOption {
return func(opts *options) {
opts.histReq.Max = max
}
}
// NewContext creates a versioned context.
func NewContext() context.Context {
md := metadata.Pairs("x-helm-api-client", version.Version)

@ -26,6 +26,8 @@ It has these top-level messages:
UninstallReleaseResponse
GetVersionRequest
GetVersionResponse
GetHistoryRequest
GetHistoryResponse
*/
package services
@ -416,7 +418,7 @@ func (*GetVersionRequest) ProtoMessage() {}
func (*GetVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
type GetVersionResponse struct {
Version *hapi_version.Version `protobuf:"bytes,1,opt,name=Version" json:"Version,omitempty"`
Version *hapi_version.Version `protobuf:"bytes,1,opt,name=Version,json=version" json:"Version,omitempty"`
}
func (m *GetVersionResponse) Reset() { *m = GetVersionResponse{} }
@ -431,6 +433,36 @@ func (m *GetVersionResponse) GetVersion() *hapi_version.Version {
return nil
}
// GetHistoryRequest requests a release's history.
type GetHistoryRequest struct {
// The name of the release.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The maximum number of releases to include.
Max int32 `protobuf:"varint,2,opt,name=max" json:"max,omitempty"`
}
func (m *GetHistoryRequest) Reset() { *m = GetHistoryRequest{} }
func (m *GetHistoryRequest) String() string { return proto.CompactTextString(m) }
func (*GetHistoryRequest) ProtoMessage() {}
func (*GetHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
// GetHistoryResponse is received in response to a GetHistory rpc.
type GetHistoryResponse struct {
Releases []*hapi_release3.Release `protobuf:"bytes,1,rep,name=releases" json:"releases,omitempty"`
}
func (m *GetHistoryResponse) Reset() { *m = GetHistoryResponse{} }
func (m *GetHistoryResponse) String() string { return proto.CompactTextString(m) }
func (*GetHistoryResponse) ProtoMessage() {}
func (*GetHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (m *GetHistoryResponse) GetReleases() []*hapi_release3.Release {
if m != nil {
return m.Releases
}
return nil
}
func init() {
proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest")
proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort")
@ -449,6 +481,8 @@ func init() {
proto.RegisterType((*UninstallReleaseResponse)(nil), "hapi.services.tiller.UninstallReleaseResponse")
proto.RegisterType((*GetVersionRequest)(nil), "hapi.services.tiller.GetVersionRequest")
proto.RegisterType((*GetVersionResponse)(nil), "hapi.services.tiller.GetVersionResponse")
proto.RegisterType((*GetHistoryRequest)(nil), "hapi.services.tiller.GetHistoryRequest")
proto.RegisterType((*GetHistoryResponse)(nil), "hapi.services.tiller.GetHistoryResponse")
proto.RegisterEnum("hapi.services.tiller.ListSort_SortBy", ListSort_SortBy_name, ListSort_SortBy_value)
proto.RegisterEnum("hapi.services.tiller.ListSort_SortOrder", ListSort_SortOrder_name, ListSort_SortOrder_value)
}
@ -483,6 +517,8 @@ type ReleaseServiceClient interface {
GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error)
// RollbackRelease rolls back a release to a previous version.
RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history.
GetHistory(ctx context.Context, in *GetHistoryRequest, opts ...grpc.CallOption) (*GetHistoryResponse, error)
}
type releaseServiceClient struct {
@ -588,6 +624,15 @@ func (c *releaseServiceClient) RollbackRelease(ctx context.Context, in *Rollback
return out, nil
}
func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryRequest, opts ...grpc.CallOption) (*GetHistoryResponse, error) {
out := new(GetHistoryResponse)
err := grpc.Invoke(ctx, "/hapi.services.tiller.ReleaseService/GetHistory", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for ReleaseService service
type ReleaseServiceServer interface {
@ -610,6 +655,8 @@ type ReleaseServiceServer interface {
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
// RollbackRelease rolls back a release to a previous version.
RollbackRelease(context.Context, *RollbackReleaseRequest) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history.
GetHistory(context.Context, *GetHistoryRequest) (*GetHistoryResponse, error)
}
func RegisterReleaseServiceServer(s *grpc.Server, srv ReleaseServiceServer) {
@ -763,6 +810,24 @@ func _ReleaseService_RollbackRelease_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler)
}
func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetHistoryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReleaseServiceServer).GetHistory(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hapi.services.tiller.ReleaseService/GetHistory",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReleaseServiceServer).GetHistory(ctx, req.(*GetHistoryRequest))
}
return interceptor(ctx, in, info, handler)
}
var _ReleaseService_serviceDesc = grpc.ServiceDesc{
ServiceName: "hapi.services.tiller.ReleaseService",
HandlerType: (*ReleaseServiceServer)(nil),
@ -795,6 +860,10 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
MethodName: "RollbackRelease",
Handler: _ReleaseService_RollbackRelease_Handler,
},
{
MethodName: "GetHistory",
Handler: _ReleaseService_GetHistory_Handler,
},
},
Streams: []grpc.StreamDesc{
{
@ -809,65 +878,68 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 946 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x72, 0xdb, 0x44,
0x14, 0xae, 0x6c, 0xc7, 0xb2, 0x4f, 0x7e, 0x48, 0xb6, 0x49, 0xac, 0x68, 0x80, 0xe9, 0x88, 0x81,
0x86, 0x42, 0x1d, 0x30, 0xb7, 0x0c, 0x33, 0x69, 0xea, 0x49, 0x43, 0x83, 0x3b, 0xb3, 0x26, 0x30,
0xc3, 0x05, 0x1e, 0xc5, 0x5e, 0x37, 0xa2, 0x8a, 0xd6, 0x68, 0xd7, 0x1e, 0x72, 0xcf, 0x0d, 0x17,
0xbc, 0x04, 0xef, 0xc1, 0x93, 0x71, 0xc3, 0x6a, 0x7f, 0x14, 0x4b, 0x96, 0x5a, 0xd5, 0x37, 0xd6,
0xee, 0x9e, 0x6f, 0xbf, 0x73, 0xce, 0x77, 0x76, 0xcf, 0x1a, 0xdc, 0x1b, 0x7f, 0x16, 0x9c, 0x30,
0x12, 0x2f, 0x82, 0x31, 0x61, 0x27, 0x3c, 0x08, 0x43, 0x12, 0x77, 0x67, 0x31, 0xe5, 0x14, 0xed,
0x27, 0xb6, 0xae, 0xb1, 0x75, 0x95, 0xcd, 0x3d, 0x94, 0x3b, 0xc6, 0x37, 0x7e, 0xcc, 0xd5, 0xaf,
0x42, 0xbb, 0x9d, 0xe5, 0x75, 0x1a, 0x4d, 0x83, 0xd7, 0xda, 0xa0, 0x5c, 0xc4, 0x24, 0x24, 0x3e,
0x23, 0xe6, 0x9b, 0xd9, 0x64, 0x6c, 0x41, 0x34, 0xa5, 0xda, 0x70, 0x94, 0x31, 0x30, 0xee, 0xf3,
0x39, 0xcb, 0xf0, 0x2d, 0x48, 0xcc, 0x02, 0x1a, 0x99, 0xaf, 0xb2, 0x79, 0xff, 0xd4, 0xe0, 0xe1,
0x65, 0xc0, 0x38, 0x56, 0x1b, 0x19, 0x26, 0xbf, 0xcf, 0x09, 0xe3, 0x68, 0x1f, 0x36, 0xc2, 0xe0,
0x36, 0xe0, 0x8e, 0xf5, 0xc8, 0x3a, 0xae, 0x63, 0x35, 0x41, 0x87, 0xd0, 0xa4, 0xd3, 0x29, 0x23,
0xdc, 0xa9, 0x89, 0xe5, 0x36, 0xd6, 0x33, 0xf4, 0x1d, 0xd8, 0x8c, 0xc6, 0x7c, 0x74, 0x7d, 0xe7,
0xd4, 0x85, 0x61, 0xa7, 0xf7, 0x69, 0xb7, 0x48, 0x8a, 0x6e, 0xe2, 0x69, 0x28, 0x80, 0xdd, 0xe4,
0xe7, 0xd9, 0x1d, 0x6e, 0x32, 0xf9, 0x4d, 0x78, 0xa7, 0x41, 0xc8, 0x49, 0xec, 0x34, 0x14, 0xaf,
0x9a, 0xa1, 0x73, 0x00, 0xc9, 0x4b, 0xe3, 0x89, 0xb0, 0x6d, 0x48, 0xea, 0xe3, 0x0a, 0xd4, 0xaf,
0x12, 0x3c, 0x6e, 0x33, 0x33, 0x44, 0xdf, 0xc2, 0x96, 0x92, 0x64, 0x34, 0xa6, 0x13, 0xc2, 0x9c,
0xe6, 0xa3, 0xba, 0xa0, 0x3a, 0x52, 0x54, 0x46, 0xe1, 0xa1, 0x12, 0xed, 0x4c, 0x20, 0xf0, 0xa6,
0x82, 0x27, 0x63, 0xe6, 0xfd, 0x0a, 0x2d, 0x43, 0xef, 0xf5, 0xa0, 0xa9, 0x82, 0x47, 0x9b, 0x60,
0x5f, 0x0d, 0x5e, 0x0e, 0x5e, 0xfd, 0x3c, 0xd8, 0x7d, 0x80, 0x5a, 0xd0, 0x18, 0x9c, 0xfe, 0xd0,
0xdf, 0xb5, 0xd0, 0x1e, 0x6c, 0x5f, 0x9e, 0x0e, 0x7f, 0x1c, 0xe1, 0xfe, 0x65, 0xff, 0x74, 0xd8,
0x7f, 0xbe, 0x5b, 0xf3, 0x3e, 0x86, 0x76, 0x1a, 0x15, 0xb2, 0xa1, 0x7e, 0x3a, 0x3c, 0x53, 0x5b,
0x9e, 0xf7, 0xc5, 0xc8, 0xf2, 0xfe, 0xb2, 0x60, 0x3f, 0x5b, 0x04, 0x36, 0xa3, 0x11, 0x23, 0x49,
0x15, 0xc6, 0x74, 0x1e, 0xa5, 0x55, 0x90, 0x13, 0x84, 0xa0, 0x11, 0x91, 0x3f, 0x4c, 0x0d, 0xe4,
0x38, 0x41, 0x72, 0xca, 0xfd, 0x50, 0xea, 0x2f, 0x90, 0x72, 0x82, 0xbe, 0x86, 0x96, 0x4e, 0x8e,
0x09, 0x65, 0xeb, 0xc7, 0x9b, 0xbd, 0x83, 0x6c, 0xca, 0xda, 0x23, 0x4e, 0x61, 0xde, 0x39, 0x74,
0xce, 0x89, 0x89, 0x44, 0x29, 0x62, 0xce, 0x44, 0xe2, 0xd7, 0xbf, 0x25, 0x32, 0x98, 0xc4, 0xaf,
0x18, 0x23, 0x07, 0x6c, 0x7d, 0xa0, 0x64, 0x38, 0x1b, 0xd8, 0x4c, 0x3d, 0x0e, 0xce, 0x2a, 0x91,
0xce, 0xab, 0x88, 0xe9, 0x33, 0x68, 0x24, 0xc7, 0x59, 0xd2, 0x6c, 0xf6, 0x50, 0x36, 0xce, 0x0b,
0x61, 0xc1, 0xd2, 0x8e, 0x3e, 0x84, 0x76, 0x82, 0x67, 0x33, 0x7f, 0x4c, 0x64, 0xb6, 0x6d, 0x7c,
0xbf, 0xe0, 0xbd, 0x58, 0xf6, 0x7a, 0x46, 0x23, 0x4e, 0x22, 0xbe, 0x5e, 0xfc, 0x97, 0x70, 0x54,
0xc0, 0xa4, 0x13, 0x38, 0x01, 0x5b, 0x87, 0x26, 0xd9, 0x4a, 0x75, 0x35, 0x28, 0xef, 0x5f, 0x51,
0xe2, 0xab, 0xd9, 0xc4, 0xe7, 0xc4, 0x98, 0xde, 0x12, 0xd4, 0x63, 0x51, 0xf6, 0xa4, 0x2d, 0x68,
0x2d, 0xf6, 0x14, 0xb7, 0xea, 0x1d, 0x67, 0xc9, 0x2f, 0x56, 0x76, 0xf4, 0x04, 0x9a, 0x0b, 0x3f,
0x14, 0x3c, 0x52, 0x88, 0x54, 0x35, 0x8d, 0x94, 0x3d, 0x05, 0x6b, 0x04, 0xea, 0x80, 0x3d, 0x89,
0xef, 0x46, 0xf1, 0x3c, 0x92, 0x97, 0xac, 0x85, 0x9b, 0x62, 0x8a, 0xe7, 0x11, 0xfa, 0x04, 0xb6,
0x27, 0x01, 0xf3, 0xaf, 0x43, 0x32, 0xba, 0xa1, 0xf4, 0x0d, 0x93, 0xf7, 0xac, 0x85, 0xb7, 0xf4,
0xe2, 0x8b, 0x64, 0x4d, 0xe8, 0x7a, 0x90, 0x0b, 0x7f, 0x5d, 0x25, 0xfe, 0xb4, 0xe0, 0x10, 0xd3,
0x30, 0xbc, 0xf6, 0xc7, 0x6f, 0x2a, 0x68, 0xb1, 0x14, 0x76, 0xed, 0xed, 0x61, 0xd7, 0x57, 0xc3,
0x5e, 0x2e, 0x6f, 0x23, 0x5b, 0xde, 0xef, 0xa1, 0xb3, 0x12, 0xc5, 0xba, 0x29, 0xfd, 0x67, 0xc1,
0xc1, 0x45, 0x24, 0x3a, 0x46, 0x18, 0xe6, 0x32, 0x4a, 0x2b, 0x69, 0x55, 0xae, 0x64, 0xed, 0x7d,
0x2a, 0x59, 0xcf, 0x48, 0x62, 0xf4, 0x6b, 0x2c, 0xe9, 0x57, 0xa5, 0xba, 0xd9, 0x3b, 0xd5, 0xcc,
0xdd, 0x29, 0xf4, 0x11, 0x40, 0x4c, 0xe6, 0x8c, 0x8c, 0x24, 0xb9, 0x2d, 0xf7, 0xb7, 0xe5, 0xca,
0x40, 0x2c, 0x78, 0x17, 0x70, 0x98, 0x4f, 0x7e, 0x5d, 0x21, 0x6f, 0xa0, 0x73, 0x15, 0x05, 0x85,
0x4a, 0x16, 0x9d, 0x8d, 0x95, 0xdc, 0x6a, 0x05, 0xb9, 0x89, 0xce, 0x38, 0x9b, 0xc7, 0xaf, 0x89,
0xd6, 0x4a, 0x4d, 0xbc, 0x97, 0xe0, 0xac, 0x7a, 0x5a, 0x37, 0xec, 0x87, 0xb0, 0x27, 0x5a, 0xc5,
0x4f, 0xea, 0x64, 0xe9, 0x80, 0xbd, 0x3e, 0xa0, 0xe5, 0xc5, 0x7b, 0x6e, 0xbd, 0x94, 0xe5, 0x36,
0xaf, 0xb2, 0xc1, 0x1b, 0x54, 0xef, 0x6f, 0x1b, 0x76, 0x4c, 0x13, 0x55, 0x4f, 0x1e, 0x0a, 0x60,
0x6b, 0xf9, 0xb5, 0x40, 0x9f, 0x97, 0xbf, 0x88, 0xb9, 0x67, 0xdd, 0x7d, 0x52, 0x05, 0xaa, 0x42,
0xf5, 0x1e, 0x7c, 0x65, 0x21, 0x06, 0xbb, 0xf9, 0x26, 0x8e, 0x9e, 0x16, 0x73, 0x94, 0xbc, 0x1a,
0x6e, 0xb7, 0x2a, 0xdc, 0xb8, 0x45, 0x0b, 0x29, 0x67, 0xb6, 0xf3, 0xa2, 0x77, 0xd2, 0x64, 0x9b,
0xbd, 0x7b, 0x52, 0x19, 0x9f, 0xfa, 0xfd, 0x0d, 0xb6, 0x33, 0x3d, 0x0e, 0x95, 0xa8, 0x55, 0xd4,
0xc7, 0xdd, 0x2f, 0x2a, 0x61, 0x53, 0x5f, 0xb7, 0xb0, 0x93, 0xbd, 0x34, 0xa8, 0x84, 0xa0, 0xb0,
0xaf, 0xb8, 0x5f, 0x56, 0x03, 0xa7, 0xee, 0x44, 0x1d, 0xf3, 0xc7, 0xbd, 0xac, 0x8e, 0x25, 0x17,
0xb0, 0xac, 0x8e, 0x65, 0xb7, 0x48, 0x38, 0xf5, 0x01, 0xee, 0x6f, 0x00, 0x7a, 0x5c, 0x5a, 0x90,
0xec, 0xc5, 0x71, 0x8f, 0xdf, 0x0d, 0x4c, 0x5d, 0xcc, 0xe0, 0x83, 0x5c, 0x17, 0x47, 0x25, 0xd2,
0x14, 0x3f, 0x39, 0xee, 0xd3, 0x8a, 0x68, 0xe3, 0xf1, 0x19, 0xfc, 0xd2, 0x32, 0xe0, 0xeb, 0xa6,
0xfc, 0x0f, 0xfd, 0xcd, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x76, 0x9b, 0x44, 0xa7, 0x14, 0x0c,
0x00, 0x00,
// 997 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0x5f, 0x6f, 0xe3, 0x44,
0x10, 0xaf, 0x93, 0x34, 0x7f, 0xa6, 0x7f, 0x68, 0xf7, 0xda, 0x26, 0xb5, 0x00, 0x9d, 0x8c, 0xe0,
0xca, 0xc1, 0xa5, 0x10, 0x9e, 0x90, 0x10, 0x52, 0xaf, 0x17, 0xb5, 0xe5, 0x4a, 0x4e, 0xda, 0x50,
0x90, 0x78, 0x20, 0x72, 0x93, 0xcd, 0xd5, 0x9c, 0xeb, 0x0d, 0xde, 0x4d, 0x75, 0x7d, 0xe7, 0x85,
0xaf, 0xc1, 0xf7, 0xe0, 0x3b, 0xf1, 0xce, 0x0b, 0xeb, 0xfd, 0xe3, 0xda, 0x8e, 0x9d, 0xf3, 0xe5,
0x25, 0xde, 0xdd, 0x99, 0xfd, 0xcd, 0xcc, 0x6f, 0x76, 0x66, 0x5a, 0xb0, 0x6f, 0xdc, 0x99, 0x77,
0xcc, 0x48, 0x78, 0xe7, 0x8d, 0x09, 0x3b, 0xe6, 0x9e, 0xef, 0x93, 0xb0, 0x3b, 0x0b, 0x29, 0xa7,
0x68, 0x2f, 0x92, 0x75, 0x8d, 0xac, 0xab, 0x64, 0xf6, 0x81, 0xbc, 0x31, 0xbe, 0x71, 0x43, 0xae,
0x7e, 0x95, 0xb6, 0xdd, 0x4e, 0x9e, 0xd3, 0x60, 0xea, 0xbd, 0xd6, 0x02, 0x65, 0x22, 0x24, 0x3e,
0x71, 0x19, 0x31, 0xdf, 0xd4, 0x25, 0x23, 0xf3, 0x82, 0x29, 0xd5, 0x82, 0xc3, 0x94, 0x80, 0x71,
0x97, 0xcf, 0x59, 0x0a, 0xef, 0x8e, 0x84, 0xcc, 0xa3, 0x81, 0xf9, 0x2a, 0x99, 0xf3, 0x77, 0x05,
0x1e, 0x5d, 0x7a, 0x8c, 0x63, 0x75, 0x91, 0x61, 0xf2, 0xc7, 0x9c, 0x30, 0x8e, 0xf6, 0x60, 0xdd,
0xf7, 0x6e, 0x3d, 0xde, 0xb1, 0x1e, 0x5b, 0x47, 0x55, 0xac, 0x36, 0xe8, 0x00, 0xea, 0x74, 0x3a,
0x65, 0x84, 0x77, 0x2a, 0xe2, 0xb8, 0x85, 0xf5, 0x0e, 0x7d, 0x0f, 0x0d, 0x46, 0x43, 0x3e, 0xba,
0xbe, 0xef, 0x54, 0x85, 0x60, 0xbb, 0xf7, 0x69, 0x37, 0x8f, 0x8a, 0x6e, 0x64, 0x69, 0x28, 0x14,
0xbb, 0xd1, 0xcf, 0xf3, 0x7b, 0x5c, 0x67, 0xf2, 0x1b, 0xe1, 0x4e, 0x3d, 0x9f, 0x93, 0xb0, 0x53,
0x53, 0xb8, 0x6a, 0x87, 0xce, 0x00, 0x24, 0x2e, 0x0d, 0x27, 0x42, 0xb6, 0x2e, 0xa1, 0x8f, 0x4a,
0x40, 0xbf, 0x8a, 0xf4, 0x71, 0x8b, 0x99, 0x25, 0xfa, 0x0e, 0x36, 0x15, 0x25, 0xa3, 0x31, 0x9d,
0x10, 0xd6, 0xa9, 0x3f, 0xae, 0x0a, 0xa8, 0x43, 0x05, 0x65, 0x18, 0x1e, 0x2a, 0xd2, 0x4e, 0x85,
0x06, 0xde, 0x50, 0xea, 0xd1, 0x9a, 0x39, 0xbf, 0x41, 0xd3, 0xc0, 0x3b, 0x3d, 0xa8, 0x2b, 0xe7,
0xd1, 0x06, 0x34, 0xae, 0x06, 0x2f, 0x07, 0xaf, 0x7e, 0x19, 0xec, 0xac, 0xa1, 0x26, 0xd4, 0x06,
0x27, 0x3f, 0xf6, 0x77, 0x2c, 0xb4, 0x0b, 0x5b, 0x97, 0x27, 0xc3, 0x9f, 0x46, 0xb8, 0x7f, 0xd9,
0x3f, 0x19, 0xf6, 0x5f, 0xec, 0x54, 0x9c, 0x8f, 0xa1, 0x15, 0x7b, 0x85, 0x1a, 0x50, 0x3d, 0x19,
0x9e, 0xaa, 0x2b, 0x2f, 0xfa, 0x62, 0x65, 0x39, 0x7f, 0x59, 0xb0, 0x97, 0x4e, 0x02, 0x9b, 0xd1,
0x80, 0x91, 0x28, 0x0b, 0x63, 0x3a, 0x0f, 0xe2, 0x2c, 0xc8, 0x0d, 0x42, 0x50, 0x0b, 0xc8, 0x5b,
0x93, 0x03, 0xb9, 0x8e, 0x34, 0x39, 0xe5, 0xae, 0x2f, 0xf9, 0x17, 0x9a, 0x72, 0x83, 0xbe, 0x86,
0xa6, 0x0e, 0x8e, 0x09, 0x66, 0xab, 0x47, 0x1b, 0xbd, 0xfd, 0x74, 0xc8, 0xda, 0x22, 0x8e, 0xd5,
0x9c, 0x33, 0x68, 0x9f, 0x11, 0xe3, 0x89, 0x62, 0xc4, 0xbc, 0x89, 0xc8, 0xae, 0x7b, 0x4b, 0xa4,
0x33, 0x91, 0x5d, 0xb1, 0x46, 0x1d, 0x68, 0xe8, 0x07, 0x25, 0xdd, 0x59, 0xc7, 0x66, 0xeb, 0x70,
0xe8, 0x2c, 0x02, 0xe9, 0xb8, 0xf2, 0x90, 0x3e, 0x83, 0x5a, 0xf4, 0x9c, 0x25, 0xcc, 0x46, 0x0f,
0xa5, 0xfd, 0xbc, 0x10, 0x12, 0x2c, 0xe5, 0xe8, 0x43, 0x68, 0x45, 0xfa, 0x6c, 0xe6, 0x8e, 0x89,
0x8c, 0xb6, 0x85, 0x1f, 0x0e, 0x9c, 0xf3, 0xa4, 0xd5, 0x53, 0x1a, 0x70, 0x12, 0xf0, 0xd5, 0xfc,
0xbf, 0x84, 0xc3, 0x1c, 0x24, 0x1d, 0xc0, 0x31, 0x34, 0xb4, 0x6b, 0x12, 0xad, 0x90, 0x57, 0xa3,
0xe5, 0xfc, 0x23, 0x52, 0x7c, 0x35, 0x9b, 0xb8, 0x9c, 0x18, 0xd1, 0x12, 0xa7, 0x9e, 0x88, 0xb4,
0x47, 0x6d, 0x41, 0x73, 0xb1, 0xab, 0xb0, 0x55, 0xef, 0x38, 0x8d, 0x7e, 0xb1, 0x92, 0xa3, 0xa7,
0x50, 0xbf, 0x73, 0x7d, 0x81, 0x23, 0x89, 0x88, 0x59, 0xd3, 0x9a, 0xb2, 0xa7, 0x60, 0xad, 0x81,
0xda, 0xd0, 0x98, 0x84, 0xf7, 0xa3, 0x70, 0x1e, 0xc8, 0x22, 0x6b, 0xe2, 0xba, 0xd8, 0xe2, 0x79,
0x80, 0x3e, 0x81, 0xad, 0x89, 0xc7, 0xdc, 0x6b, 0x9f, 0x8c, 0x6e, 0x28, 0x7d, 0xc3, 0x64, 0x9d,
0x35, 0xf1, 0xa6, 0x3e, 0x3c, 0x8f, 0xce, 0x04, 0xaf, 0xfb, 0x19, 0xf7, 0x57, 0x65, 0xe2, 0x4f,
0x0b, 0x0e, 0x30, 0xf5, 0xfd, 0x6b, 0x77, 0xfc, 0xa6, 0x04, 0x17, 0x09, 0xb7, 0x2b, 0xcb, 0xdd,
0xae, 0x2e, 0xba, 0x9d, 0x4c, 0x6f, 0x2d, 0x9d, 0xde, 0x1f, 0xa0, 0xbd, 0xe0, 0xc5, 0xaa, 0x21,
0xfd, 0x67, 0xc1, 0xfe, 0x45, 0x20, 0x3a, 0x86, 0xef, 0x67, 0x22, 0x8a, 0x33, 0x69, 0x95, 0xce,
0x64, 0xe5, 0x7d, 0x32, 0x59, 0x4d, 0x51, 0x62, 0xf8, 0xab, 0x25, 0xf8, 0x2b, 0x93, 0xdd, 0x74,
0x4d, 0xd5, 0x33, 0x35, 0x85, 0x3e, 0x02, 0x08, 0xc9, 0x9c, 0x91, 0x91, 0x04, 0x6f, 0xc8, 0xfb,
0x2d, 0x79, 0x32, 0x10, 0x07, 0xce, 0x05, 0x1c, 0x64, 0x83, 0x5f, 0x95, 0xc8, 0x1b, 0x68, 0x5f,
0x05, 0x5e, 0x2e, 0x93, 0x79, 0x6f, 0x63, 0x21, 0xb6, 0x4a, 0x4e, 0x6c, 0xa2, 0x33, 0xce, 0xe6,
0xe1, 0x6b, 0xa2, 0xb9, 0x52, 0x1b, 0xe7, 0x25, 0x74, 0x16, 0x2d, 0xad, 0xea, 0xf6, 0x23, 0xd8,
0x15, 0xad, 0xe2, 0x67, 0xf5, 0xb2, 0xb4, 0xc3, 0x4e, 0x1f, 0x50, 0xf2, 0xf0, 0x01, 0x5b, 0x1f,
0xa5, 0xb1, 0xcd, 0x54, 0x36, 0xfa, 0xf1, 0x3b, 0xfd, 0x56, 0x62, 0x9f, 0x8b, 0xe9, 0x40, 0x45,
0x92, 0x97, 0x90, 0xb1, 0x03, 0xd5, 0x5b, 0xf7, 0xad, 0xee, 0x62, 0xd1, 0x52, 0xb4, 0x72, 0x94,
0xbc, 0xaa, 0x3d, 0x48, 0xce, 0x04, 0xab, 0xd4, 0x4c, 0xe8, 0xfd, 0xdb, 0x80, 0x6d, 0xd3, 0xc8,
0xd5, 0xd8, 0x45, 0x1e, 0x6c, 0x26, 0x27, 0x16, 0xfa, 0xbc, 0x78, 0x2a, 0x67, 0xfe, 0xb4, 0xb0,
0x9f, 0x96, 0x51, 0x55, 0xce, 0x3a, 0x6b, 0x5f, 0x59, 0x88, 0xc1, 0x4e, 0x76, 0x90, 0xa0, 0x67,
0xf9, 0x18, 0x05, 0x93, 0xcb, 0xee, 0x96, 0x55, 0x37, 0x66, 0xd1, 0x9d, 0xa4, 0x3d, 0xdd, 0xfd,
0xd1, 0x3b, 0x61, 0xd2, 0x03, 0xc7, 0x3e, 0x2e, 0xad, 0x1f, 0xdb, 0xfd, 0x1d, 0xb6, 0x52, 0x7d,
0x16, 0x15, 0xb0, 0x95, 0x37, 0x4b, 0xec, 0x2f, 0x4a, 0xe9, 0xc6, 0xb6, 0x6e, 0x61, 0x3b, 0x5d,
0xb8, 0xa8, 0x00, 0x20, 0xb7, 0xb7, 0xd9, 0x5f, 0x96, 0x53, 0x8e, 0xcd, 0x89, 0x3c, 0x66, 0x4b,
0xae, 0x28, 0x8f, 0x05, 0x4d, 0xa0, 0x28, 0x8f, 0x45, 0x95, 0x2c, 0x8c, 0xba, 0x00, 0x0f, 0x55,
0x88, 0x9e, 0x14, 0x26, 0x24, 0x5d, 0xbc, 0xf6, 0xd1, 0xbb, 0x15, 0x63, 0x13, 0x33, 0xf8, 0x20,
0x33, 0x49, 0x50, 0x01, 0x35, 0xf9, 0x63, 0xcf, 0x7e, 0x56, 0x52, 0x3b, 0x13, 0x94, 0x2e, 0xec,
0x25, 0x41, 0xa5, 0xbb, 0xc6, 0x92, 0xa0, 0x32, 0x3d, 0xc2, 0x59, 0x7b, 0x0e, 0xbf, 0x36, 0x8d,
0xde, 0x75, 0x5d, 0xfe, 0xab, 0xf0, 0xcd, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8a, 0x4d,
0xa4, 0xfb, 0x0c, 0x00, 0x00,
}

@ -126,6 +126,18 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) {
}
}
// History returns the revision history for the release with the provided name, or
// returns ErrReleaseNotFound if no such release name exists.
func (s *Storage) History(name string) ([]*rspb.Release, error) {
log.Printf("Getting release history for '%s'\n", name)
l, err := s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"})
if err != nil {
return nil, err
}
return l, nil
}
// makeKey concatenates a release name and version into
// a string with format ```<release_name>#v<version>```.
// This key is used to uniquely identify storage objects.

@ -186,6 +186,37 @@ func TestStorageDeployed(t *testing.T) {
}
}
func TestStorageHistory(t *testing.T) {
storage := Init(driver.NewMemory())
const name = "angry-bird"
// setup storage with test releases
setup := func() {
// release records
rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.Status_DEPLOYED}.ToRelease()
// create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)")
}
setup()
h, err := storage.History(name)
if err != nil {
t.Fatalf("Failed to query for release history (%q): %s\n", name, err)
}
if len(h) != 4 {
t.Fatalf("Release history (%q) is empty\n", name)
}
}
type ReleaseTestData struct {
Name string
Version int32

Loading…
Cancel
Save