pull/1092/merge
Brian 9 years ago committed by GitHub
commit 65a83f8c1f

@ -129,6 +129,8 @@ message ListReleasesResponse {
message GetReleaseStatusRequest { message GetReleaseStatusRequest {
// Name is the name of the release // Name is the name of the release
string name = 1; string name = 1;
// Version is the version of the release
int32 version = 2;
} }
// GetReleaseStatusResponse is the response indicating the status of the named release. // GetReleaseStatusResponse is the response indicating the status of the named release.
@ -144,6 +146,8 @@ message GetReleaseStatusResponse {
message GetReleaseContentRequest { message GetReleaseContentRequest {
// The name of the release // The name of the release
string name = 1; string name = 1;
// Version is the version of the release
int32 version = 2;
} }
// GetReleaseContentResponse is a response containing the contents of a release. // GetReleaseContentResponse is a response containing the contents of a release.
@ -165,6 +169,9 @@ message UpdateReleaseRequest {
// DisableHooks causes the server to skip running any hooks for the upgrade. // DisableHooks causes the server to skip running any hooks for the upgrade.
bool disable_hooks = 5; bool disable_hooks = 5;
// Version is the version of the release to be updated
int32 version = 6;
} }
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.

@ -48,6 +48,7 @@ type getCmd struct {
release string release string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
version int32
} }
func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -71,6 +72,9 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
return get.run() return get.run()
}, },
} }
cmd.PersistentFlags().Int32Var(&get.version, "version", 0, "version of release")
cmd.AddCommand(newGetValuesCmd(nil, out)) cmd.AddCommand(newGetValuesCmd(nil, out))
cmd.AddCommand(newGetManifestCmd(nil, out)) cmd.AddCommand(newGetManifestCmd(nil, out))
cmd.AddCommand(newGetHooksCmd(nil, out)) cmd.AddCommand(newGetHooksCmd(nil, out))
@ -96,7 +100,7 @@ MANIFEST:
// getCmd is the command that implements 'helm get' // getCmd is the command that implements 'helm get'
func (g *getCmd) run() error { func (g *getCmd) run() error {
res, err := g.client.ReleaseContent(g.release) res, err := g.client.ReleaseContent(g.release, helm.ContentReleaseVersion(g.version))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }

@ -34,6 +34,7 @@ type statusCmd struct {
release string release string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
version int32
} }
func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command { func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -57,11 +58,14 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
return status.run() return status.run()
}, },
} }
cmd.PersistentFlags().Int32Var(&status.version, "version", 0, "version of release")
return cmd return cmd
} }
func (s *statusCmd) run() error { func (s *statusCmd) run() error {
res, err := s.client.ReleaseStatus(s.release) res, err := s.client.ReleaseStatus(s.release, helm.StatusReleaseVersion(s.version))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }

@ -31,7 +31,6 @@ import (
"k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
"k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned"
) )
@ -208,7 +207,7 @@ func New() *Environment {
return &Environment{ return &Environment{
EngineYard: ey, EngineYard: ey,
Releases: storage.Init(driver.NewMemory()), Releases: storage.Init(nil),
KubeClient: kube.New(nil), KubeClient: kube.New(nil),
} }
} }

@ -23,9 +23,6 @@ import (
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned" unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/client/unversioned/testclient"
) )
@ -38,52 +35,6 @@ func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]s
return e.out, nil return e.out, nil
} }
type mockReleaseStorage struct {
rel *release.Release
}
var _ driver.Driver = (*mockReleaseStorage)(nil)
func (r *mockReleaseStorage) Create(v *release.Release) error {
r.rel = v
return nil
}
func (r *mockReleaseStorage) Name() string {
return "mockReleaseStorage"
}
func (r *mockReleaseStorage) Get(k string) (*release.Release, error) {
return r.rel, nil
}
func (r *mockReleaseStorage) Update(v *release.Release) error {
r.rel = v
return nil
}
func (r *mockReleaseStorage) Delete(k string) (*release.Release, error) {
return r.rel, nil
}
func (r *mockReleaseStorage) List(func(*release.Release) bool) ([]*release.Release, error) {
return []*release.Release{}, nil
}
func (r *mockReleaseStorage) Query(labels map[string]string) ([]*release.Release, error) {
return []*release.Release{}, nil
}
func (r *mockReleaseStorage) History(n string) ([]*release.Release, error) {
res := []*release.Release{}
rel, err := r.Get(n)
if err != nil {
return res, err
}
res = append(res, rel)
return res, nil
}
type mockKubeClient struct { type mockKubeClient struct {
} }
@ -126,32 +77,6 @@ func TestEngine(t *testing.T) {
} }
} }
func TestReleaseStorage(t *testing.T) {
rs := &mockReleaseStorage{}
env := New()
env.Releases = storage.Init(rs)
release := &release.Release{Name: "mariner"}
if err := env.Releases.Create(release); err != nil {
t.Fatalf("failed to store release: %s", err)
}
if err := env.Releases.Update(release); err != nil {
t.Fatalf("failed to update release: %s", err)
}
if v, err := env.Releases.Get("albatross"); err != nil {
t.Errorf("Error fetching release: %s", err)
} else if v.Name != "mariner" {
t.Errorf("Expected mariner, got %q", v.Name)
}
if _, err := env.Releases.Delete("albatross"); err != nil {
t.Fatalf("failed to delete release: %s", err)
}
}
func TestKubeClient(t *testing.T) { func TestKubeClient(t *testing.T) {
kc := &mockKubeClient{} kc := &mockKubeClient{}
env := New() env := New()

@ -158,7 +158,7 @@ func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
if req.Name == "" { if req.Name == "" {
return nil, errMissingRelease return nil, errMissingRelease
} }
rel, err := s.env.Releases.Get(req.Name) rel, err := s.env.Releases.Get(req.Name, req.Version)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -183,7 +183,7 @@ func (s *releaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleas
if req.Name == "" { if req.Name == "" {
return nil, errMissingRelease return nil, errMissingRelease
} }
rel, err := s.env.Releases.Get(req.Name) rel, err := s.env.Releases.Get(req.Name, req.Version)
return &services.GetReleaseContentResponse{Release: rel}, err return &services.GetReleaseContentResponse{Release: rel}, err
} }
@ -198,7 +198,7 @@ func (s *releaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
return nil, err return nil, err
} }
if err := s.env.Releases.Update(updatedRelease); err != nil { if err := s.env.Releases.Create(updatedRelease); err != nil {
return nil, err return nil, err
} }
@ -234,6 +234,11 @@ func (s *releaseServer) performUpdate(originalRelease, updatedRelease *release.R
} }
} }
originalRelease.Info.Status.Code = release.Status_SUPERSEDED
if err := env.Releases.Update(originalRelease); err != nil {
return nil, fmt.Errorf("Update of %s failed: %s", originalRelease.Name, err)
}
updatedRelease.Info.Status.Code = release.Status_DEPLOYED updatedRelease.Info.Status.Code = release.Status_DEPLOYED
return res, nil return res, nil
@ -250,7 +255,7 @@ func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
} }
// finds the non-deleted release with the given name // finds the non-deleted release with the given name
currentRelease, err := s.env.Releases.Get(req.Name) currentRelease, err := s.env.Releases.Get(req.Name, req.Version)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -302,7 +307,7 @@ func (s *releaseServer) uniqName(start string, reuse bool) (string, error) {
return "", fmt.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) return "", fmt.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen)
} }
if rel, err := s.env.Releases.Get(start); err == driver.ErrReleaseNotFound { if rel, err := s.env.Releases.Get(start, 0); err == driver.ErrReleaseNotFound {
return start, nil return start, nil
} else if st := rel.Info.Status.Code; reuse && (st == release.Status_DELETED || st == release.Status_FAILED) { } else if st := rel.Info.Status.Code; reuse && (st == release.Status_DELETED || st == release.Status_FAILED) {
// Allowe re-use of names if the previous release is marked deleted. // Allowe re-use of names if the previous release is marked deleted.
@ -323,7 +328,7 @@ func (s *releaseServer) uniqName(start string, reuse bool) (string, error) {
log.Printf("info: Candidate name %q exceeds maximum length %d. Skipping.", name, releaseNameMaxLen) log.Printf("info: Candidate name %q exceeds maximum length %d. Skipping.", name, releaseNameMaxLen)
continue continue
} }
if _, err := s.env.Releases.Get(name); err == driver.ErrReleaseNotFound { if _, err := s.env.Releases.Get(name, 0); err == driver.ErrReleaseNotFound {
return name, nil return name, nil
} }
log.Printf("info: Name %q is taken. Searching again.", name) log.Printf("info: Name %q is taken. Searching again.", name)
@ -525,7 +530,7 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
return nil, errMissingRelease return nil, errMissingRelease
} }
rel, err := s.env.Releases.Get(req.Name) rel, err := s.env.Releases.Get(req.Name, 0)
if err != nil { if err != nil {
log.Printf("uninstall: Release not loaded: %s", req.Name) log.Printf("uninstall: Release not loaded: %s", req.Name)
return nil, err return nil, err

@ -178,7 +178,7 @@ func TestInstallRelease(t *testing.T) {
t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace)
} }
rel, err := rs.env.Releases.Get(res.Release.Name) rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil { if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
} }
@ -248,7 +248,7 @@ func TestInstallReleaseDryRun(t *testing.T) {
t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest) t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest)
} }
if _, err := rs.env.Releases.Get(res.Release.Name); err == nil { if _, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version); err == nil {
t.Errorf("Expected no stored release.") t.Errorf("Expected no stored release.")
} }
@ -317,10 +317,11 @@ func TestUpdateRelease(t *testing.T) {
{Name: "hooks", Data: []byte(manifestWithUpgradeHooks)}, {Name: "hooks", Data: []byte(manifestWithUpgradeHooks)},
}, },
}, },
Version: 1,
} }
res, err := rs.UpdateRelease(c, req) res, err := rs.UpdateRelease(c, req)
if err != nil { if err != nil {
t.Errorf("Failed updated: %s", err) t.Fatalf("Failed updated: %s", err)
} }
if res.Release.Name == "" { if res.Release.Name == "" {
@ -335,7 +336,7 @@ func TestUpdateRelease(t *testing.T) {
t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace)
} }
updated, err := rs.env.Releases.Get(res.Release.Name) updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil { if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
} }
@ -392,7 +393,7 @@ func TestUpdateReleaseNoHooks(t *testing.T) {
res, err := rs.UpdateRelease(c, req) res, err := rs.UpdateRelease(c, req)
if err != nil { if err != nil {
t.Errorf("Failed updated: %s", err) t.Fatalf("Failed updated: %s", err)
} }
if hl := res.Release.Hooks[0].LastRun; hl != nil { if hl := res.Release.Hooks[0].LastRun; hl != nil {
@ -412,7 +413,7 @@ func TestUninstallRelease(t *testing.T) {
res, err := rs.UninstallRelease(c, req) res, err := rs.UninstallRelease(c, req)
if err != nil { if err != nil {
t.Errorf("Failed uninstall: %s", err) t.Fatalf("Failed uninstall: %s", err)
} }
if res.Release.Name != "angry-panda" { if res.Release.Name != "angry-panda" {
@ -451,7 +452,7 @@ func TestUninstallReleaseNoHooks(t *testing.T) {
res, err := rs.UninstallRelease(c, req) res, err := rs.UninstallRelease(c, req)
if err != nil { if err != nil {
t.Errorf("Failed uninstall: %s", err) t.Fatalf("Failed uninstall: %s", err)
} }
// The default value for a protobuf timestamp is nil. // The default value for a protobuf timestamp is nil.
@ -470,7 +471,7 @@ func TestGetReleaseContent(t *testing.T) {
res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name}) res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name})
if err != nil { if err != nil {
t.Errorf("Error getting release content: %s", err) t.Fatalf("Error getting release content: %s", err)
} }
if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name {
@ -488,7 +489,7 @@ func TestGetReleaseStatus(t *testing.T) {
res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name}) res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name})
if err != nil { if err != nil {
t.Errorf("Error getting release content: %s", err) t.Fatalf("Error getting release content: %s", err)
} }
if res.Info.Status.Code != release.Status_DEPLOYED { if res.Info.Status.Code != release.Status_DEPLOYED {

@ -47,6 +47,10 @@ type options struct {
instReq rls.InstallReleaseRequest instReq rls.InstallReleaseRequest
// release update options are applied directly to the update release request // release update options are applied directly to the update release request
updateReq rls.UpdateReleaseRequest updateReq rls.UpdateReleaseRequest
// release get status options are applied directly to the get release status request
statusReq rls.GetReleaseStatusRequest
// release get content options are applied directly to the get release content request
contentReq rls.GetReleaseContentRequest
} }
// Home specifies the location of helm home, (default = "$HOME/.helm"). // Home specifies the location of helm home, (default = "$HOME/.helm").
@ -178,13 +182,32 @@ func InstallReuseName(reuse bool) InstallOption {
} }
} }
// ContentOption -- TODO // ContentOption allows setting optional attributes when
// performing a GetReleaseContent tiller rpc.
type ContentOption func(*options) type ContentOption func(*options)
// StatusOption -- TODO // ContentReleaseVersion will instruct Tiller to retrieve the content
// of a paritcular version of a release.
func ContentReleaseVersion(version int32) ContentOption {
return func(opts *options) {
opts.contentReq.Version = version
}
}
// StatusOption allows setting optional attributes when
// performing a GetReleaseStatus tiller rpc.
type StatusOption func(*options) type StatusOption func(*options)
// DeleteOption -- TODO // StatusReleaseVersion will instruct Tiller to retrieve the status
// of a paritcular version of a release.
func StatusReleaseVersion(version int32) StatusOption {
return func(opts *options) {
opts.statusReq.Version = version
}
}
// DeleteOption allows setting optional attributes when
// performing a UninstallRelease tiller rpc.
type DeleteOption func(*options) type DeleteOption func(*options)
// UpdateOption allows specifying various settings // UpdateOption allows specifying various settings
@ -257,12 +280,18 @@ func (o *options) rpcUpdateRelease(rlsName string, chr *cpb.Chart, rlc rls.Relea
// Executes tiller.GetReleaseStatus RPC. // Executes tiller.GetReleaseStatus RPC.
func (o *options) rpcGetReleaseStatus(rlsName string, rlc rls.ReleaseServiceClient, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) { func (o *options) rpcGetReleaseStatus(rlsName string, rlc rls.ReleaseServiceClient, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) {
req := &rls.GetReleaseStatusRequest{Name: rlsName} for _, opt := range opts {
return rlc.GetReleaseStatus(context.TODO(), req) opt(o)
}
o.statusReq.Name = rlsName
return rlc.GetReleaseStatus(context.TODO(), &o.statusReq)
} }
// Executes tiller.GetReleaseContent. // Executes tiller.GetReleaseContent.
func (o *options) rpcGetReleaseContent(rlsName string, rlc rls.ReleaseServiceClient, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) { func (o *options) rpcGetReleaseContent(rlsName string, rlc rls.ReleaseServiceClient, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) {
req := &rls.GetReleaseContentRequest{Name: rlsName} for _, opt := range opts {
return rlc.GetReleaseContent(context.TODO(), req) opt(o)
}
o.contentReq.Name = rlsName
return rlc.GetReleaseContent(context.TODO(), &o.contentReq)
} }

@ -160,6 +160,8 @@ func (m *ListReleasesResponse) GetReleases() []*hapi_release3.Release {
type GetReleaseStatusRequest struct { type GetReleaseStatusRequest struct {
// Name is the name of the release // Name is the name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Version is the version of the release
Version int32 `protobuf:"varint,2,opt,name=version" json:"version,omitempty"`
} }
func (m *GetReleaseStatusRequest) Reset() { *m = GetReleaseStatusRequest{} } func (m *GetReleaseStatusRequest) Reset() { *m = GetReleaseStatusRequest{} }
@ -191,6 +193,8 @@ func (m *GetReleaseStatusResponse) GetInfo() *hapi_release2.Info {
type GetReleaseContentRequest struct { type GetReleaseContentRequest struct {
// The name of the release // The name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Version is the version of the release
Version int32 `protobuf:"varint,2,opt,name=version" json:"version,omitempty"`
} }
func (m *GetReleaseContentRequest) Reset() { *m = GetReleaseContentRequest{} } func (m *GetReleaseContentRequest) Reset() { *m = GetReleaseContentRequest{} }
@ -228,6 +232,8 @@ type UpdateReleaseRequest struct {
DryRun bool `protobuf:"varint,4,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"` DryRun bool `protobuf:"varint,4,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"`
// DisableHooks causes the server to skip running any hooks for the upgrade. // DisableHooks causes the server to skip running any hooks for the upgrade.
DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"` DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
// Version is the version of the release to be updated
Version int32 `protobuf:"varint,6,opt,name=version" json:"version,omitempty"`
} }
func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} }
@ -626,54 +632,55 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
} }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 774 bytes of a gzipped FileDescriptorProto // 792 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdb, 0x6e, 0xd3, 0x4c, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0xd3, 0x4a,
0x10, 0xae, 0x93, 0x34, 0x87, 0xe9, 0x41, 0xe9, 0xfe, 0x6d, 0x93, 0xdf, 0xfa, 0x7f, 0x84, 0x8c, 0x10, 0xae, 0xf3, 0x9f, 0xe9, 0x8f, 0xd2, 0x3d, 0x6d, 0x93, 0x63, 0x9d, 0x73, 0x74, 0x64, 0x04,
0x80, 0x52, 0xa8, 0x03, 0xe1, 0x1e, 0x29, 0x6d, 0xa3, 0xb6, 0x6a, 0x48, 0xa5, 0x0d, 0x05, 0x89, 0x94, 0x02, 0x0e, 0x84, 0x7b, 0xa4, 0xb4, 0x8d, 0xda, 0xaa, 0x21, 0x95, 0x36, 0x14, 0x24, 0x2e,
0x0b, 0x22, 0x37, 0xd9, 0x50, 0x83, 0x6b, 0x07, 0xef, 0xa6, 0xa2, 0x8f, 0xc0, 0x1b, 0x71, 0xc3, 0x88, 0xdc, 0x64, 0x43, 0x0d, 0xae, 0x1d, 0xbc, 0x9b, 0x88, 0x3e, 0x02, 0x6f, 0xc4, 0x9b, 0xf0,
0xdb, 0xf0, 0x16, 0xdc, 0xb0, 0x07, 0xaf, 0xc9, 0xc1, 0x06, 0xd3, 0x1b, 0x67, 0x77, 0xe7, 0xdb, 0x02, 0xbc, 0x05, 0x37, 0xac, 0x77, 0xbd, 0x26, 0x4e, 0x6c, 0x30, 0xb9, 0x71, 0x76, 0x77, 0xbe,
0x6f, 0x66, 0xbe, 0x99, 0x9d, 0x16, 0xcc, 0x4b, 0x67, 0xec, 0x36, 0x28, 0x09, 0xaf, 0xdd, 0x01, 0xfd, 0x66, 0xe6, 0x9b, 0x9d, 0x69, 0x41, 0xbf, 0xb6, 0x26, 0x76, 0x93, 0x12, 0x7f, 0x66, 0x0f,
0xa1, 0x0d, 0xe6, 0x7a, 0x1e, 0x09, 0xed, 0x71, 0x18, 0xb0, 0x00, 0x6d, 0x0a, 0x9b, 0xad, 0x6d, 0x09, 0x6d, 0x32, 0xdb, 0x71, 0x88, 0x6f, 0x4e, 0x7c, 0x8f, 0x79, 0x68, 0x27, 0xb0, 0x99, 0xca,
0xb6, 0xb2, 0x99, 0xdb, 0xf2, 0xc6, 0xe0, 0xd2, 0x09, 0x99, 0xfa, 0x2a, 0xb4, 0x59, 0x9b, 0x3e, 0x66, 0x4a, 0x9b, 0xbe, 0x27, 0x6e, 0x0c, 0xaf, 0x2d, 0x9f, 0xc9, 0xaf, 0x44, 0xeb, 0xf5, 0xf9,
0x0f, 0xfc, 0x91, 0xfb, 0x3e, 0x32, 0x28, 0x17, 0x21, 0xf1, 0x88, 0x43, 0x89, 0xfe, 0x9d, 0xb9, 0x73, 0xcf, 0x1d, 0xdb, 0xef, 0x42, 0x83, 0x74, 0xe1, 0x13, 0x87, 0x58, 0x94, 0xa8, 0xdf, 0xd8,
0xa4, 0x6d, 0xae, 0x3f, 0x0a, 0x94, 0xc1, 0xfa, 0x6e, 0xc0, 0x3f, 0x1d, 0x97, 0x32, 0xac, 0x4c, 0x25, 0x65, 0xb3, 0xdd, 0xb1, 0x27, 0x0d, 0xc6, 0x37, 0x0d, 0xfe, 0xea, 0xda, 0x94, 0x61, 0x69,
0x14, 0x93, 0x4f, 0x13, 0x42, 0x19, 0xda, 0x84, 0x65, 0xcf, 0xbd, 0x72, 0x59, 0xdd, 0xb8, 0x6b, 0xa2, 0x98, 0x7c, 0x9c, 0x12, 0xca, 0xd0, 0x0e, 0x14, 0x1d, 0xfb, 0xc6, 0x66, 0x0d, 0xed, 0x7f,
0xec, 0xe4, 0xb1, 0xda, 0xa0, 0x6d, 0x28, 0x06, 0xa3, 0x11, 0x25, 0xac, 0x9e, 0xe3, 0xc7, 0x15, 0x6d, 0x3f, 0x8f, 0xe5, 0x06, 0xed, 0x41, 0xc9, 0x1b, 0x8f, 0x29, 0x61, 0x8d, 0x1c, 0x3f, 0xae,
0x1c, 0xed, 0xd0, 0x0b, 0x28, 0xd1, 0x20, 0x64, 0xfd, 0x8b, 0x9b, 0x7a, 0x9e, 0x1b, 0xd6, 0x9b, 0xe2, 0x70, 0x87, 0x9e, 0x43, 0x99, 0x7a, 0x3e, 0x1b, 0x5c, 0xdd, 0x36, 0xf2, 0xdc, 0xb0, 0xd5,
0xf7, 0xed, 0xa4, 0x9c, 0x6c, 0xe1, 0xa9, 0xc7, 0x81, 0xb6, 0xf8, 0xec, 0xdf, 0xe0, 0x22, 0x95, 0xba, 0x6b, 0x26, 0xe5, 0x64, 0x06, 0x9e, 0xfa, 0x1c, 0x68, 0x06, 0x9f, 0xc3, 0x5b, 0x5c, 0xa2,
0xbf, 0x82, 0x77, 0xe4, 0x7a, 0x8c, 0x84, 0xf5, 0x82, 0xe2, 0x55, 0x3b, 0x74, 0x04, 0x20, 0x79, 0xe2, 0x37, 0xe0, 0x1d, 0xdb, 0x0e, 0x23, 0x7e, 0xa3, 0x20, 0x79, 0xe5, 0x0e, 0x9d, 0x00, 0x08,
0x83, 0x70, 0xc8, 0x6d, 0xcb, 0x92, 0x7a, 0x27, 0x03, 0xf5, 0x99, 0xc0, 0xe3, 0x0a, 0xd5, 0x4b, 0x5e, 0xcf, 0x1f, 0x71, 0x5b, 0x51, 0x50, 0xef, 0x67, 0xa0, 0xbe, 0x08, 0xf0, 0xb8, 0x4a, 0xd5,
0xeb, 0x1d, 0x94, 0x35, 0xc0, 0x6a, 0x42, 0x51, 0xb9, 0x47, 0x2b, 0x50, 0x3a, 0xef, 0x9e, 0x76, 0xd2, 0x78, 0x0b, 0x15, 0x05, 0x30, 0x5a, 0x50, 0x92, 0xee, 0xd1, 0x3a, 0x94, 0x2f, 0x7b, 0xe7,
0xcf, 0xde, 0x74, 0xab, 0x4b, 0xa8, 0x0c, 0x85, 0x6e, 0xeb, 0x65, 0xbb, 0x6a, 0xa0, 0x0d, 0x58, 0xbd, 0x8b, 0xd7, 0xbd, 0xda, 0x1a, 0xaa, 0x40, 0xa1, 0xd7, 0x7e, 0xd1, 0xa9, 0x69, 0x68, 0x1b,
0xeb, 0xb4, 0x7a, 0xaf, 0xfa, 0xb8, 0xdd, 0x69, 0xb7, 0x7a, 0xed, 0xc3, 0x6a, 0xce, 0xba, 0x03, 0x36, 0xbb, 0xed, 0xfe, 0xcb, 0x01, 0xee, 0x74, 0x3b, 0xed, 0x7e, 0xe7, 0xb8, 0x96, 0x33, 0xfe,
0x95, 0x98, 0x17, 0x95, 0x20, 0xdf, 0xea, 0x1d, 0xa8, 0x2b, 0x87, 0x6d, 0xbe, 0x32, 0xac, 0x2f, 0x83, 0x6a, 0xc4, 0x8b, 0xca, 0x90, 0x6f, 0xf7, 0x8f, 0xe4, 0x95, 0xe3, 0x0e, 0x5f, 0x69, 0xc6,
0x06, 0x6c, 0xce, 0xca, 0x48, 0xc7, 0x81, 0x4f, 0x89, 0xd0, 0x71, 0x10, 0x4c, 0xfc, 0x58, 0x47, 0x67, 0x0d, 0x76, 0xe2, 0x32, 0xd2, 0x89, 0xe7, 0x52, 0x12, 0xe8, 0x38, 0xf4, 0xa6, 0x6e, 0xa4,
0xb9, 0x41, 0x08, 0x0a, 0x3e, 0xf9, 0xac, 0x55, 0x94, 0x6b, 0x81, 0x64, 0x01, 0x73, 0x3c, 0xa9, 0xa3, 0xd8, 0x20, 0x04, 0x05, 0x97, 0x7c, 0x52, 0x2a, 0x8a, 0x75, 0x80, 0x64, 0x1e, 0xb3, 0x1c,
0x20, 0x47, 0xca, 0x0d, 0x7a, 0x06, 0xe5, 0xa8, 0x6a, 0x94, 0x6b, 0x93, 0xdf, 0x59, 0x69, 0x6e, 0xa1, 0x20, 0x47, 0x8a, 0x0d, 0x7a, 0x0a, 0x95, 0xb0, 0x6a, 0x94, 0x6b, 0x93, 0xdf, 0x5f, 0x6f,
0xa9, 0xfc, 0x75, 0x7d, 0x23, 0x8f, 0x38, 0x86, 0x59, 0x7b, 0x50, 0x3b, 0x22, 0x3a, 0x92, 0x1e, 0xed, 0xca, 0xfc, 0x55, 0x7d, 0x43, 0x8f, 0x38, 0x82, 0x19, 0x27, 0x50, 0x3f, 0x21, 0x2a, 0x92,
0x73, 0xd8, 0x24, 0xae, 0xaa, 0xf0, 0xeb, 0x5c, 0x11, 0x19, 0x8c, 0xf0, 0xcb, 0xd7, 0xd6, 0x6b, 0x3e, 0xb3, 0xd8, 0x34, 0xaa, 0x6a, 0xe0, 0xd7, 0xba, 0x21, 0x22, 0x98, 0xc0, 0x2f, 0x5f, 0xa3,
0xa8, 0x2f, 0xc2, 0xa3, 0xe8, 0x13, 0xf0, 0xe8, 0x01, 0x14, 0x44, 0xff, 0xc8, 0xd8, 0x57, 0x9a, 0x06, 0x94, 0x67, 0xc4, 0xa7, 0xb6, 0xe7, 0x8a, 0x70, 0x8a, 0x58, 0x6d, 0x8d, 0x57, 0xd0, 0x58,
0x68, 0x36, 0x9a, 0x13, 0x6e, 0xc1, 0xd2, 0x6e, 0xd9, 0xd3, 0xbc, 0x07, 0x81, 0xcf, 0x88, 0xcf, 0x26, 0x0a, 0xf3, 0x4a, 0x62, 0xba, 0x07, 0x85, 0xe0, 0x65, 0x09, 0x9a, 0xf5, 0x16, 0x8a, 0xc7,
0x7e, 0x17, 0x47, 0x07, 0xfe, 0x4d, 0xc0, 0x47, 0x81, 0x34, 0xa0, 0x14, 0xb9, 0x90, 0x77, 0x52, 0x79, 0xc6, 0x2d, 0x58, 0xd8, 0x8d, 0xd3, 0x79, 0xde, 0x23, 0xcf, 0x65, 0xc4, 0x65, 0xab, 0x45,
0x55, 0xd0, 0x28, 0xeb, 0x1b, 0x2f, 0xc8, 0xf9, 0x78, 0xe8, 0x30, 0xa2, 0x4d, 0xe9, 0xae, 0xd1, 0xd8, 0x85, 0xbf, 0x13, 0x98, 0xc2, 0x10, 0x9b, 0x50, 0x0e, 0x9d, 0x0b, 0xb6, 0x54, 0xe5, 0x14,
0x43, 0x5e, 0x24, 0xf1, 0x9e, 0xa2, 0x9c, 0x36, 0x14, 0xb7, 0x7a, 0x74, 0x07, 0xe2, 0x8b, 0x95, 0xca, 0xf8, 0xca, 0x8b, 0x78, 0x39, 0x19, 0x59, 0x8c, 0x28, 0xd3, 0x2f, 0x82, 0xba, 0xcf, 0x0b,
0x1d, 0xed, 0x42, 0xf1, 0xda, 0xf1, 0x38, 0x8f, 0x2c, 0x52, 0x9c, 0x7d, 0x84, 0x94, 0x8f, 0x11, 0x1b, 0xf4, 0x60, 0x98, 0xed, 0xb6, 0xe4, 0x96, 0x8d, 0x7a, 0x14, 0x7c, 0xb1, 0xb4, 0xa3, 0x03,
0x47, 0x08, 0x54, 0x83, 0xd2, 0x30, 0xbc, 0xe9, 0x87, 0x13, 0x5f, 0x36, 0x75, 0x19, 0x17, 0xf9, 0x28, 0xcd, 0x2c, 0x87, 0xf3, 0x88, 0xc2, 0x46, 0xba, 0x84, 0x48, 0xd1, 0xc0, 0x38, 0x44, 0xa0,
0x16, 0x4f, 0x7c, 0x74, 0x0f, 0xd6, 0x86, 0x2e, 0x75, 0x2e, 0x3c, 0xd2, 0xbf, 0x0c, 0x82, 0x8f, 0x3a, 0x94, 0x47, 0xfe, 0xed, 0xc0, 0x9f, 0xba, 0xa2, 0x11, 0x2a, 0xb8, 0xc4, 0xb7, 0x78, 0xea,
0x54, 0xf6, 0x75, 0x19, 0xaf, 0x46, 0x87, 0xc7, 0xe2, 0xcc, 0x3a, 0x86, 0xad, 0xb9, 0xf0, 0x6f, 0xa2, 0x3b, 0xb0, 0x39, 0xb2, 0xa9, 0x75, 0xe5, 0x90, 0xc1, 0xb5, 0xe7, 0x7d, 0xa0, 0xa2, 0x17,
0xab, 0xc4, 0x0f, 0x03, 0xb6, 0x4e, 0x7c, 0xca, 0x9b, 0xc9, 0x9b, 0x93, 0x22, 0x4e, 0xdb, 0xc8, 0x2a, 0x78, 0x23, 0x3c, 0x3c, 0x0d, 0xce, 0xe6, 0x75, 0x2a, 0xc5, 0x75, 0x3a, 0x85, 0xdd, 0x85,
0x9c, 0x76, 0xee, 0x6f, 0xd2, 0xce, 0xcf, 0xa4, 0xad, 0x85, 0x2f, 0x4c, 0x09, 0x9f, 0x45, 0x0a, 0xc4, 0x56, 0xd5, 0xe8, 0xbb, 0x06, 0xbb, 0x67, 0x2e, 0xe5, 0x4f, 0xd3, 0x59, 0x10, 0x29, 0x12,
0xf4, 0x1f, 0x54, 0x04, 0x98, 0x8e, 0x9d, 0x01, 0xa9, 0x17, 0xe5, 0xed, 0x5f, 0x07, 0xe8, 0x7f, 0x44, 0xcb, 0x2c, 0x48, 0xee, 0x4f, 0x04, 0xc9, 0xc7, 0x04, 0x51, 0x25, 0x29, 0xcc, 0x95, 0x24,
0x80, 0x90, 0x4c, 0x28, 0xe9, 0x4b, 0xf2, 0x92, 0xbc, 0x5f, 0x91, 0x27, 0x5d, 0xd1, 0x55, 0x27, 0x93, 0x48, 0xff, 0x40, 0x35, 0x00, 0xd3, 0x89, 0x35, 0x24, 0x42, 0xa6, 0x2a, 0xfe, 0x79, 0x80,
0xb0, 0x3d, 0x9f, 0xfc, 0x6d, 0x85, 0xc4, 0x50, 0x3b, 0xf7, 0xdd, 0x44, 0x25, 0x93, 0x9a, 0x6a, 0xfe, 0x05, 0xf0, 0xc9, 0x94, 0x92, 0x81, 0x20, 0x2f, 0x8b, 0xfb, 0x55, 0x71, 0xd2, 0xe3, 0x07,
0x21, 0xb7, 0x5c, 0x42, 0x99, 0x4f, 0xa1, 0xbe, 0xc8, 0x79, 0xcb, 0x00, 0x9b, 0x5f, 0x97, 0x61, 0xc6, 0x19, 0xec, 0x2d, 0x26, 0xbf, 0xaa, 0x90, 0x18, 0xea, 0x97, 0xae, 0x9d, 0xa8, 0x64, 0xd2,
0x5d, 0xbf, 0x63, 0x35, 0x1d, 0x91, 0x0b, 0xab, 0xd3, 0x63, 0x09, 0x3d, 0x4a, 0x1f, 0x9e, 0x73, 0x73, 0x5b, 0xca, 0x2d, 0xb7, 0x9c, 0x9b, 0x71, 0x0e, 0x8d, 0x65, 0xce, 0x15, 0x03, 0x6c, 0x7d,
0x7f, 0x01, 0xcc, 0xdd, 0x2c, 0x50, 0x15, 0xaa, 0xb5, 0xf4, 0xd4, 0x40, 0x14, 0xaa, 0xf3, 0x73, 0x29, 0xc2, 0x96, 0xea, 0x7d, 0x39, 0x6b, 0x91, 0x0d, 0x1b, 0xf3, 0x43, 0x0e, 0x3d, 0x48, 0x1f,
0x04, 0xed, 0x25, 0x73, 0xa4, 0x8c, 0x27, 0xd3, 0xce, 0x0a, 0xd7, 0x6e, 0xd1, 0x35, 0x6c, 0x2c, 0xc5, 0x0b, 0x7f, 0x4f, 0xf4, 0x83, 0x2c, 0x50, 0x19, 0xaa, 0xb1, 0xf6, 0x44, 0x43, 0x14, 0x6a,
0x0c, 0x0d, 0xf4, 0x47, 0x9a, 0xd9, 0x69, 0x64, 0x36, 0x32, 0xe3, 0x63, 0xbf, 0x1f, 0x60, 0x6d, 0x8b, 0xb3, 0x07, 0x3d, 0x4e, 0xe6, 0x48, 0x19, 0x76, 0xba, 0x99, 0x15, 0xae, 0xdc, 0xa2, 0x19,
0xe6, 0x79, 0xa2, 0x14, 0xb5, 0x92, 0x46, 0x90, 0xf9, 0x38, 0x13, 0x36, 0xf6, 0x75, 0x05, 0xeb, 0x6c, 0x2f, 0x8d, 0x13, 0xf4, 0x5b, 0x9a, 0xf8, 0x04, 0xd3, 0x9b, 0x99, 0xf1, 0x91, 0xdf, 0xf7,
0xb3, 0x2d, 0x8c, 0x52, 0x08, 0x12, 0x5f, 0xb9, 0xf9, 0x24, 0x1b, 0x38, 0x76, 0xc7, 0xeb, 0x38, 0xb0, 0x19, 0x6b, 0x4f, 0x94, 0xa2, 0x56, 0xd2, 0x70, 0xd2, 0x1f, 0x66, 0xc2, 0x46, 0xbe, 0x6e,
0xdf, 0x92, 0x69, 0x75, 0x4c, 0x79, 0x0e, 0x69, 0x75, 0x4c, 0xeb, 0x74, 0x6b, 0x69, 0x1f, 0xde, 0x60, 0x2b, 0xfe, 0x84, 0x51, 0x0a, 0x41, 0x62, 0x97, 0xeb, 0x8f, 0xb2, 0x81, 0x23, 0x77, 0xbc,
0x96, 0x35, 0xfa, 0xa2, 0x28, 0xff, 0x33, 0x79, 0xfe, 0x33, 0x00, 0x00, 0xff, 0xff, 0x97, 0xd7, 0x8e, 0x8b, 0x4f, 0x32, 0xad, 0x8e, 0x29, 0xed, 0x90, 0x56, 0xc7, 0xb4, 0x97, 0x6e, 0xac, 0x1d,
0x36, 0xd6, 0x33, 0x09, 0x00, 0x00, 0xc2, 0x9b, 0x8a, 0x42, 0x5f, 0x95, 0xc4, 0xff, 0x39, 0xcf, 0x7e, 0x04, 0x00, 0x00, 0xff, 0xff,
0x4c, 0x6b, 0x35, 0xf8, 0x81, 0x09, 0x00, 0x00,
} }

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
kberrs "k8s.io/kubernetes/pkg/api/errors" kberrs "k8s.io/kubernetes/pkg/api/errors"
client "k8s.io/kubernetes/pkg/client/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned"
kblabels "k8s.io/kubernetes/pkg/labels"
) )
// ConfigMapsDriverName is the string name of the driver. // ConfigMapsDriverName is the string name of the driver.
@ -95,6 +96,10 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas
return nil, err return nil, err
} }
if len(list.Items) == 0 {
return nil, ErrReleaseNotFound
}
var results []*rspb.Release var results []*rspb.Release
// iterate over the configmaps object list // iterate over the configmaps object list
@ -112,9 +117,41 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas
return results, nil return results, nil
} }
// Query fetches all releases that match the provided map of labels.
// An error is returned if the configmap fails to retrieve the releases.
func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) {
ls := kblabels.Set{}
for k, v := range labels {
ls[k] = v
}
opts := api.ListOptions{LabelSelector: ls.AsSelector()}
list, err := cfgmaps.impl.List(opts)
if err != nil {
logerrf(err, "query: failed to query with labels")
return nil, err
}
if len(list.Items) == 0 {
return nil, ErrReleaseNotFound
}
var results []*rspb.Release
for _, item := range list.Items {
rls, err := decodeRelease(item.Data["release"])
if err != nil {
logerrf(err, "query: failed to decode release: %s", err)
continue
}
results = append(results, rls)
}
return results, nil
}
// Create creates a new ConfigMap holding the release. If the // Create creates a new ConfigMap holding the release. If the
// ConfigMap already exists, ErrReleaseExists is returned. // ConfigMap already exists, ErrReleaseExists is returned.
func (cfgmaps *ConfigMaps) Create(rls *rspb.Release) error { func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
// set labels for configmaps object meta data // set labels for configmaps object meta data
var lbs labels var lbs labels
@ -122,9 +159,9 @@ func (cfgmaps *ConfigMaps) Create(rls *rspb.Release) error {
lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix()))) lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new configmap to hold the release // create a new configmap to hold the release
obj, err := newConfigMapsObject(rls, lbs) obj, err := newConfigMapsObject(key, rls, lbs)
if err != nil { if err != nil {
logerrf(err, "create: failed to encode release %q", rls.Name) logerrf(err, "create: failed to encode release %q", key)
return err return err
} }
// push the configmap object out into the kubiverse // push the configmap object out into the kubiverse
@ -141,7 +178,7 @@ func (cfgmaps *ConfigMaps) Create(rls *rspb.Release) error {
// Update updates the ConfigMap holding the release. If not found // Update updates the ConfigMap holding the release. If not found
// the ConfigMap is created to hold the release. // the ConfigMap is created to hold the release.
func (cfgmaps *ConfigMaps) Update(rls *rspb.Release) error { func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
// set labels for configmaps object meta data // set labels for configmaps object meta data
var lbs labels var lbs labels
@ -149,9 +186,9 @@ func (cfgmaps *ConfigMaps) Update(rls *rspb.Release) error {
lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix()))) lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new configmap object to hold the release // create a new configmap object to hold the release
obj, err := newConfigMapsObject(rls, lbs) obj, err := newConfigMapsObject(key, rls, lbs)
if err != nil { if err != nil {
logerrf(err, "update: failed to encode release %q", rls.Name) logerrf(err, "update: failed to encode release %q", key)
return err return err
} }
// push the configmap object out into the kubiverse // push the configmap object out into the kubiverse
@ -194,7 +231,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
// "OWNER" - owner of the configmap, currently "TILLER". // "OWNER" - owner of the configmap, currently "TILLER".
// "NAME" - name of the release. // "NAME" - name of the release.
// //
func newConfigMapsObject(rls *rspb.Release, lbs labels) (*api.ConfigMap, error) { func newConfigMapsObject(objkey string, rls *rspb.Release, lbs labels) (*api.ConfigMap, error) {
const owner = "TILLER" const owner = "TILLER"
// encode the release // encode the release
@ -216,7 +253,7 @@ func newConfigMapsObject(rls *rspb.Release, lbs labels) (*api.ConfigMap, error)
// create and return configmap object // create and return configmap object
return &api.ConfigMap{ return &api.ConfigMap{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: rls.Name, Name: objkey,
Labels: lbs.toMap(), Labels: lbs.toMap(),
}, },
Data: map[string]string{"release": s}, Data: map[string]string{"release": s},

@ -37,8 +37,10 @@ func TestConfigMapName(t *testing.T) {
} }
func TestConfigMapGet(t *testing.T) { func TestConfigMapGet(t *testing.T) {
key := "key-1" vers := int32(1)
rel := newTestRelease(key, 1, rspb.Status_DEPLOYED) name := "smug-pigeon"
key := testKey(name, vers)
rel := newTestRelease(name, vers, rspb.Status_DEPLOYED)
cfgmaps := newTestFixture(t, []*rspb.Release{rel}...) cfgmaps := newTestFixture(t, []*rspb.Release{rel}...)
@ -100,14 +102,20 @@ func TestConfigMapList(t *testing.T) {
} }
} }
func TestConfigMapQuery(t *testing.T) {
t.Skip("TestConfigMapQuery")
}
func TestConfigMapCreate(t *testing.T) { func TestConfigMapCreate(t *testing.T) {
cfgmaps := newTestFixture(t) cfgmaps := newTestFixture(t)
key := "key-1" vers := int32(1)
rel := newTestRelease(key, 1, rspb.Status_DEPLOYED) name := "smug-pigeon"
key := testKey(name, vers)
rel := newTestRelease(name, vers, rspb.Status_DEPLOYED)
// store the release in a configmap // store the release in a configmap
if err := cfgmaps.Create(rel); err != nil { if err := cfgmaps.Create(key, rel); err != nil {
t.Fatalf("Failed to create release with key %q: %s", key, err) t.Fatalf("Failed to create release with key %q: %s", key, err)
} }
@ -124,16 +132,18 @@ func TestConfigMapCreate(t *testing.T) {
} }
func TestConfigMapUpdate(t *testing.T) { func TestConfigMapUpdate(t *testing.T) {
key := "key-1" vers := int32(1)
rel := newTestRelease(key, 1, rspb.Status_DEPLOYED) name := "smug-pigeon"
key := testKey(name, vers)
rel := newTestRelease(name, vers, rspb.Status_DEPLOYED)
cfgmaps := newTestFixture(t, []*rspb.Release{rel}...) cfgmaps := newTestFixture(t, []*rspb.Release{rel}...)
// modify release status code & version // modify release status code
rel = newTestRelease(key, 2, rspb.Status_SUPERSEDED) rel.Info.Status.Code = rspb.Status_SUPERSEDED
// perform the update // perform the update
if err := cfgmaps.Update(rel); err != nil { if err := cfgmaps.Update(key, rel); err != nil {
t.Fatalf("Failed to update release: %s", err) t.Fatalf("Failed to update release: %s", err)
} }
@ -144,11 +154,8 @@ func TestConfigMapUpdate(t *testing.T) {
} }
// check release has actually been updated by comparing modified fields // check release has actually been updated by comparing modified fields
switch { if rel.Info.Status.Code != got.Info.Status.Code {
case rel.Info.Status.Code != got.Info.Status.Code:
t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code) t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code)
case rel.Version != got.Version:
t.Errorf("Expected version %d, got version %d", rel.Version, got.Version)
} }
} }
@ -177,11 +184,13 @@ func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Releas
mock.objects = map[string]*api.ConfigMap{} mock.objects = map[string]*api.ConfigMap{}
for _, rls := range releases { for _, rls := range releases {
cfgmap, err := newConfigMapsObject(rls, nil) objkey := testKey(rls.Name, rls.Version)
cfgmap, err := newConfigMapsObject(objkey, rls, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create configmap: %s", err) t.Fatalf("Failed to create configmap: %s", err)
} }
mock.objects[rls.Name] = cfgmap mock.objects[objkey] = cfgmap
} }
} }

@ -34,7 +34,7 @@ var (
// Create stores the release or returns ErrReleaseExists // Create stores the release or returns ErrReleaseExists
// if an identical release already exists. // if an identical release already exists.
type Creator interface { type Creator interface {
Create(rls *rspb.Release) error Create(key string, rls *rspb.Release) error
} }
// Updator is the interface that wraps the Update method. // Updator is the interface that wraps the Update method.
@ -42,7 +42,7 @@ type Creator interface {
// Update updates an existing release or returns // Update updates an existing release or returns
// ErrReleaseNotFound if the release does not exist. // ErrReleaseNotFound if the release does not exist.
type Updator interface { type Updator interface {
Update(rls *rspb.Release) error Update(key string, rls *rspb.Release) error
} }
// Deletor is the interface that wraps the Delete method. // Deletor is the interface that wraps the Delete method.
@ -53,15 +53,18 @@ type Deletor interface {
Delete(key string) (*rspb.Release, error) Delete(key string) (*rspb.Release, error)
} }
// Queryor is the interface that wraps the Get and List methods. // Queryor is the interface that wraps the Get, List, and Query methods.
// //
// Get returns the release named by key or returns ErrReleaseNotFound // Get returns the release named by key or returns ErrReleaseNotFound
// if the release does not exist. // if the release does not exist.
// //
// List returns the set of all releases that satisfy the filter predicate. // List returns the set of all releases that satisfy the filter predicate.
//
// Query returns the set of all releases that match the provided set of labels.
type Queryor interface { type Queryor interface {
Get(key string) (*rspb.Release, error) Get(key string) (*rspb.Release, error)
List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error)
Query(labels map[string]string) ([]*rspb.Release, error)
} }
// Driver is the interface composed of Creator, Updator, Deletor, Queryor // Driver is the interface composed of Creator, Updator, Deletor, Queryor

@ -17,6 +17,7 @@ limitations under the License.
package driver // import "k8s.io/helm/pkg/storage/driver" package driver // import "k8s.io/helm/pkg/storage/driver"
import ( import (
"fmt"
"sync" "sync"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
@ -64,23 +65,28 @@ func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error
return releases, nil return releases, nil
} }
// Query does not apply to in-memory storage.
func (mem *Memory) Query(_ map[string]string) ([]*rspb.Release, error) {
return nil, fmt.Errorf("memory: cannot apply query by labels to in-memory storage")
}
// Create creates a new release or returns ErrReleaseExists. // Create creates a new release or returns ErrReleaseExists.
func (mem *Memory) Create(rls *rspb.Release) error { func (mem *Memory) Create(key string, rls *rspb.Release) error {
defer unlock(mem.wlock()) defer unlock(mem.wlock())
if _, ok := mem.cache[rls.Name]; ok { if _, ok := mem.cache[key]; ok {
return ErrReleaseExists return ErrReleaseExists
} }
mem.cache[rls.Name] = rls mem.cache[key] = rls
return nil return nil
} }
// Update updates a release or returns ErrReleaseNotFound. // Update updates a release or returns ErrReleaseNotFound.
func (mem *Memory) Update(rls *rspb.Release) error { func (mem *Memory) Update(key string, rls *rspb.Release) error {
defer unlock(mem.wlock()) defer unlock(mem.wlock())
if _, ok := mem.cache[rls.Name]; ok { if _, ok := mem.cache[key]; ok {
mem.cache[rls.Name] = rls mem.cache[key] = rls
return nil return nil
} }
return ErrReleaseNotFound return ErrReleaseNotFound

@ -17,6 +17,7 @@ limitations under the License.
package driver // import "k8s.io/helm/pkg/storage/driver" package driver // import "k8s.io/helm/pkg/storage/driver"
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
@ -33,11 +34,14 @@ func TestMemoryName(t *testing.T) {
} }
func TestMemoryGet(t *testing.T) { func TestMemoryGet(t *testing.T) {
key := "test-1" vers := int32(1)
rls := &rspb.Release{Name: key} name := "smug-pigeon"
key := testKey(name, vers)
rls := &rspb.Release{Name: name, Version: vers}
mem := NewMemory() mem := NewMemory()
if err := mem.Create(rls); err != nil { if err := mem.Create(key, rls); err != nil {
t.Fatalf("Failed create: %s", err) t.Fatalf("Failed create: %s", err)
} }
@ -45,46 +49,76 @@ func TestMemoryGet(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Could not get %s: %s", key, err) t.Errorf("Could not get %s: %s", key, err)
} }
if res.Name != key { if res.Name != name {
t.Errorf("Expected %s, got %s", key, res.Name) t.Errorf("Expected name %s, got name %s", key, res.Name)
}
if res.Version != vers {
t.Errorf("Expected version %d, got version %d", vers, res.Version)
} }
} }
func TestMemoryCreate(t *testing.T) { func TestMemoryCreate(t *testing.T) {
key := "test-1" vers := int32(1)
rls := &rspb.Release{Name: key} name := "smug-pigeon"
key := testKey(name, vers)
rls := &rspb.Release{Name: name, Version: vers}
mem := NewMemory() mem := NewMemory()
if err := mem.Create(rls); err != nil { if err := mem.Create(key, rls); err != nil {
t.Fatalf("Failed created: %s", err) t.Fatalf("Failed created: %s", err)
} }
if mem.cache[key].Name != key { if mem.cache[key].Name != name {
t.Errorf("Unexpected release name: %s", mem.cache[key].Name) t.Errorf("Unexpected release name: %s", mem.cache[key].Name)
} }
if mem.cache[key].Version != vers {
t.Errorf("Unexpected release version: %d", mem.cache[key].Version)
}
} }
func TestMemoryUpdate(t *testing.T) { func TestMemoryUpdate(t *testing.T) {
key := "test-1" vers := int32(1)
rls := &rspb.Release{Name: key} name := "smug-pigeon"
key := testKey(name, vers)
rls := &rspb.Release{
Name: name,
Version: vers,
Info: &rspb.Info{Status: &rspb.Status{Code: rspb.Status_DEPLOYED}},
}
mem := NewMemory() mem := NewMemory()
if err := mem.Create(rls); err != nil { if err := mem.Create(key, rls); err != nil {
t.Fatalf("Failed create: %s", err) t.Fatalf("Failed create: %s", err)
} }
if err := mem.Update(rls); err != nil {
// make an update to the release
rls.Info.Status.Code = rspb.Status_DELETED
if err := mem.Update(key, rls); err != nil {
t.Fatalf("Failed update: %s", err) t.Fatalf("Failed update: %s", err)
} }
if mem.cache[key].Name != key {
if mem.cache[key].Name != name {
t.Errorf("Unexpected release name: %s", mem.cache[key].Name) t.Errorf("Unexpected release name: %s", mem.cache[key].Name)
} }
if mem.cache[key].Version != vers {
t.Errorf("Unexpected release version: %d", mem.cache[key].Version)
}
if mem.cache[key].Info.Status.Code != rspb.Status_DELETED {
t.Errorf("Unexpected status code: %s", mem.cache[key].Info.Status.Code)
}
} }
func TestMemoryDelete(t *testing.T) { func TestMemoryDelete(t *testing.T) {
key := "test-1" vers := int32(1)
rls := &rspb.Release{Name: key} name := "smug-pigeon"
key := testKey(name, vers)
rls := &rspb.Release{Name: key, Version: vers}
mem := NewMemory() mem := NewMemory()
if err := mem.Create(rls); err != nil { if err := mem.Create(key, rls); err != nil {
t.Fatalf("Failed create: %s", err) t.Fatalf("Failed create: %s", err)
} }
@ -99,3 +133,7 @@ func TestMemoryDelete(t *testing.T) {
t.Errorf("Expected %s, got %s", rls, res) t.Errorf("Expected %s, got %s", rls, res)
} }
} }
func testKey(name string, version int32) string {
return fmt.Sprintf("%s.v%d", name, version)
}

@ -17,6 +17,7 @@ limitations under the License.
package storage // import "k8s.io/helm/pkg/storage" package storage // import "k8s.io/helm/pkg/storage"
import ( import (
"fmt"
"log" "log"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
@ -30,34 +31,51 @@ type Storage struct {
// Get retrieves the release from storage. An error is returned // Get retrieves the release from storage. An error is returned
// if the storage driver failed to fetch the release, or the // if the storage driver failed to fetch the release, or the
// release identified by key does not exist. // release identified by the key, version pair does not exist.
func (s *Storage) Get(key string) (*rspb.Release, error) { func (s *Storage) Get(name string, version int32) (*rspb.Release, error) {
log.Printf("Getting release %q from storage\n", key) log.Printf("Getting release %q (v%d) from storage\n", name, version)
return s.Driver.Get(key) // an unspecified version implies latest so we need to select from the
// set of releases any such that NAME == "name" and STATUS == "DEPLOYED"
if version == 0 {
ls, err := s.Driver.Query(map[string]string{
"NAME": name,
"STATUS": "DEPLOYED",
})
switch {
case err != nil:
return nil, err
case len(ls) == 0:
return nil, fmt.Errorf("bad query")
default:
return ls[0], nil
}
}
return s.Driver.Get(makeKey(name, version))
} }
// Create creates a new storage entry holding the release. An // Create creates a new storage entry holding the release. An
// error is returned if the storage driver failed to store the // error is returned if the storage driver failed to store the
// release, or a release with identical an key already exists. // release, or a release with identical an key already exists.
func (s *Storage) Create(rls *rspb.Release) error { func (s *Storage) Create(rls *rspb.Release) error {
log.Printf("Create release %q in storage\n", rls.Name) log.Printf("Create release %q (v%d) in storage\n", rls.Name, rls.Version)
return s.Driver.Create(rls) return s.Driver.Create(makeKey(rls.Name, rls.Version), rls)
} }
// Update update the release in storage. An error is returned if the // Update update the release in storage. An error is returned if the
// storage backend fails to update the release or if the release // storage backend fails to update the release or if the release
// does not exist. // does not exist.
func (s *Storage) Update(rls *rspb.Release) error { func (s *Storage) Update(rls *rspb.Release) error {
log.Printf("Updating %q in storage\n", rls.Name) log.Printf("Updating %q (v%d) in storage\n", rls.Name, rls.Version)
return s.Driver.Update(rls) return s.Driver.Update(makeKey(rls.Name, rls.Version), rls)
} }
// Delete deletes the release from storage. An error is returned if // Delete deletes the release from storage. An error is returned if
// the storage backend fails to delete the release or if the release // the storage backend fails to delete the release or if the release
// does not exist. // does not exist.
func (s *Storage) Delete(key string) (*rspb.Release, error) { func (s *Storage) Delete(name string, version int32) (*rspb.Release, error) {
log.Printf("Deleting release %q from storage\n", key) log.Printf("Deleting release %q (v%d) from storage\n", name, version)
return s.Driver.Delete(key) return s.Driver.Delete(makeKey(name, version))
} }
// ListReleases returns all releases from storage. An error is returned if the // ListReleases returns all releases from storage. An error is returned if the
@ -114,3 +132,10 @@ func Init(d driver.Driver) *Storage {
} }
return &Storage{Driver: d} return &Storage{Driver: d}
} }
// 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.
func makeKey(rlsname string, version int32) string {
return fmt.Sprintf("%s.v%d", rlsname, version)
}

@ -30,11 +30,12 @@ func TestStorageCreate(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease() rls := ReleaseTestData{Name: "angry-beaver", Version: 1}.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// fetch the release // fetch the release
res, err := storage.Get(rls.Name) res, err := storage.Get(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "QueryRelease") assertErrNil(t.Fatal, err, "QueryRelease")
// verify the fetched and created release are the same // verify the fetched and created release are the same
@ -48,16 +49,16 @@ func TestStorageUpdate(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease() rls := ReleaseTestData{Name: "angry-beaver", Version: 1}.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// modify the release // modify the release
rls.Version = 2
rls.Manifest = "new-manifest" rls.Manifest = "new-manifest"
assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease") assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease")
// retrieve the updated release // retrieve the updated release
res, err := storage.Get(rls.Name) res, err := storage.Get(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "QueryRelease") assertErrNil(t.Fatal, err, "QueryRelease")
// verify updated and fetched releases are the same. // verify updated and fetched releases are the same.
@ -71,11 +72,11 @@ func TestStorageDelete(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease() rls := ReleaseTestData{Name: "angry-beaver", Version: 1}.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// delete the release // delete the release
res, err := storage.Delete(rls.Name) res, err := storage.Delete(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "DeleteRelease") assertErrNil(t.Fatal, err, "DeleteRelease")
// verify updated and fetched releases are the same. // verify updated and fetched releases are the same.

Loading…
Cancel
Save