From 6f06945db482f74ee821daaf8e7a77dde7446d4c Mon Sep 17 00:00:00 2001 From: saumanbiswas Date: Thu, 9 Feb 2017 22:10:12 +0600 Subject: [PATCH 1/8] Pass user authentication to Tiller (#4) --- pkg/helm/client.go | 1 - pkg/helm/option.go | 98 ++++++++++++++++- pkg/helm/types.go | 13 +++ pkg/tiller/server.go | 252 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 pkg/helm/types.go diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 03df61c6d..3d22a8613 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -86,7 +86,6 @@ func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... req.DisableHooks = h.opts.disableHooks req.ReuseName = h.opts.reuseName ctx := NewContext() - if h.opts.before != nil { if err := h.opts.before(ctx, req); err != nil { return nil, err diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 3853133ef..c2b077ea7 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -17,14 +17,20 @@ limitations under the License. package helm import ( + "bytes" + "encoding/base64" + "io/ioutil" + "log" + "github.com/golang/protobuf/proto" + "github.com/spf13/pflag" "golang.org/x/net/context" "google.golang.org/grpc/metadata" - cpb "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" rls "k8s.io/helm/pkg/proto/hapi/services" "k8s.io/helm/pkg/version" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) // Option allows specifying various settings configurable by @@ -383,10 +389,94 @@ func WithMaxHistory(max int32) HistoryOption { } } -// NewContext creates a versioned context. +// NewContext creates a versioned context with kubernetes client data. func NewContext() context.Context { - md := metadata.Pairs("x-helm-api-client", version.Version) - return metadata.NewContext(context.TODO(), md) + return metadata.NewContext( + context.TODO(), + metadata.Join( + metadata.New( + extractKubeConfig()), + metadata.New(map[string]string{ + "x-helm-api-client": version.Version, + }), + ), + ) +} + +func extractKubeConfig() map[string]string { + configData := make(map[string]string) + clientConfig := cmdutil.DefaultClientConfig(pflag.NewFlagSet("", pflag.ContinueOnError)) + c, err := clientConfig.ClientConfig() + if err != nil { + log.Println("Failed to extract kubeconfig") + return configData + } + + // Kube APIServer URL + if len(c.Host) != 0 { + configData[K8sServer] = c.Host + } + + if c.AuthProvider != nil { + switch c.AuthProvider.Name { + case "gcp": + configData[Authorization] = "Bearer " + c.AuthProvider.Config["access_token"] + case "oidc": + configData[Authorization] = "Bearer " + c.AuthProvider.Config["id-token"] + default: + panic("Unknown auth provider: " + c.AuthProvider.Name) + } + } + + if len(c.BearerToken) != 0 { + configData[Authorization] = "Bearer " + c.BearerToken + } + + if len(c.Username) != 0 && len(c.Password) != 0 { + configData[Authorization] = "Basic " + base64.StdEncoding.EncodeToString([]byte(c.Username+":"+c.Password)) + } + + if len(string(c.CAData)) != 0 { + configData[K8sCertificateAuthority] = base64.StdEncoding.EncodeToString(bytes.TrimSpace(c.CAData)) + } + + if len(string(c.TLSClientConfig.KeyData)) != 0 { + configData[K8sClientKey] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.KeyData) + } + + if len(string(c.TLSClientConfig.CertData)) != 0 { + configData[K8sClientCertificate] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.CertData) + } + + if len(c.TLSClientConfig.CAFile) != 0 { + b, err := ioutil.ReadFile(c.TLSClientConfig.CAFile) + if err != nil { + log.Println(err) + } else { + configData[K8sCertificateAuthority] = base64.StdEncoding.EncodeToString(b) + } + } + + if len(c.TLSClientConfig.CertFile) != 0 { + b, err := ioutil.ReadFile(c.TLSClientConfig.CertFile) + if err != nil { + log.Println(err) + } else { + configData[K8sClientCertificate] = base64.StdEncoding.EncodeToString(b) + } + } + + if len(c.TLSClientConfig.KeyFile) != 0 { + if len(c.TLSClientConfig.KeyFile) != 0 { + b, err := ioutil.ReadFile(c.TLSClientConfig.KeyFile) + if err != nil { + log.Println(err) + } else { + configData[K8sClientKey] = base64.StdEncoding.EncodeToString(b) + } + } + } + return configData } // ReleaseTestOption allows configuring optional request data for diff --git a/pkg/helm/types.go b/pkg/helm/types.go new file mode 100644 index 000000000..b13d18166 --- /dev/null +++ b/pkg/helm/types.go @@ -0,0 +1,13 @@ +package helm + +const ( + Authorization = "authorization" + K8sServer = "k8s-server" + K8sClientCertificate = "k8s-client-certificate" + K8sCertificateAuthority = "k8s-certificate-authority" + K8sClientKey = "k8s-client-key" + + // Generated from input keys above + K8sUser = "k8s-user" + K8sConfig = "k8s-client-config" +) diff --git a/pkg/tiller/server.go b/pkg/tiller/server.go index 6cecda70f..c28d67a8c 100644 --- a/pkg/tiller/server.go +++ b/pkg/tiller/server.go @@ -17,6 +17,10 @@ limitations under the License. package tiller import ( + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" "fmt" "log" "strings" @@ -24,8 +28,13 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" - + "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/version" + authenticationapi "k8s.io/kubernetes/pkg/apis/authentication" + clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + rest "k8s.io/kubernetes/pkg/client/restclient" + "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" ) // maxMsgSize use 10MB as the default message size limit. @@ -41,25 +50,75 @@ func NewServer() *grpc.Server { ) } +func authenticate(ctx context.Context) (context.Context, error) { + md, ok := metadata.FromContext(ctx) + if !ok { + return nil, errors.New("Missing metadata in context.") + } + + var user *authenticationapi.UserInfo + var kubeConfig *rest.Config + var err error + authHeader, ok := md[helm.Authorization] + if !ok || authHeader[0] == "" { + user, kubeConfig, err = checkClientCert(ctx) + } else { + if strings.HasPrefix(authHeader[0], "Bearer ") { + user, kubeConfig, err = checkBearerAuth(ctx) + } else if strings.HasPrefix(authHeader[0], "Basic ") { + user, kubeConfig, err = checkBasicAuth(ctx) + } else { + return nil, errors.New("Unknown authorization scheme.") + } + } + if err != nil { + return nil, err + } + ctx = context.WithValue(ctx, helm.K8sUser, user) + ctx = context.WithValue(ctx, helm.K8sConfig, kubeConfig) + + // TODO: Remove + if user == nil { + log.Println("user not found in context") + } else { + log.Println("authenticated user:", user) + } + return ctx, nil +} + func newUnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { - if err := checkClientVersion(ctx); err != nil { + err = checkClientVersion(ctx) + if err != nil { // whitelist GetVersion() from the version check if _, m := splitMethod(info.FullMethod); m != "GetVersion" { log.Println(err) return nil, err } } + ctx, err = authenticate(ctx) + if err != nil { + log.Println(err) + return nil, err + } return handler(ctx, req) } } func newStreamInterceptor() grpc.StreamServerInterceptor { return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { - if err := checkClientVersion(ss.Context()); err != nil { + ctx := ss.Context() + err := checkClientVersion(ctx) + if err != nil { + log.Println(err) + return err + } + ctx, err = authenticate(ctx) + if err != nil { log.Println(err) return err } + // TODO: How to pass modified ctx? return handler(srv, ss) } } @@ -87,3 +146,190 @@ func checkClientVersion(ctx context.Context) error { } return nil } + +func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) { + md, _ := metadata.FromContext(ctx) + token := md[helm.Authorization][0][len("Bearer "):] + + apiServer, err := getServerURL(md) + if err != nil { + return nil, nil, err + } + caCert, _ := getCertificateAuthority(md) + + // TODO: Should be InClusterConfig() ? + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{DefaultClientConfig: &clientcmd.DefaultClientConfig}, + &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{ + Server: apiServer, + CertificateAuthorityData: caCert, + }}).ClientConfig() + if err != nil { + return nil, nil, err + } + + client, err := clientset.NewForConfig(kubeConfig) + if err != nil { + return nil, nil, err + } + + // verify token + tokenReq := &authenticationapi.TokenReview{ + Spec: authenticationapi.TokenReviewSpec{ + Token: token, + }, + } + result, err := client.AuthenticationClient.TokenReviews().Create(tokenReq) + if err != nil { + return nil, nil, err + } + if !result.Status.Authenticated { + return nil, nil, errors.New("Not authenticated") + } + kubeConfig.BearerToken = token + return &result.Status.User, kubeConfig, nil +} + +func checkBasicAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) { + md, _ := metadata.FromContext(ctx) + authz := md[helm.Authorization][0] + + apiServer, err := getServerURL(md) + if err != nil { + return nil, nil, err + } + basicAuth, err := base64.StdEncoding.DecodeString(authz[len("Basic "):]) + if err != nil { + return nil, nil, err + } + username, password := getUserPasswordFromBasicAuth(string(basicAuth)) + if len(username) == 0 || len(password) == 0 { + return nil, nil, errors.New("Missing username or password.") + } + kubeConfig := &rest.Config{ + Host: apiServer, + Username: username, + Password: password, + } + caCert, err := getCertificateAuthority(md) + if err == nil { + kubeConfig.TLSClientConfig = rest.TLSClientConfig{ + CAData: caCert, + } + } + + client, err := clientset.NewForConfig(kubeConfig) + if err != nil { + return nil, nil, err + } + + // verify credentials + _, err = client.DiscoveryClient.ServerVersion() + if err != nil { + return nil, nil, err + } + + return &authenticationapi.UserInfo{ + Username: username, + }, kubeConfig, nil +} + +func getUserPasswordFromBasicAuth(token string) (string, string) { + st := strings.SplitN(token, ":", 2) + if len(st) == 2 { + return st[0], st[1] + } + return "", "" +} + +func checkClientCert(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) { + md, _ := metadata.FromContext(ctx) + + apiServer, err := getServerURL(md) + if err != nil { + return nil, nil, err + } + kubeConfig := &rest.Config{ + Host: apiServer, + } + crt, err := getClientCert(md) + if err != nil { + return nil, nil, err + } + key, err := getClientKey(md) + if err != nil { + return nil, nil, err + } + kubeConfig.TLSClientConfig = rest.TLSClientConfig{ + KeyData: key, + CertData: crt, + } + caCert, err := getCertificateAuthority(md) + if err == nil { + kubeConfig.TLSClientConfig.CAData = caCert + } + client, err := clientset.NewForConfig(kubeConfig) + if err != nil { + return nil, nil, err + } + + // verify credentials + _, err = client.DiscoveryClient.ServerVersion() + if err != nil { + return nil, nil, err + } + + pem, _ := pem.Decode([]byte(crt)) + c, err := x509.ParseCertificate(pem.Bytes) + if err != nil { + return nil, nil, err + } + + return &authenticationapi.UserInfo{ + Username: c.Subject.CommonName, + }, kubeConfig, nil +} + +func getClientCert(md metadata.MD) ([]byte, error) { + cert, ok := md[helm.K8sClientCertificate] + if !ok { + return nil, errors.New("Client certificate not found") + } + certData, err := base64.StdEncoding.DecodeString(cert[0]) + if err != nil { + return nil, err + } + return certData, nil +} + +func getClientKey(md metadata.MD) ([]byte, error) { + key, ok := md[helm.K8sClientKey] + if !ok { + return nil, errors.New("Client key not found") + } + keyData, err := base64.StdEncoding.DecodeString(key[0]) + if err != nil { + return nil, err + } + return keyData, nil +} + +func getCertificateAuthority(md metadata.MD) ([]byte, error) { + caData, ok := md[helm.K8sCertificateAuthority] + if !ok { + return nil, errors.New("CAcert not found") + } + caCert, err := base64.StdEncoding.DecodeString(caData[0]) + if err != nil { + return nil, err + } + return caCert, nil +} + +func getServerURL(md metadata.MD) (string, error) { + apiserver, ok := md[helm.K8sServer] + if !ok { + return "", errors.New("API server url not found") + } + return apiserver[0], nil +} From ba83a841673fae64885c5baf2d1025d00170a967 Mon Sep 17 00:00:00 2001 From: tamal Date: Thu, 9 Feb 2017 08:30:06 -0800 Subject: [PATCH 2/8] Fix kubeconfig loading for TokenReview. --- pkg/tiller/server.go | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/pkg/tiller/server.go b/pkg/tiller/server.go index c28d67a8c..1b83770a1 100644 --- a/pkg/tiller/server.go +++ b/pkg/tiller/server.go @@ -23,8 +23,10 @@ import ( "errors" "fmt" "log" + "os" "strings" + "github.com/spf13/pflag" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -35,6 +37,7 @@ import ( rest "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + utilflag "k8s.io/kubernetes/pkg/util/flag" ) // maxMsgSize use 10MB as the default message size limit. @@ -157,18 +160,35 @@ func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Co } caCert, _ := getCertificateAuthority(md) - // TODO: Should be InClusterConfig() ? - kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{DefaultClientConfig: &clientcmd.DefaultClientConfig}, - &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{ + // ref: k8s.io/helm/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util#NewFactory() + flags := pflag.NewFlagSet("", pflag.ContinueOnError) + flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + // use the standard defaults for this client command + // DEPRECATED: remove and replace with something more accurate + loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig + + flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") + + overrides := &clientcmd.ConfigOverrides{ + ClusterDefaults: clientcmd.ClusterDefaults, + ClusterInfo: clientcmdapi.Cluster{ Server: apiServer, CertificateAuthorityData: caCert, - }}).ClientConfig() + }, + } + + flagNames := clientcmd.RecommendedConfigOverrideFlags("") + // short flagnames are disabled by default. These are here for compatibility with existing scripts + flagNames.ClusterOverrideFlags.APIServer.ShortName = "s" + + clientcmd.BindOverrideFlags(overrides, flags, flagNames) + tokenConfig, err := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin).ClientConfig() if err != nil { return nil, nil, err } - client, err := clientset.NewForConfig(kubeConfig) + client, err := clientset.NewForConfig(tokenConfig) if err != nil { return nil, nil, err } @@ -186,7 +206,13 @@ func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Co if !result.Status.Authenticated { return nil, nil, errors.New("Not authenticated") } - kubeConfig.BearerToken = token + kubeConfig := &rest.Config{ + Host: apiServer, + BearerToken: token, + TLSClientConfig: rest.TLSClientConfig{ + CAData: caCert, + }, + } return &result.Status.User, kubeConfig, nil } From 6a4054de2715dff7e1202965bc3ab37537fadb3b Mon Sep 17 00:00:00 2001 From: saumanbiswas Date: Fri, 10 Feb 2017 18:10:18 +0600 Subject: [PATCH 3/8] Store Username added in ReleaseInfo (#5) --- _proto/hapi/release/info.proto | 3 +++ cmd/helm/history.go | 15 ++++++----- cmd/helm/list.go | 5 ++-- cmd/helm/printer.go | 2 ++ cmd/helm/status.go | 2 ++ pkg/helm/types.go | 10 +++---- pkg/proto/hapi/chart/metadata.pb.go | 2 +- pkg/proto/hapi/release/info.pb.go | 37 +++++++++++++------------ pkg/proto/hapi/services/tiller.pb.go | 40 ++++++++++++++-------------- pkg/tiller/release_server.go | 37 ++++++++++++++++++------- 10 files changed, 91 insertions(+), 62 deletions(-) diff --git a/_proto/hapi/release/info.proto b/_proto/hapi/release/info.proto index e23175d3d..39c424b7f 100644 --- a/_proto/hapi/release/info.proto +++ b/_proto/hapi/release/info.proto @@ -34,4 +34,7 @@ message Info { // Description is human-friendly "log entry" about this release. string Description = 5; + + // Username is the authenticated user who performed this release. + string Username = 6; } diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 5abace2b8..b14fa0a8c 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -38,11 +38,11 @@ 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 DESCRIPTION - 1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Initial install - 2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Upgraded successfully - 3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Rolled back to 2 - 4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0 Upgraded successfully + REVISION UPDATED STATUS CHART DESCRIPTION RELEASED BY + 1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Initial install x + 2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Upgraded successfully y + 3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Rolled back to 2 z + 4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0 Upgraded successfully x ` type historyCmd struct { @@ -98,7 +98,7 @@ func (cmd *historyCmd) run() error { func formatHistory(rls []*release.Release) string { tbl := uitable.New() tbl.MaxColWidth = 60 - tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION") + tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION", "RELEASED BY") for i := len(rls) - 1; i >= 0; i-- { r := rls[i] c := formatChartname(r.Chart) @@ -106,7 +106,8 @@ func formatHistory(rls []*release.Release) string { s := r.Info.Status.Code.String() v := r.Version d := r.Info.Description - tbl.AddRow(v, t, s, c, d) + u := r.Info.Username + tbl.AddRow(v, t, s, c, d, u) } return tbl.String() } diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 3764f365e..bb6a8506e 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -202,14 +202,15 @@ func (l *listCmd) statusCodes() []release.Status_Code { func formatList(rels []*release.Release) string { table := uitable.New() table.MaxColWidth = 60 - table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE") + table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE", "RELEASED BY") for _, r := range rels { 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 n := r.Namespace - table.AddRow(r.Name, v, t, s, c, n) + u := r.Info.Username + table.AddRow(r.Name, v, t, s, c, n, u) } return table.String() } diff --git a/cmd/helm/printer.go b/cmd/helm/printer.go index 34b1122be..a135bc886 100644 --- a/cmd/helm/printer.go +++ b/cmd/helm/printer.go @@ -28,6 +28,7 @@ import ( var printReleaseTemplate = `REVISION: {{.Release.Version}} RELEASED: {{.ReleaseDate}} +RELEASED BY: {{.ReleasedBy}} CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}} USER-SUPPLIED VALUES: {{.Release.Config.Raw}} @@ -61,6 +62,7 @@ func printRelease(out io.Writer, rel *release.Release) error { "Release": rel, "ComputedValues": cfgStr, "ReleaseDate": timeconv.Format(rel.Info.LastDeployed, time.ANSIC), + "ReleasedBy": rel.Info.Username, } return tpl(printReleaseTemplate, data, out) } diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 9f158e408..ba42a26b2 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -38,6 +38,7 @@ The status consists of: - last deployment time - k8s namespace in which the release lives - state of the release (can be: UNKNOWN, DEPLOYED, DELETED, SUPERSEDED, FAILED or DELETING) +- name of the user who deployed the release - list of resources that this release consists of, sorted by kind - details on last test suite run, if applicable - additional notes provided by the chart @@ -96,6 +97,7 @@ func PrintStatus(out io.Writer, res *services.GetReleaseStatusResponse) { } fmt.Fprintf(out, "NAMESPACE: %s\n", res.Namespace) fmt.Fprintf(out, "STATUS: %s\n", res.Info.Status.Code) + fmt.Fprintf(out, "RELEASED BY: %s\n", res.Info.Username) fmt.Fprintf(out, "\n") if len(res.Info.Status.Resources) > 0 { re := regexp.MustCompile(" +") diff --git a/pkg/helm/types.go b/pkg/helm/types.go index b13d18166..9b0b0a32e 100644 --- a/pkg/helm/types.go +++ b/pkg/helm/types.go @@ -1,13 +1,13 @@ package helm const ( - Authorization = "authorization" - K8sServer = "k8s-server" - K8sClientCertificate = "k8s-client-certificate" + Authorization = "authorization" + K8sServer = "k8s-server" + K8sClientCertificate = "k8s-client-certificate" K8sCertificateAuthority = "k8s-certificate-authority" - K8sClientKey = "k8s-client-key" + K8sClientKey = "k8s-client-key" // Generated from input keys above - K8sUser = "k8s-user" + K8sUser = "k8s-user" K8sConfig = "k8s-client-config" ) diff --git a/pkg/proto/hapi/chart/metadata.pb.go b/pkg/proto/hapi/chart/metadata.pb.go index 322719e3d..536142835 100644 --- a/pkg/proto/hapi/chart/metadata.pb.go +++ b/pkg/proto/hapi/chart/metadata.pb.go @@ -74,7 +74,7 @@ type Metadata struct { // The condition to check to enable chart Condition string `protobuf:"bytes,11,opt,name=condition" json:"condition,omitempty"` // The tags to check to enable chart - Tags []string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"` + Tags string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"` } func (m *Metadata) Reset() { *m = Metadata{} } diff --git a/pkg/proto/hapi/release/info.pb.go b/pkg/proto/hapi/release/info.pb.go index a73dcab2f..24a895b02 100644 --- a/pkg/proto/hapi/release/info.pb.go +++ b/pkg/proto/hapi/release/info.pb.go @@ -22,7 +22,9 @@ type Info struct { // Deleted tracks when this object was deleted. Deleted *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=deleted" json:"deleted,omitempty"` // Description is human-friendly "log entry" about this release. - Description string `protobuf:"bytes,5,opt,name=Description" json:"Description,omitempty"` + Description string `protobuf:"bytes,5,opt,name=Description,json=description" json:"Description,omitempty"` + // Username is the authenticated user who performed this release. + Username string `protobuf:"bytes,6,opt,name=Username,json=username" json:"Username,omitempty"` } func (m *Info) Reset() { *m = Info{} } @@ -65,20 +67,21 @@ func init() { func init() { proto.RegisterFile("hapi/release/info.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 235 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x31, 0x4f, 0xc3, 0x30, - 0x10, 0x85, 0x95, 0x52, 0x5a, 0xd5, 0x6d, 0x19, 0x2c, 0x24, 0x42, 0x16, 0x22, 0xa6, 0x0e, 0xc8, - 0x91, 0x80, 0x1d, 0x81, 0xba, 0xb0, 0x06, 0x26, 0x16, 0xe4, 0xe2, 0x73, 0xb1, 0xe4, 0xe6, 0x2c, - 0xfb, 0x3a, 0xf0, 0x2f, 0xf8, 0xc9, 0xa8, 0xb6, 0x83, 0xd2, 0xa9, 0xab, 0xbf, 0xf7, 0x3e, 0xbf, - 0x63, 0x57, 0xdf, 0xd2, 0x99, 0xc6, 0x83, 0x05, 0x19, 0xa0, 0x31, 0x9d, 0x46, 0xe1, 0x3c, 0x12, - 0xf2, 0xc5, 0x01, 0x88, 0x0c, 0xaa, 0x9b, 0x2d, 0xe2, 0xd6, 0x42, 0x13, 0xd9, 0x66, 0xaf, 0x1b, - 0x32, 0x3b, 0x08, 0x24, 0x77, 0x2e, 0xc5, 0xab, 0xeb, 0x23, 0x4f, 0x20, 0x49, 0xfb, 0x90, 0xd0, - 0xed, 0xef, 0x88, 0x8d, 0x5f, 0x3b, 0x8d, 0xfc, 0x8e, 0x4d, 0x12, 0x28, 0x8b, 0xba, 0x58, 0xcd, - 0xef, 0x2f, 0xc5, 0xf0, 0x0f, 0xf1, 0x16, 0x59, 0x9b, 0x33, 0xfc, 0x99, 0x5d, 0x68, 0xe3, 0x03, - 0x7d, 0x2a, 0x70, 0x16, 0x7f, 0x40, 0x95, 0xa3, 0xd8, 0xaa, 0x44, 0xda, 0x22, 0xfa, 0x2d, 0xe2, - 0xbd, 0xdf, 0xd2, 0x2e, 0x63, 0x63, 0x9d, 0x0b, 0xfc, 0x89, 0x2d, 0xad, 0x1c, 0x1a, 0xce, 0x4e, - 0x1a, 0x16, 0x87, 0xc2, 0xbf, 0xe0, 0x91, 0x4d, 0x15, 0x58, 0x20, 0x50, 0xe5, 0xf8, 0x64, 0xb5, - 0x8f, 0xf2, 0x9a, 0xcd, 0xd7, 0x10, 0xbe, 0xbc, 0x71, 0x64, 0xb0, 0x2b, 0xcf, 0xeb, 0x62, 0x35, - 0x6b, 0x87, 0x4f, 0x2f, 0xb3, 0x8f, 0x69, 0xbe, 0x7a, 0x33, 0x89, 0xa6, 0x87, 0xbf, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x1a, 0x52, 0x8f, 0x9c, 0x89, 0x01, 0x00, 0x00, + // 252 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x90, 0xb1, 0x4e, 0xc3, 0x30, + 0x14, 0x45, 0x95, 0x52, 0xd2, 0xd6, 0x69, 0x19, 0x2c, 0x24, 0x4c, 0x16, 0x22, 0xa6, 0x0e, 0xc8, + 0x91, 0x80, 0x1d, 0x81, 0xba, 0xb0, 0x06, 0x58, 0x58, 0x90, 0x8b, 0x5f, 0x8a, 0x25, 0x27, 0xb6, + 0xec, 0x97, 0x81, 0x7f, 0xe2, 0x23, 0x51, 0x1d, 0x07, 0xda, 0x29, 0x63, 0x72, 0xee, 0xb9, 0xef, + 0xca, 0xe4, 0xe2, 0x4b, 0x58, 0x55, 0x3a, 0xd0, 0x20, 0x3c, 0x94, 0xaa, 0xad, 0x0d, 0xb7, 0xce, + 0xa0, 0xa1, 0xcb, 0x3d, 0xe0, 0x11, 0xe4, 0x57, 0x3b, 0x63, 0x76, 0x1a, 0xca, 0xc0, 0xb6, 0x5d, + 0x5d, 0xa2, 0x6a, 0xc0, 0xa3, 0x68, 0x6c, 0x1f, 0xcf, 0x2f, 0x8f, 0x7a, 0x3c, 0x0a, 0xec, 0x7c, + 0x8f, 0xae, 0x7f, 0x26, 0x64, 0xfa, 0xdc, 0xd6, 0x86, 0xde, 0x90, 0xb4, 0x07, 0x2c, 0x29, 0x92, + 0x75, 0x76, 0x7b, 0xce, 0x0f, 0x6f, 0xf0, 0x97, 0xc0, 0xaa, 0x98, 0xa1, 0x8f, 0xe4, 0xac, 0x56, + 0xce, 0xe3, 0x87, 0x04, 0xab, 0xcd, 0x37, 0x48, 0x36, 0x09, 0x56, 0xce, 0xfb, 0x2d, 0x7c, 0xd8, + 0xc2, 0x5f, 0x87, 0x2d, 0xd5, 0x2a, 0x18, 0x9b, 0x28, 0xd0, 0x07, 0xb2, 0xd2, 0xe2, 0xb0, 0xe1, + 0x64, 0xb4, 0x61, 0xb9, 0x17, 0xfe, 0x0a, 0xee, 0xc9, 0x4c, 0x82, 0x06, 0x04, 0xc9, 0xa6, 0xa3, + 0xea, 0x10, 0xa5, 0x05, 0xc9, 0x36, 0xe0, 0x3f, 0x9d, 0xb2, 0xa8, 0x4c, 0xcb, 0x4e, 0x8b, 0x64, + 0xbd, 0xa8, 0x32, 0xf9, 0xff, 0x8b, 0xe6, 0x64, 0xfe, 0xe6, 0xc1, 0xb5, 0xa2, 0x01, 0x96, 0x06, + 0x3c, 0xef, 0xe2, 0xf7, 0xd3, 0xe2, 0x7d, 0x16, 0x5f, 0x64, 0x9b, 0x86, 0x2b, 0x77, 0xbf, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x32, 0x11, 0x9d, 0xcc, 0xa5, 0x01, 0x00, 0x00, } diff --git a/pkg/proto/hapi/services/tiller.pb.go b/pkg/proto/hapi/services/tiller.pb.go index 4e772dd0d..d987189cc 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -447,7 +447,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{} } @@ -1051,23 +1051,23 @@ var fileDescriptor0 = []byte{ 0xf2, 0x0e, 0x14, 0xb5, 0x02, 0x28, 0x36, 0xa0, 0xb1, 0x88, 0xa3, 0x4b, 0x2c, 0xa1, 0x15, 0x93, 0x6c, 0x8e, 0x75, 0x2d, 0x47, 0x67, 0x0c, 0xd6, 0xdd, 0x18, 0x1e, 0x99, 0x51, 0x12, 0x75, 0x4a, 0xdd, 0x1d, 0x41, 0xd3, 0xce, 0x3a, 0xac, 0x1d, 0x61, 0xf6, 0x46, 0x1c, 0x00, 0x99, 0x9e, 0x33, - 0x04, 0x94, 0x5d, 0xbc, 0xf5, 0x27, 0x97, 0x74, 0x7f, 0xea, 0x1d, 0xa3, 0xf4, 0x95, 0x96, 0xf3, - 0x25, 0xb7, 0x7d, 0xec, 0x53, 0x46, 0xa2, 0x9b, 0xfb, 0xa0, 0xeb, 0x82, 0x39, 0xf7, 0xde, 0x49, - 0x66, 0x4f, 0x86, 0xce, 0x11, 0x8f, 0x20, 0xdd, 0x2a, 0x23, 0xc8, 0xde, 0x93, 0x46, 0xb5, 0x7b, - 0xf2, 0x47, 0x40, 0xaf, 0x71, 0x7a, 0x65, 0x3f, 0x70, 0xc5, 0xa8, 0x22, 0xd4, 0xf4, 0x46, 0xb3, - 0xa0, 0x35, 0x09, 0xb0, 0x17, 0xc6, 0x0b, 0x59, 0x36, 0x35, 0x75, 0xf6, 0x60, 0x5d, 0xb3, 0x2e, - 0xe3, 0x4c, 0xf2, 0xa1, 0x97, 0xd2, 0x7a, 0x32, 0x1c, 0xfc, 0xd3, 0x86, 0x55, 0x75, 0xc7, 0x8a, - 0xf7, 0x12, 0xf2, 0x61, 0x25, 0xfb, 0x98, 0x40, 0x4f, 0xcb, 0x9f, 0x53, 0xb9, 0x37, 0xa1, 0xfd, - 0xac, 0x8a, 0xaa, 0x88, 0xc5, 0x59, 0xfa, 0xcc, 0x40, 0x14, 0xba, 0xf9, 0x3b, 0x1e, 0x3d, 0x2f, - 0xb6, 0x51, 0xf2, 0xa8, 0xb0, 0xfb, 0x55, 0xd5, 0x95, 0x5b, 0x74, 0xcd, 0xab, 0xaf, 0x5f, 0xcc, - 0xe8, 0x41, 0x33, 0xfa, 0x5b, 0xc0, 0xde, 0xaf, 0xac, 0x9f, 0xfa, 0xfd, 0x19, 0x9e, 0x68, 0xb7, - 0x12, 0x2a, 0x41, 0xab, 0xe8, 0x9a, 0xb7, 0x3f, 0xa9, 0xa4, 0x9b, 0xfa, 0x9a, 0xc3, 0xaa, 0x4e, - 0x37, 0xa8, 0xc4, 0x40, 0x21, 0x7f, 0xdb, 0x9f, 0x56, 0x53, 0x4e, 0xdd, 0x51, 0xe8, 0xe6, 0xd9, - 0xa0, 0xac, 0x8e, 0x25, 0xcc, 0x55, 0x56, 0xc7, 0x32, 0x92, 0x71, 0x96, 0x90, 0x07, 0x70, 0x4b, - 0x06, 0x68, 0xaf, 0xb4, 0x20, 0x3a, 0x87, 0xd8, 0xbd, 0x87, 0x15, 0x53, 0x17, 0x0b, 0xf8, 0x5f, - 0xee, 0xb6, 0x44, 0x25, 0xd0, 0x14, 0x3f, 0x12, 0xec, 0xe7, 0x15, 0xb5, 0x73, 0x49, 0x49, 0x7e, - 0xb9, 0x27, 0x29, 0x9d, 0xbc, 0xee, 0x49, 0x2a, 0x47, 0x55, 0xce, 0x12, 0xf2, 0x61, 0xd5, 0x8d, - 0x43, 0xe9, 0x3a, 0x61, 0x09, 0x54, 0xb2, 0xfb, 0x2e, 0x3f, 0xd9, 0x4f, 0x2b, 0x68, 0xde, 0x9e, - 0xef, 0x17, 0xf0, 0x43, 0x5b, 0xa9, 0x5e, 0x34, 0xf9, 0xdf, 0xc9, 0x2f, 0xfe, 0x0d, 0x00, 0x00, - 0xff, 0xff, 0xf9, 0x32, 0x44, 0xf7, 0x1f, 0x0f, 0x00, 0x00, + 0x04, 0x94, 0x5d, 0xbc, 0xf5, 0x27, 0x97, 0x74, 0x7f, 0xea, 0x1d, 0xa3, 0xf4, 0x53, 0x6a, 0xfe, + 0x92, 0xdb, 0x3e, 0xf6, 0x29, 0x23, 0xd1, 0xcd, 0x7d, 0xd0, 0x75, 0xc1, 0x9c, 0x7b, 0xef, 0x24, + 0xb3, 0x27, 0x43, 0xe7, 0x88, 0x47, 0x90, 0x6e, 0x95, 0x11, 0x64, 0xef, 0x49, 0xa3, 0xda, 0x3d, + 0xf9, 0x23, 0xa0, 0xd7, 0x38, 0xbd, 0xb2, 0x1f, 0xb8, 0x62, 0x54, 0x11, 0x6a, 0x7a, 0xa3, 0x59, + 0xd0, 0x9a, 0x04, 0xd8, 0x0b, 0xe3, 0x85, 0x2c, 0x9b, 0x9a, 0x3a, 0x7b, 0xb0, 0xae, 0x59, 0x97, + 0x71, 0x26, 0xf9, 0xd0, 0x4b, 0x69, 0x3d, 0x19, 0x0e, 0xfe, 0x69, 0xc3, 0xaa, 0xba, 0x63, 0xc5, + 0x7b, 0x09, 0xf9, 0xb0, 0x92, 0x7d, 0x4c, 0xa0, 0xa7, 0xe5, 0xcf, 0xa9, 0xdc, 0x9b, 0xd0, 0x7e, + 0x56, 0x45, 0x55, 0xc4, 0xe2, 0x2c, 0x7d, 0x66, 0x20, 0x0a, 0xdd, 0xfc, 0x1d, 0x8f, 0x9e, 0x17, + 0xdb, 0x28, 0x79, 0x54, 0xd8, 0xfd, 0xaa, 0xea, 0xca, 0x2d, 0xba, 0xe6, 0xd5, 0xd7, 0x2f, 0x66, + 0xf4, 0xa0, 0x19, 0xfd, 0x2d, 0x60, 0xef, 0x57, 0xd6, 0x4f, 0xfd, 0xfe, 0x0c, 0x4f, 0xb4, 0x5b, + 0x09, 0x95, 0xa0, 0x55, 0x74, 0xcd, 0xdb, 0x9f, 0x54, 0xd2, 0x4d, 0x7d, 0xcd, 0x61, 0x55, 0xa7, + 0x1b, 0x54, 0x62, 0xa0, 0x90, 0xbf, 0xed, 0x4f, 0xab, 0x29, 0xa7, 0xee, 0x28, 0x74, 0xf3, 0x6c, + 0x50, 0x56, 0xc7, 0x12, 0xe6, 0x2a, 0xab, 0x63, 0x19, 0xc9, 0x38, 0x4b, 0xc8, 0x03, 0xb8, 0x25, + 0x03, 0xb4, 0x57, 0x5a, 0x10, 0x9d, 0x43, 0xec, 0xde, 0xc3, 0x8a, 0xa9, 0x8b, 0x05, 0xfc, 0x2f, + 0x77, 0x5b, 0xa2, 0x12, 0x68, 0x8a, 0x1f, 0x09, 0xf6, 0xf3, 0x8a, 0xda, 0xb9, 0xa4, 0x24, 0xbf, + 0xdc, 0x93, 0x94, 0x4e, 0x5e, 0xf7, 0x24, 0x95, 0xa3, 0x2a, 0x67, 0x09, 0xf9, 0xb0, 0xea, 0xc6, + 0xa1, 0x74, 0x9d, 0xb0, 0x04, 0x2a, 0xd9, 0x7d, 0x97, 0x9f, 0xec, 0xa7, 0x15, 0x34, 0x6f, 0xcf, + 0xf7, 0x0b, 0xf8, 0xa1, 0xad, 0x54, 0x2f, 0x9a, 0xfc, 0xef, 0xe4, 0x17, 0xff, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x26, 0xd9, 0xff, 0xa0, 0x1f, 0x0f, 0x00, 0x00, } diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 8e9f20c32..c032852af 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -27,12 +27,9 @@ import ( "github.com/technosophos/moniker" ctx "golang.org/x/net/context" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/typed/discovery" - "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" @@ -43,6 +40,10 @@ import ( "k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/version" + "k8s.io/kubernetes/pkg/api/unversioned" + authenticationapi "k8s.io/kubernetes/pkg/apis/authentication" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/client/typed/discovery" ) // releaseNameMaxLen is the maximum length of a release name. @@ -289,7 +290,7 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease return nil, err } - res, err := s.performUpdate(currentRelease, updatedRelease, req) + res, err := s.performUpdate(c, currentRelease, updatedRelease, req) if err != nil { return res, err } @@ -303,9 +304,10 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease return res, nil } -func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { +func (s *ReleaseServer) performUpdate(c ctx.Context, originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { res := &services.UpdateReleaseResponse{Release: updatedRelease} + updatedRelease.Info.Username = getUserName(c) if req.DryRun { log.Printf("Dry run for %s", updatedRelease.Name) res.Release.Info.Description = "Dry run complete" @@ -448,7 +450,7 @@ func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackRel return nil, err } - res, err := s.performRollback(currentRelease, targetRelease, req) + res, err := s.performRollback(c, currentRelease, targetRelease, req) if err != nil { return res, err } @@ -462,9 +464,11 @@ func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackRel return res, nil } -func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { +func (s *ReleaseServer) performRollback(c ctx.Context, currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { res := &services.RollbackReleaseResponse{Release: targetRelease} + targetRelease.Info.Username = getUserName(c) + if req.DryRun { log.Printf("Dry run for %s", targetRelease.Name) return res, nil @@ -634,7 +638,7 @@ func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea return res, err } - res, err := s.performRelease(rel, req) + res, err := s.performRelease(c, rel, req) if err != nil { log.Printf("Failed install perform step: %s", err) } @@ -819,9 +823,10 @@ func (s *ReleaseServer) recordRelease(r *release.Release, reuse bool) { } // performRelease runs a release. -func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { +func (s *ReleaseServer) performRelease(c ctx.Context, r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { res := &services.InstallReleaseResponse{Release: r} + r.Info.Username = getUserName(c) if req.DryRun { log.Printf("Dry run for %s", r.Name) res.Release.Info.Description = "Dry run complete" @@ -1110,3 +1115,15 @@ func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream return s.env.Releases.Update(rel) } + +func getUserName(c ctx.Context) string { + user := c.Value(helm.K8sUser) + if user == nil { + return "" + } + userInfo, ok := user.(*authenticationapi.UserInfo) + if !ok { + return "" + } + return userInfo.Username +} From d1d3bc022f64089ad7f3f1c7dc124e9053a8be32 Mon Sep 17 00:00:00 2001 From: Sadlil Rhythom Date: Fri, 10 Feb 2017 18:36:44 +0600 Subject: [PATCH 4/8] Added auth provider token to context (#6) --- pkg/tiller/server.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/tiller/server.go b/pkg/tiller/server.go index 1b83770a1..2b018ffa0 100644 --- a/pkg/tiller/server.go +++ b/pkg/tiller/server.go @@ -121,11 +121,29 @@ func newStreamInterceptor() grpc.StreamServerInterceptor { log.Println(err) return err } - // TODO: How to pass modified ctx? - return handler(srv, ss) + + newStream := serverStreamWrapper{ + ss: ss, + ctx: ctx, + } + return handler(srv, newStream) } } +// serverStreamWrapper wraps original ServerStream but uses modified context. +// this modified context will be available inside handler() +type serverStreamWrapper struct { + ss grpc.ServerStream + ctx context.Context +} + +func (w serverStreamWrapper) Context() context.Context { return w.ctx } +func (w serverStreamWrapper) RecvMsg(msg interface{}) error { return w.ss.RecvMsg(msg) } +func (w serverStreamWrapper) SendMsg(msg interface{}) error { return w.ss.SendMsg(msg) } +func (w serverStreamWrapper) SendHeader(md metadata.MD) error { return w.ss.SendHeader(md) } +func (w serverStreamWrapper) SetHeader(md metadata.MD) error { return w.ss.SetHeader(md) } +func (w serverStreamWrapper) SetTrailer(md metadata.MD) { w.ss.SetTrailer(md) } + func splitMethod(fullMethod string) (string, string) { if frags := strings.Split(fullMethod, "/"); len(frags) == 3 { return frags[1], frags[2] From aff90fe6d4092aaec8f599b25ec1fca47991e0b3 Mon Sep 17 00:00:00 2001 From: saumanbiswas Date: Wed, 22 Feb 2017 18:28:25 +0600 Subject: [PATCH 5/8] Unit test fixed (#7) --- cmd/helm/get_test.go | 3 ++- cmd/helm/helm_test.go | 3 +++ cmd/helm/history_test.go | 5 +++-- cmd/helm/list_test.go | 3 ++- cmd/helm/status_test.go | 22 ++++++++++++---------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 77d8d4d19..cfcc49968 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/spf13/cobra" + "fmt" ) func TestGetCmd(t *testing.T) { @@ -29,7 +30,7 @@ func TestGetCmd(t *testing.T) { name: "get with a release", resp: releaseMock(&releaseOptions{name: "thomas-guide"}), args: []string{"thomas-guide"}, - expected: "REVISION: 1\nRELEASED: (.*)\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + mockHookTemplate + "\nMANIFEST:", + expected: fmt.Sprintf("REVISION: 1\nRELEASED: (.*)\nRELEASED BY: %s\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + mockHookTemplate + "\nMANIFEST:", username), }, { name: "get requires release name arg", diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index b0a5075a7..1c5f9dd6d 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -59,6 +59,8 @@ type releaseOptions struct { namespace string } +var username = "John" + func releaseMock(opts *releaseOptions) *release.Release { date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} @@ -102,6 +104,7 @@ func releaseMock(opts *releaseOptions) *release.Release { LastDeployed: &date, Status: &release.Status{Code: scode}, Description: "Release mock", + Username: username, }, Chart: ch, Config: &chart.Config{Raw: `name: "value"`}, diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index 5f57e1748..953890037 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -22,6 +22,7 @@ import ( "testing" rpb "k8s.io/helm/pkg/proto/hapi/release" + "fmt" ) func TestHistoryCmd(t *testing.T) { @@ -50,7 +51,7 @@ func TestHistoryCmd(t *testing.T) { mk("angry-bird", 2, rpb.Status_SUPERSEDED), mk("angry-bird", 1, rpb.Status_SUPERSEDED), }, - xout: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n", + xout: fmt.Sprintf("REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \tRELEASED BY\n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\t%s \n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\t%s \n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\t%s \n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\t%s \n", username, username, username, username), }, { cmds: "helm history --max=MAX RELEASE_NAME", @@ -60,7 +61,7 @@ func TestHistoryCmd(t *testing.T) { mk("angry-bird", 4, rpb.Status_DEPLOYED), mk("angry-bird", 3, rpb.Status_SUPERSEDED), }, - xout: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n", + xout: fmt.Sprintf("REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \tRELEASED BY\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\t%s \n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\t%s \n", username, username), }, } diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 611f47973..4c0fc549c 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -22,6 +22,7 @@ import ( "testing" "k8s.io/helm/pkg/proto/hapi/release" + "fmt" ) func TestListCmd(t *testing.T) { @@ -45,7 +46,7 @@ func TestListCmd(t *testing.T) { resp: []*release.Release{ releaseMock(&releaseOptions{name: "atlas"}), }, - expected: "NAME \tREVISION\tUPDATED \tSTATUS \tCHART \tNAMESPACE\natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\tdefault \n", + expected: fmt.Sprintf("NAME \tREVISION\tUPDATED \tSTATUS \tCHART \tNAMESPACE\tRELEASED BY\natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\tdefault \t%s \n", username), }, { name: "list, one deployed, one failed", diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 0ab0db08f..ba2e5f33c 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -50,7 +50,7 @@ func TestStatusCmd(t *testing.T) { { name: "get status of a deployed release", args: []string{"flummoxed-chickadee"}, - expected: outputWithStatus("DEPLOYED\n\n"), + expected: outputWithStatus("DEPLOYED", username+"\n\n"), rel: releaseMockWithStatus(&release.Status{ Code: release.Status_DEPLOYED, }), @@ -58,7 +58,7 @@ func TestStatusCmd(t *testing.T) { { name: "get status of a deployed release with notes", args: []string{"flummoxed-chickadee"}, - expected: outputWithStatus("DEPLOYED\n\nNOTES:\nrelease notes\n"), + expected: outputWithStatus("DEPLOYED", username+"\n\nNOTES:\nrelease notes\n"), rel: releaseMockWithStatus(&release.Status{ Code: release.Status_DEPLOYED, Notes: "release notes", @@ -67,7 +67,7 @@ func TestStatusCmd(t *testing.T) { { name: "get status of a deployed release with resources", args: []string{"flummoxed-chickadee"}, - expected: outputWithStatus("DEPLOYED\n\nRESOURCES:\nresource A\nresource B\n\n"), + expected: outputWithStatus("DEPLOYED", username+"\n\nRESOURCES:\nresource A\nresource B\n\n"), rel: releaseMockWithStatus(&release.Status{ Code: release.Status_DEPLOYED, Resources: "resource A\nresource B\n", @@ -76,10 +76,10 @@ func TestStatusCmd(t *testing.T) { { name: "get status of a deployed release with test suite", args: []string{"flummoxed-chickadee"}, - expected: outputWithStatus( - fmt.Sprintf("DEPLOYED\n\nTEST SUITE:\nLast Started: %s\nLast Completed: %s\n\n", dateString, dateString) + - fmt.Sprint("TEST \tSTATUS \tINFO \tSTARTED \tCOMPLETED \n") + - fmt.Sprintf("test run 1\tSUCCESS \textra info\t%s\t%s\n", dateString, dateString) + + expected: outputWithStatus("DEPLOYED", + fmt.Sprintf("%s\n\nTEST SUITE:\nLast Started: %s\nLast Completed: %s\n\n", username, dateString, dateString)+ + fmt.Sprint("TEST \tSTATUS \tINFO \tSTARTED \tCOMPLETED \n")+ + fmt.Sprintf("test run 1\tSUCCESS \textra info\t%s\t%s\n", dateString, dateString)+ fmt.Sprintf("test run 2\tFAILURE \t \t%s\t%s\n", dateString, dateString)), rel: releaseMockWithStatus(&release.Status{ Code: release.Status_DEPLOYED, @@ -131,10 +131,11 @@ func TestStatusCmd(t *testing.T) { } } -func outputWithStatus(status string) string { - return fmt.Sprintf("LAST DEPLOYED: %s\nNAMESPACE: \nSTATUS: %s", +func outputWithStatus(status string, username string) string { + return fmt.Sprintf("LAST DEPLOYED: %s\nNAMESPACE: \nSTATUS: %s\nRELEASED BY: %s", dateString, - status) + status, + username) } func releaseMockWithStatus(status *release.Status) *release.Release { @@ -144,6 +145,7 @@ func releaseMockWithStatus(status *release.Status) *release.Release { FirstDeployed: &date, LastDeployed: &date, Status: status, + Username: username, }, } } From 1b858b33ce8c51fc8d4b8aa33964be8438e60357 Mon Sep 17 00:00:00 2001 From: saumanbiswas Date: Thu, 23 Feb 2017 01:24:26 +0600 Subject: [PATCH 6/8] go fmt and authheader type (#8) --- cmd/helm/get_test.go | 4 ++-- cmd/helm/history_test.go | 2 +- cmd/helm/list_test.go | 2 +- pkg/helm/option.go | 22 +++++++++++----------- pkg/helm/types.go | 16 +++++++++------- pkg/tiller/release_server.go | 2 +- pkg/tiller/server.go | 16 ++++++++-------- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index cfcc49968..ff213508a 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -17,11 +17,11 @@ limitations under the License. package main import ( + "fmt" "io" "testing" "github.com/spf13/cobra" - "fmt" ) func TestGetCmd(t *testing.T) { @@ -30,7 +30,7 @@ func TestGetCmd(t *testing.T) { name: "get with a release", resp: releaseMock(&releaseOptions{name: "thomas-guide"}), args: []string{"thomas-guide"}, - expected: fmt.Sprintf("REVISION: 1\nRELEASED: (.*)\nRELEASED BY: %s\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + mockHookTemplate + "\nMANIFEST:", username), + expected: fmt.Sprintf("REVISION: 1\nRELEASED: (.*)\nRELEASED BY: %s\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n"+mockHookTemplate+"\nMANIFEST:", username), }, { name: "get requires release name arg", diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index 953890037..a99f8c885 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -18,11 +18,11 @@ package main import ( "bytes" + "fmt" "regexp" "testing" rpb "k8s.io/helm/pkg/proto/hapi/release" - "fmt" ) func TestHistoryCmd(t *testing.T) { diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 4c0fc549c..8982e633a 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -18,11 +18,11 @@ package main import ( "bytes" + "fmt" "regexp" "testing" "k8s.io/helm/pkg/proto/hapi/release" - "fmt" ) func TestListCmd(t *testing.T) { diff --git a/pkg/helm/option.go b/pkg/helm/option.go index c2b077ea7..8d44a0e92 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -414,38 +414,38 @@ func extractKubeConfig() map[string]string { // Kube APIServer URL if len(c.Host) != 0 { - configData[K8sServer] = c.Host + configData[string(K8sServer)] = c.Host } if c.AuthProvider != nil { switch c.AuthProvider.Name { case "gcp": - configData[Authorization] = "Bearer " + c.AuthProvider.Config["access_token"] + configData[string(Authorization)] = "Bearer " + c.AuthProvider.Config["access_token"] case "oidc": - configData[Authorization] = "Bearer " + c.AuthProvider.Config["id-token"] + configData[string(Authorization)] = "Bearer " + c.AuthProvider.Config["id-token"] default: panic("Unknown auth provider: " + c.AuthProvider.Name) } } if len(c.BearerToken) != 0 { - configData[Authorization] = "Bearer " + c.BearerToken + configData[string(Authorization)] = "Bearer " + c.BearerToken } if len(c.Username) != 0 && len(c.Password) != 0 { - configData[Authorization] = "Basic " + base64.StdEncoding.EncodeToString([]byte(c.Username+":"+c.Password)) + configData[string(Authorization)] = "Basic " + base64.StdEncoding.EncodeToString([]byte(c.Username+":"+c.Password)) } if len(string(c.CAData)) != 0 { - configData[K8sCertificateAuthority] = base64.StdEncoding.EncodeToString(bytes.TrimSpace(c.CAData)) + configData[string(K8sCertificateAuthority)] = base64.StdEncoding.EncodeToString(bytes.TrimSpace(c.CAData)) } if len(string(c.TLSClientConfig.KeyData)) != 0 { - configData[K8sClientKey] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.KeyData) + configData[string(K8sClientKey)] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.KeyData) } if len(string(c.TLSClientConfig.CertData)) != 0 { - configData[K8sClientCertificate] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.CertData) + configData[string(K8sClientCertificate)] = base64.StdEncoding.EncodeToString(c.TLSClientConfig.CertData) } if len(c.TLSClientConfig.CAFile) != 0 { @@ -453,7 +453,7 @@ func extractKubeConfig() map[string]string { if err != nil { log.Println(err) } else { - configData[K8sCertificateAuthority] = base64.StdEncoding.EncodeToString(b) + configData[string(K8sCertificateAuthority)] = base64.StdEncoding.EncodeToString(b) } } @@ -462,7 +462,7 @@ func extractKubeConfig() map[string]string { if err != nil { log.Println(err) } else { - configData[K8sClientCertificate] = base64.StdEncoding.EncodeToString(b) + configData[string(K8sClientCertificate)] = base64.StdEncoding.EncodeToString(b) } } @@ -472,7 +472,7 @@ func extractKubeConfig() map[string]string { if err != nil { log.Println(err) } else { - configData[K8sClientKey] = base64.StdEncoding.EncodeToString(b) + configData[string(K8sClientKey)] = base64.StdEncoding.EncodeToString(b) } } } diff --git a/pkg/helm/types.go b/pkg/helm/types.go index 9b0b0a32e..8f92aaa6f 100644 --- a/pkg/helm/types.go +++ b/pkg/helm/types.go @@ -1,13 +1,15 @@ package helm +type AuthHeader string + const ( - Authorization = "authorization" - K8sServer = "k8s-server" - K8sClientCertificate = "k8s-client-certificate" - K8sCertificateAuthority = "k8s-certificate-authority" - K8sClientKey = "k8s-client-key" + Authorization AuthHeader = "authorization" + K8sServer AuthHeader = "k8s-server" + K8sClientCertificate AuthHeader = "k8s-client-certificate" + K8sCertificateAuthority AuthHeader = "k8s-certificate-authority" + K8sClientKey AuthHeader = "k8s-client-key" // Generated from input keys above - K8sUser = "k8s-user" - K8sConfig = "k8s-client-config" + K8sUser AuthHeader = "k8s-user" + K8sConfig AuthHeader = "k8s-client-config" ) diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index c032852af..d28b6d708 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -28,8 +28,8 @@ import ( "github.com/technosophos/moniker" ctx "golang.org/x/net/context" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hooks" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/hooks" "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" diff --git a/pkg/tiller/server.go b/pkg/tiller/server.go index 2b018ffa0..fc775021f 100644 --- a/pkg/tiller/server.go +++ b/pkg/tiller/server.go @@ -62,7 +62,7 @@ func authenticate(ctx context.Context) (context.Context, error) { var user *authenticationapi.UserInfo var kubeConfig *rest.Config var err error - authHeader, ok := md[helm.Authorization] + authHeader, ok := md[string(helm.Authorization)] if !ok || authHeader[0] == "" { user, kubeConfig, err = checkClientCert(ctx) } else { @@ -123,7 +123,7 @@ func newStreamInterceptor() grpc.StreamServerInterceptor { } newStream := serverStreamWrapper{ - ss: ss, + ss: ss, ctx: ctx, } return handler(srv, newStream) @@ -170,7 +170,7 @@ func checkClientVersion(ctx context.Context) error { func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) { md, _ := metadata.FromContext(ctx) - token := md[helm.Authorization][0][len("Bearer "):] + token := md[string(helm.Authorization)][0][len("Bearer "):] apiServer, err := getServerURL(md) if err != nil { @@ -236,7 +236,7 @@ func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Co func checkBasicAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) { md, _ := metadata.FromContext(ctx) - authz := md[helm.Authorization][0] + authz := md[string(helm.Authorization)][0] apiServer, err := getServerURL(md) if err != nil { @@ -335,7 +335,7 @@ func checkClientCert(ctx context.Context) (*authenticationapi.UserInfo, *rest.Co } func getClientCert(md metadata.MD) ([]byte, error) { - cert, ok := md[helm.K8sClientCertificate] + cert, ok := md[string(helm.K8sClientCertificate)] if !ok { return nil, errors.New("Client certificate not found") } @@ -347,7 +347,7 @@ func getClientCert(md metadata.MD) ([]byte, error) { } func getClientKey(md metadata.MD) ([]byte, error) { - key, ok := md[helm.K8sClientKey] + key, ok := md[string(helm.K8sClientKey)] if !ok { return nil, errors.New("Client key not found") } @@ -359,7 +359,7 @@ func getClientKey(md metadata.MD) ([]byte, error) { } func getCertificateAuthority(md metadata.MD) ([]byte, error) { - caData, ok := md[helm.K8sCertificateAuthority] + caData, ok := md[string(helm.K8sCertificateAuthority)] if !ok { return nil, errors.New("CAcert not found") } @@ -371,7 +371,7 @@ func getCertificateAuthority(md metadata.MD) ([]byte, error) { } func getServerURL(md metadata.MD) (string, error) { - apiserver, ok := md[helm.K8sServer] + apiserver, ok := md[string(helm.K8sServer)] if !ok { return "", errors.New("API server url not found") } From 5d04ec8eeec8b94e13c7bad523c4102187957bba Mon Sep 17 00:00:00 2001 From: sauman Date: Thu, 23 Feb 2017 10:01:58 +0600 Subject: [PATCH 7/8] license headers added --- pkg/helm/types.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/helm/types.go b/pkg/helm/types.go index 8f92aaa6f..63620001f 100644 --- a/pkg/helm/types.go +++ b/pkg/helm/types.go @@ -1,3 +1,19 @@ +/* +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 helm type AuthHeader string From f16ed977bea1540f0f8446a2eee550e0436691fa Mon Sep 17 00:00:00 2001 From: sauman Date: Thu, 23 Feb 2017 10:08:50 +0600 Subject: [PATCH 8/8] Comment added for AuthHeader --- pkg/helm/types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/helm/types.go b/pkg/helm/types.go index 63620001f..ade3e7397 100644 --- a/pkg/helm/types.go +++ b/pkg/helm/types.go @@ -16,6 +16,7 @@ limitations under the License. package helm +//AuthHeader is key type for context type AuthHeader string const (