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