github.com/Beeketing/helm@v2.12.1+incompatible/cmd/tiller/tiller.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main // import "k8s.io/helm/cmd/tiller"
    18  
    19  import (
    20  	"crypto/tls"
    21  	"flag"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"log"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"path/filepath"
    29  	"strconv"
    30  	"strings"
    31  	"time"
    32  
    33  	goprom "github.com/grpc-ecosystem/go-grpc-prometheus"
    34  	"google.golang.org/grpc"
    35  	"google.golang.org/grpc/credentials"
    36  	"google.golang.org/grpc/health"
    37  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    38  	"google.golang.org/grpc/keepalive"
    39  
    40  	// Import to initialize client auth plugins.
    41  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    42  
    43  	"k8s.io/helm/pkg/kube"
    44  	"k8s.io/helm/pkg/proto/hapi/services"
    45  	"k8s.io/helm/pkg/storage"
    46  	"k8s.io/helm/pkg/storage/driver"
    47  	"k8s.io/helm/pkg/tiller"
    48  	"k8s.io/helm/pkg/tiller/environment"
    49  	"k8s.io/helm/pkg/tlsutil"
    50  	"k8s.io/helm/pkg/version"
    51  )
    52  
    53  const (
    54  	// tlsEnableEnvVar names the environment variable that enables TLS.
    55  	tlsEnableEnvVar = "TILLER_TLS_ENABLE"
    56  	// tlsVerifyEnvVar names the environment variable that enables
    57  	// TLS, as well as certificate verification of the remote.
    58  	tlsVerifyEnvVar = "TILLER_TLS_VERIFY"
    59  	// tlsCertsEnvVar names the environment variable that points to
    60  	// the directory where Tiller's TLS certificates are located.
    61  	tlsCertsEnvVar = "TILLER_TLS_CERTS"
    62  	// historyMaxEnvVar is the name of the env var for setting max history.
    63  	historyMaxEnvVar = "TILLER_HISTORY_MAX"
    64  
    65  	storageMemory    = "memory"
    66  	storageConfigMap = "configmap"
    67  	storageSecret    = "secret"
    68  
    69  	probeAddr = ":44135"
    70  	traceAddr = ":44136"
    71  
    72  	// defaultMaxHistory sets the maximum number of releases to 0: unlimited
    73  	defaultMaxHistory = 0
    74  )
    75  
    76  var (
    77  	grpcAddr             = flag.String("listen", ":44134", "address:port to listen on")
    78  	enableTracing        = flag.Bool("trace", false, "enable rpc tracing")
    79  	store                = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', or 'secret'")
    80  	remoteReleaseModules = flag.Bool("experimental-release", false, "enable experimental release modules")
    81  	tlsEnable            = flag.Bool("tls", tlsEnableEnvVarDefault(), "enable TLS")
    82  	tlsVerify            = flag.Bool("tls-verify", tlsVerifyEnvVarDefault(), "enable TLS and verify remote certificate")
    83  	keyFile              = flag.String("tls-key", tlsDefaultsFromEnv("tls-key"), "path to TLS private key file")
    84  	certFile             = flag.String("tls-cert", tlsDefaultsFromEnv("tls-cert"), "path to TLS certificate file")
    85  	caCertFile           = flag.String("tls-ca-cert", tlsDefaultsFromEnv("tls-ca-cert"), "trust certificates signed by this CA")
    86  	maxHistory           = flag.Int("history-max", historyMaxFromEnv(), "maximum number of releases kept in release history, with 0 meaning no limit")
    87  	printVersion         = flag.Bool("version", false, "print the version number")
    88  
    89  	// rootServer is the root gRPC server.
    90  	//
    91  	// Each gRPC service registers itself to this server during start().
    92  	rootServer *grpc.Server
    93  
    94  	// env is the default environment.
    95  	//
    96  	// Any changes to env should be done before rootServer.Serve() is called.
    97  	env = environment.New()
    98  
    99  	logger *log.Logger
   100  )
   101  
   102  func main() {
   103  	// TODO: use spf13/cobra for tiller instead of flags
   104  	flag.Parse()
   105  
   106  	if *printVersion {
   107  		fmt.Println(version.GetVersion())
   108  		os.Exit(0)
   109  	}
   110  
   111  	if *enableTracing {
   112  		log.SetFlags(log.Lshortfile)
   113  	}
   114  	logger = newLogger("main")
   115  
   116  	start()
   117  }
   118  
   119  func start() {
   120  
   121  	healthSrv := health.NewServer()
   122  	healthSrv.SetServingStatus("Tiller", healthpb.HealthCheckResponse_NOT_SERVING)
   123  
   124  	clientset, err := kube.New(nil).KubernetesClientSet()
   125  	if err != nil {
   126  		logger.Fatalf("Cannot initialize Kubernetes connection: %s", err)
   127  	}
   128  
   129  	switch *store {
   130  	case storageMemory:
   131  		env.Releases = storage.Init(driver.NewMemory())
   132  	case storageConfigMap:
   133  		cfgmaps := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace()))
   134  		cfgmaps.Log = newLogger("storage/driver").Printf
   135  
   136  		env.Releases = storage.Init(cfgmaps)
   137  		env.Releases.Log = newLogger("storage").Printf
   138  	case storageSecret:
   139  		secrets := driver.NewSecrets(clientset.CoreV1().Secrets(namespace()))
   140  		secrets.Log = newLogger("storage/driver").Printf
   141  
   142  		env.Releases = storage.Init(secrets)
   143  		env.Releases.Log = newLogger("storage").Printf
   144  	}
   145  
   146  	if *maxHistory > 0 {
   147  		env.Releases.MaxHistory = *maxHistory
   148  	}
   149  
   150  	kubeClient := kube.New(nil)
   151  	kubeClient.Log = newLogger("kube").Printf
   152  	env.KubeClient = kubeClient
   153  
   154  	if *tlsEnable || *tlsVerify {
   155  		opts := tlsutil.Options{CertFile: *certFile, KeyFile: *keyFile}
   156  		if *tlsVerify {
   157  			opts.CaCertFile = *caCertFile
   158  		}
   159  	}
   160  
   161  	var opts []grpc.ServerOption
   162  	if *tlsEnable || *tlsVerify {
   163  		cfg, err := tlsutil.ServerConfig(tlsOptions())
   164  		if err != nil {
   165  			logger.Fatalf("Could not create server TLS configuration: %v", err)
   166  		}
   167  		opts = append(opts, grpc.Creds(credentials.NewTLS(cfg)))
   168  	}
   169  
   170  	opts = append(opts, grpc.KeepaliveParams(keepalive.ServerParameters{
   171  		MaxConnectionIdle: 10 * time.Minute,
   172  		// If needed, we can configure the max connection age
   173  	}))
   174  	opts = append(opts, grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
   175  		MinTime: time.Duration(20) * time.Second, // For compatibility with the client keepalive.ClientParameters
   176  	}))
   177  
   178  	rootServer = tiller.NewServer(opts...)
   179  	healthpb.RegisterHealthServer(rootServer, healthSrv)
   180  
   181  	lstn, err := net.Listen("tcp", *grpcAddr)
   182  	if err != nil {
   183  		logger.Fatalf("Server died: %s", err)
   184  	}
   185  
   186  	logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), *tlsEnable || *tlsVerify)
   187  	logger.Printf("GRPC listening on %s", *grpcAddr)
   188  	logger.Printf("Probes listening on %s", probeAddr)
   189  	logger.Printf("Storage driver is %s", env.Releases.Name())
   190  	logger.Printf("Max history per release is %d", *maxHistory)
   191  
   192  	if *enableTracing {
   193  		startTracing(traceAddr)
   194  	}
   195  
   196  	srvErrCh := make(chan error)
   197  	probeErrCh := make(chan error)
   198  	go func() {
   199  		svc := tiller.NewReleaseServer(env, clientset, *remoteReleaseModules)
   200  		svc.Log = newLogger("tiller").Printf
   201  		services.RegisterReleaseServiceServer(rootServer, svc)
   202  		if err := rootServer.Serve(lstn); err != nil {
   203  			srvErrCh <- err
   204  		}
   205  	}()
   206  
   207  	go func() {
   208  		mux := newProbesMux()
   209  
   210  		// Register gRPC server to prometheus to initialized matrix
   211  		goprom.Register(rootServer)
   212  		addPrometheusHandler(mux)
   213  
   214  		if err := http.ListenAndServe(probeAddr, mux); err != nil {
   215  			probeErrCh <- err
   216  		}
   217  	}()
   218  
   219  	healthSrv.SetServingStatus("Tiller", healthpb.HealthCheckResponse_SERVING)
   220  
   221  	select {
   222  	case err := <-srvErrCh:
   223  		logger.Fatalf("Server died: %s", err)
   224  	case err := <-probeErrCh:
   225  		logger.Printf("Probes server died: %s", err)
   226  	}
   227  }
   228  
   229  func newLogger(prefix string) *log.Logger {
   230  	if len(prefix) > 0 {
   231  		prefix = fmt.Sprintf("[%s] ", prefix)
   232  	}
   233  	return log.New(os.Stderr, prefix, log.Flags())
   234  }
   235  
   236  // namespace returns the namespace of tiller
   237  func namespace() string {
   238  	if ns := os.Getenv("TILLER_NAMESPACE"); ns != "" {
   239  		return ns
   240  	}
   241  
   242  	// Fall back to the namespace associated with the service account token, if available
   243  	if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
   244  		if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
   245  			return ns
   246  		}
   247  	}
   248  
   249  	return environment.DefaultTillerNamespace
   250  }
   251  
   252  func tlsOptions() tlsutil.Options {
   253  	opts := tlsutil.Options{CertFile: *certFile, KeyFile: *keyFile}
   254  	if *tlsVerify {
   255  		opts.CaCertFile = *caCertFile
   256  
   257  		// We want to force the client to not only provide a cert, but to
   258  		// provide a cert that we can validate.
   259  		// http://www.bite-code.com/2015/06/25/tls-mutual-auth-in-golang/
   260  		opts.ClientAuth = tls.RequireAndVerifyClientCert
   261  	}
   262  	return opts
   263  }
   264  
   265  func tlsDefaultsFromEnv(name string) (value string) {
   266  	switch certsDir := os.Getenv(tlsCertsEnvVar); name {
   267  	case "tls-key":
   268  		return filepath.Join(certsDir, "tls.key")
   269  	case "tls-cert":
   270  		return filepath.Join(certsDir, "tls.crt")
   271  	case "tls-ca-cert":
   272  		return filepath.Join(certsDir, "ca.crt")
   273  	}
   274  	return ""
   275  }
   276  
   277  func historyMaxFromEnv() int {
   278  	val := os.Getenv(historyMaxEnvVar)
   279  	if val == "" {
   280  		return defaultMaxHistory
   281  	}
   282  	ret, err := strconv.Atoi(val)
   283  	if err != nil {
   284  		log.Printf("Invalid max history %q. Defaulting to 0.", val)
   285  		return defaultMaxHistory
   286  	}
   287  	return ret
   288  }
   289  
   290  func tlsEnableEnvVarDefault() bool { return os.Getenv(tlsEnableEnvVar) != "" }
   291  func tlsVerifyEnvVarDefault() bool { return os.Getenv(tlsVerifyEnvVar) != "" }