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) != "" }