Commit c6680231 authored by Pål Karlsrud's avatar Pål Karlsrud
Browse files

When managing releases, use a kubernetes client that is authenticated as

the user who performs the action.
parent 73f520f2
......@@ -24,6 +24,7 @@ import (
"github.com/paalka/helm/pkg/chartutil"
"github.com/paalka/helm/pkg/hooks"
"github.com/paalka/helm/pkg/kube"
"github.com/paalka/helm/pkg/proto/hapi/release"
"github.com/paalka/helm/pkg/proto/hapi/services"
relutil "github.com/paalka/helm/pkg/releaseutil"
......@@ -33,6 +34,7 @@ import (
// InstallRelease installs a release and stores the release record.
func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
s.Log("preparing install for %s", req.Name)
rel, err := s.prepareRelease(req)
if err != nil {
s.Log("failed install prepare step: %s", err)
......@@ -47,8 +49,13 @@ func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
}
rel.Info.Username = getUserName(c)
usrCli, err := getUserClient(c)
if err != nil {
return nil, err
}
s.Log("performing install for %s", req.Name)
res, err := s.performRelease(rel, req)
res, err := s.performRelease(rel, req, usrCli)
if err != nil {
s.Log("failed install perform step: %s", err)
}
......@@ -133,7 +140,7 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
}
// performRelease runs a release.
func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest, usrCli *kube.Client) (*services.InstallReleaseResponse, error) {
res := &services.InstallReleaseResponse{Release: r}
if req.DryRun {
......@@ -144,7 +151,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// pre-install hooks
if !req.DisableHooks {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout, usrCli); err != nil {
return res, err
}
} else {
......@@ -174,7 +181,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
Timeout: req.Timeout,
}
s.recordRelease(r, false)
if err := s.ReleaseModule.Update(old, r, updateReq, s.env); err != nil {
if err := s.ReleaseModule.Update(old, r, updateReq, usrCli); err != nil {
msg := fmt.Sprintf("Release replace %q failed: %s", r.Name, err)
s.Log("warning: %s", msg)
old.Info.Status.Code = release.Status_SUPERSEDED
......@@ -189,7 +196,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// nothing to replace, create as normal
// regular manifests
s.recordRelease(r, false)
if err := s.ReleaseModule.Create(r, req, s.env); err != nil {
if err := s.ReleaseModule.Create(r, req, usrCli); err != nil {
msg := fmt.Sprintf("Release %q failed: %s", r.Name, err)
s.Log("warning: %s", msg)
r.Info.Status.Code = release.Status_FAILED
......@@ -201,7 +208,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// post-install hooks
if !req.DisableHooks {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout, usrCli); err != nil {
msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err)
s.Log("warning: %s", msg)
r.Info.Status.Code = release.Status_FAILED
......
......@@ -37,11 +37,11 @@ import (
// ReleaseModule is an interface that allows ReleaseServer to run operations on release via either local implementation or Rudder service
type ReleaseModule interface {
Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error
Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error
Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error
Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error)
Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error)
Create(r *release.Release, req *services.InstallReleaseRequest, client *kube.Client) error
Update(current, target *release.Release, req *services.UpdateReleaseRequest, client *kube.Client) error
Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, client *kube.Client) error
Status(r *release.Release, req *services.GetReleaseStatusRequest, client *kube.Client) (string, error)
Delete(r *release.Release, req *services.UninstallReleaseRequest, client *kube.Client) (string, []error)
}
// LocalReleaseModule is a local implementation of ReleaseModule
......@@ -49,52 +49,52 @@ type LocalReleaseModule struct {
clientset internalclientset.Interface
}
// Create creates a release via kubeclient from provided environment
func (m *LocalReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error {
// Create creates a release via the provided kubeclient
func (m *LocalReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, client *kube.Client) error {
b := bytes.NewBufferString(r.Manifest)
return env.KubeClient.Create(r.Namespace, b, req.Timeout, req.Wait)
return client.Create(r.Namespace, b, req.Timeout, req.Wait)
}
// Update performs an update from current to target release
func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error {
func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, client *kube.Client) error {
c := bytes.NewBufferString(current.Manifest)
t := bytes.NewBufferString(target.Manifest)
return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait)
return client.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait)
}
// Rollback performs a rollback from current to target release
func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error {
func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, client *kube.Client) error {
c := bytes.NewBufferString(current.Manifest)
t := bytes.NewBufferString(target.Manifest)
return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait)
return client.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait)
}
// Status returns kubectl-like formatted status of release objects
func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) {
return env.KubeClient.Get(r.Namespace, bytes.NewBufferString(r.Manifest))
func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, client *kube.Client) (string, error) {
return client.Get(r.Namespace, bytes.NewBufferString(r.Manifest))
}
// Delete deletes the release and returns manifests that were kept in the deletion process
func (m *LocalReleaseModule) Delete(rel *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (kept string, errs []error) {
func (m *LocalReleaseModule) Delete(rel *release.Release, req *services.UninstallReleaseRequest, client *kube.Client) (kept string, errs []error) {
vs, err := GetVersionSet(m.clientset.Discovery())
if err != nil {
return rel.Manifest, []error{fmt.Errorf("Could not get apiVersions from Kubernetes: %v", err)}
}
return DeleteRelease(rel, vs, env.KubeClient)
return DeleteRelease(rel, vs, client)
}
// RemoteReleaseModule is a ReleaseModule which calls Rudder service to operate on a release
type RemoteReleaseModule struct{}
// Create calls rudder.InstallRelease
func (m *RemoteReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error {
func (m *RemoteReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, client *kube.Client) error {
request := &rudderAPI.InstallReleaseRequest{Release: r}
_, err := rudder.InstallRelease(request)
return err
}
// Update calls rudder.UpgradeRelease
func (m *RemoteReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error {
func (m *RemoteReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, client *kube.Client) error {
upgrade := &rudderAPI.UpgradeReleaseRequest{
Current: current,
Target: target,
......@@ -108,7 +108,7 @@ func (m *RemoteReleaseModule) Update(current, target *release.Release, req *serv
}
// Rollback calls rudder.Rollback
func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error {
func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, client *kube.Client) error {
rollback := &rudderAPI.RollbackReleaseRequest{
Current: current,
Target: target,
......@@ -121,7 +121,7 @@ func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *se
}
// Status returns status retrieved from rudder.ReleaseStatus
func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) {
func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, client *kube.Client) (string, error) {
statusRequest := &rudderAPI.ReleaseStatusRequest{Release: r}
resp, err := rudder.ReleaseStatus(statusRequest)
if resp == nil {
......@@ -131,7 +131,7 @@ func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleas
}
// Delete calls rudder.DeleteRelease
func (m *RemoteReleaseModule) Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error) {
func (m *RemoteReleaseModule) Delete(r *release.Release, req *services.UninstallReleaseRequest, client *kube.Client) (string, []error) {
deleteRequest := &rudderAPI.DeleteReleaseRequest{Release: r}
resp, err := rudder.DeleteRelease(deleteRequest)
......
......@@ -22,6 +22,7 @@ import (
ctx "golang.org/x/net/context"
"github.com/paalka/helm/pkg/hooks"
"github.com/paalka/helm/pkg/kube"
"github.com/paalka/helm/pkg/proto/hapi/release"
"github.com/paalka/helm/pkg/proto/hapi/services"
"github.com/paalka/helm/pkg/timeconv"
......@@ -42,8 +43,14 @@ func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackRel
return nil, err
}
}
usrCli, err := getUserClient(c)
if err != nil {
return nil, err
}
s.Log("performing rollback of %s", req.Name)
res, err := s.performRollback(currentRelease, targetRelease, req)
res, err := s.performRollback(currentRelease, targetRelease, req, usrCli)
if err != nil {
return res, err
}
......@@ -112,7 +119,7 @@ func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*
return crls, target, nil
}
func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) {
func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest, usrCli *kube.Client) (*services.RollbackReleaseResponse, error) {
res := &services.RollbackReleaseResponse{Release: targetRelease}
if req.DryRun {
......@@ -122,14 +129,14 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R
// pre-rollback hooks
if !req.DisableHooks {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout); err != nil {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout, usrCli); err != nil {
return res, err
}
} else {
s.Log("rollback hooks disabled for %s", req.Name)
}
if err := s.ReleaseModule.Rollback(currentRelease, targetRelease, req, s.env); err != nil {
if err := s.ReleaseModule.Rollback(currentRelease, targetRelease, req, usrCli); err != nil {
msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)
s.Log("warning: %s", msg)
currentRelease.Info.Status.Code = release.Status_SUPERSEDED
......@@ -142,7 +149,7 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R
// post-rollback hooks
if !req.DisableHooks {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout); err != nil {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout, usrCli); err != nil {
return res, err
}
}
......
......@@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"github.com/paalka/helm/pkg/chartutil"
"github.com/paalka/helm/pkg/kube"
"github.com/paalka/helm/pkg/proto/hapi/chart"
"github.com/paalka/helm/pkg/proto/hapi/release"
"github.com/paalka/helm/pkg/proto/hapi/services"
......@@ -316,8 +317,7 @@ func (s *ReleaseServer) recordRelease(r *release.Release, reuse bool) {
}
}
func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook string, timeout int64) error {
kubeCli := s.env.KubeClient
func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook string, timeout int64, kubeCli *kube.Client) error {
code, ok := events[hook]
if !ok {
return fmt.Errorf("unknown hook %s", hook)
......
......@@ -62,9 +62,14 @@ func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
Info: rel.Info,
}
usrCli, err := getUserClient(c)
if err != nil {
return nil, err
}
// Ok, we got the status of the release as we had jotted down, now we need to match the
// manifest we stashed away with reality from the cluster.
resp, err := s.ReleaseModule.Status(rel, req, s.env)
resp, err := s.ReleaseModule.Status(rel, req, usrCli)
if sc == release.Status_DELETED || sc == release.Status_FAILED {
// Skip errors if this is already deleted or failed.
return statusResp, nil
......
......@@ -67,8 +67,12 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
rel.Info.Description = "Deletion in progress (or silently failed)"
res := &services.UninstallReleaseResponse{Release: rel}
usrCli, err := getUserClient(c)
if err != nil {
return nil, err
}
if !req.DisableHooks {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PreDelete, req.Timeout); err != nil {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PreDelete, req.Timeout, usrCli); err != nil {
return res, err
}
} else {
......@@ -81,7 +85,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
s.Log("uninstall: Failed to store updated release: %s", err)
}
kept, errs := s.ReleaseModule.Delete(rel, req, s.env)
kept, errs := s.ReleaseModule.Delete(rel, req, usrCli)
res.Info = kept
es := make([]string, 0, len(errs))
......@@ -91,7 +95,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
}
if !req.DisableHooks {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PostDelete, req.Timeout); err != nil {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PostDelete, req.Timeout, usrCli); err != nil {
es = append(es, err.Error())
}
}
......
......@@ -23,6 +23,7 @@ import (
"github.com/paalka/helm/pkg/chartutil"
"github.com/paalka/helm/pkg/hooks"
"github.com/paalka/helm/pkg/kube"
"github.com/paalka/helm/pkg/proto/hapi/release"
"github.com/paalka/helm/pkg/proto/hapi/services"
"github.com/paalka/helm/pkg/timeconv"
......@@ -48,8 +49,13 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
}
}
usrCli, err := getUserClient(c)
if err != nil {
return nil, err
}
s.Log("performing update for %s", req.Name)
res, err := s.performUpdate(currentRelease, updatedRelease, req)
res, err := s.performUpdate(currentRelease, updatedRelease, req, usrCli)
if err != nil {
return res, err
}
......@@ -132,7 +138,7 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
return currentRelease, updatedRelease, err
}
func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest, usrCli *kube.Client) (*services.UpdateReleaseResponse, error) {
res := &services.UpdateReleaseResponse{Release: updatedRelease}
if req.DryRun {
......@@ -143,13 +149,13 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
// pre-upgrade hooks
if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout, usrCli); err != nil {
return res, err
}
} else {
s.Log("update hooks disabled for %s", req.Name)
}
if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, s.env); err != nil {
if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, usrCli); err != nil {
msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err)
s.Log("warning: %s", msg)
originalRelease.Info.Status.Code = release.Status_SUPERSEDED
......@@ -162,7 +168,7 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
// post-upgrade hooks
if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout, usrCli); err != nil {
return res, err
}
}
......
......@@ -191,7 +191,7 @@ func checkBearerAuth(c context.Context, h string, sysCli *kube.Client) (context.
Prefix: syscfg.Prefix,
BearerToken: token,
}
usrcfg.TLSClientConfig.CertData = syscfg.TLSClientConfig.CertData
usrcfg.TLSClientConfig = syscfg.TLSClientConfig
c = context.WithValue(c, kube.UserInfo, &result.Status.User)
c = context.WithValue(c, kube.UserClient, kube.New(&wrapClientConfig{cfg: usrcfg}))
......@@ -318,6 +318,14 @@ func (wrapClientConfig) ConfigAccess() clientcmd.ConfigAccess {
return clientcmd.NewDefaultClientConfigLoadingRules()
}
func getUserClient(c context.Context) (*kube.Client, error) {
usrCli, ok := c.Value(kube.UserClient).(*kube.Client)
if !ok {
return nil, fmt.Errorf("Unable to obtain user client")
}
return usrCli, nil
}
func getUserName(c context.Context) string {
user := c.Value(kube.UserInfo)
if user == nil {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment