github.com/pachyderm/pachyderm@v1.13.4/src/server/cmd/pachd/main.go (about) 1 package main 2 3 import ( 4 gotls "crypto/tls" 5 "fmt" 6 "net" 7 "net/http" 8 "os" 9 "path" 10 "runtime/debug" 11 "runtime/pprof" 12 "strconv" 13 14 adminclient "github.com/pachyderm/pachyderm/src/client/admin" 15 authclient "github.com/pachyderm/pachyderm/src/client/auth" 16 debugclient "github.com/pachyderm/pachyderm/src/client/debug" 17 eprsclient "github.com/pachyderm/pachyderm/src/client/enterprise" 18 healthclient "github.com/pachyderm/pachyderm/src/client/health" 19 pfsclient "github.com/pachyderm/pachyderm/src/client/pfs" 20 "github.com/pachyderm/pachyderm/src/client/pkg/discovery" 21 "github.com/pachyderm/pachyderm/src/client/pkg/errors" 22 "github.com/pachyderm/pachyderm/src/client/pkg/grpcutil" 23 "github.com/pachyderm/pachyderm/src/client/pkg/shard" 24 "github.com/pachyderm/pachyderm/src/client/pkg/tracing" 25 ppsclient "github.com/pachyderm/pachyderm/src/client/pps" 26 transactionclient "github.com/pachyderm/pachyderm/src/client/transaction" 27 "github.com/pachyderm/pachyderm/src/client/version" 28 "github.com/pachyderm/pachyderm/src/client/version/versionpb" 29 adminserver "github.com/pachyderm/pachyderm/src/server/admin/server" 30 auth_iface "github.com/pachyderm/pachyderm/src/server/auth" 31 authserver "github.com/pachyderm/pachyderm/src/server/auth/server" 32 debugserver "github.com/pachyderm/pachyderm/src/server/debug/server" 33 eprsserver "github.com/pachyderm/pachyderm/src/server/enterprise/server" 34 "github.com/pachyderm/pachyderm/src/server/health" 35 pach_http "github.com/pachyderm/pachyderm/src/server/http" 36 pfs_iface "github.com/pachyderm/pachyderm/src/server/pfs" 37 "github.com/pachyderm/pachyderm/src/server/pfs/s3" 38 pfs_server "github.com/pachyderm/pachyderm/src/server/pfs/server" 39 cache_pb "github.com/pachyderm/pachyderm/src/server/pkg/cache/groupcachepb" 40 cache_server "github.com/pachyderm/pachyderm/src/server/pkg/cache/server" 41 "github.com/pachyderm/pachyderm/src/server/pkg/cmdutil" 42 col "github.com/pachyderm/pachyderm/src/server/pkg/collection" 43 "github.com/pachyderm/pachyderm/src/server/pkg/deploy/assets" 44 "github.com/pachyderm/pachyderm/src/server/pkg/hashtree" 45 logutil "github.com/pachyderm/pachyderm/src/server/pkg/log" 46 "github.com/pachyderm/pachyderm/src/server/pkg/metrics" 47 "github.com/pachyderm/pachyderm/src/server/pkg/netutil" 48 "github.com/pachyderm/pachyderm/src/server/pkg/serviceenv" 49 txnenv "github.com/pachyderm/pachyderm/src/server/pkg/transactionenv" 50 "github.com/pachyderm/pachyderm/src/server/pkg/uuid" 51 pps_iface "github.com/pachyderm/pachyderm/src/server/pps" 52 pps_server "github.com/pachyderm/pachyderm/src/server/pps/server" 53 "github.com/pachyderm/pachyderm/src/server/pps/server/githook" 54 txnserver "github.com/pachyderm/pachyderm/src/server/transaction/server" 55 "go.uber.org/automaxprocs/maxprocs" 56 57 etcd "github.com/coreos/etcd/clientv3" 58 units "github.com/docker/go-units" 59 "github.com/pachyderm/pachyderm/src/client/pkg/tls" 60 "github.com/prometheus/client_golang/prometheus/promhttp" 61 log "github.com/sirupsen/logrus" 62 flag "github.com/spf13/pflag" 63 "golang.org/x/net/context" 64 "google.golang.org/grpc" 65 ) 66 67 const ( 68 defaultTreeCacheSize = 8 69 ) 70 71 var mode string 72 var readiness bool 73 74 func init() { 75 flag.StringVar(&mode, "mode", "full", "Pachd currently supports two modes: full and sidecar. The former includes everything you need in a full pachd node. The latter runs only PFS, the Auth service, and a stripped-down version of PPS.") 76 flag.BoolVar(&readiness, "readiness", false, "Run readiness check.") 77 flag.Parse() 78 } 79 80 func main() { 81 log.SetFormatter(logutil.FormatterFunc(logutil.Pretty)) 82 maxprocs.Set(maxprocs.Logger(log.Printf)) 83 84 switch { 85 case readiness: 86 cmdutil.Main(doReadinessCheck, &serviceenv.PachdFullConfiguration{}) 87 case mode == "full": 88 cmdutil.Main(doFullMode, &serviceenv.PachdFullConfiguration{}) 89 case mode == "sidecar": 90 cmdutil.Main(doSidecarMode, &serviceenv.PachdFullConfiguration{}) 91 default: 92 fmt.Printf("unrecognized mode: %s\n", mode) 93 } 94 } 95 96 func doReadinessCheck(config interface{}) error { 97 env := serviceenv.InitPachOnlyEnv(serviceenv.NewConfiguration(config)) 98 return env.GetPachClient(context.Background()).Health() 99 } 100 101 func doSidecarMode(config interface{}) (retErr error) { 102 defer func() { 103 if retErr != nil { 104 pprof.Lookup("goroutine").WriteTo(os.Stderr, 2) 105 } 106 }() 107 switch logLevel := os.Getenv("LOG_LEVEL"); logLevel { 108 case "debug": 109 log.SetLevel(log.DebugLevel) 110 case "error": 111 log.SetLevel(log.ErrorLevel) 112 case "info", "": 113 log.SetLevel(log.InfoLevel) 114 default: 115 log.Errorf("Unrecognized log level %s, falling back to default of \"info\"", logLevel) 116 log.SetLevel(log.InfoLevel) 117 } 118 // must run InstallJaegerTracer before InitWithKube (otherwise InitWithKube 119 // may create a pach client before tracing is active, not install the Jaeger 120 // gRPC interceptor in the client, and not propagate traces) 121 if endpoint := tracing.InstallJaegerTracerFromEnv(); endpoint != "" { 122 log.Printf("connecting to Jaeger at %q", endpoint) 123 } else { 124 log.Printf("no Jaeger collector found (JAEGER_COLLECTOR_SERVICE_HOST not set)") 125 } 126 env := serviceenv.InitWithKube(serviceenv.NewConfiguration(config)) 127 debug.SetGCPercent(env.GCPercent) 128 if env.EtcdPrefix == "" { 129 env.EtcdPrefix = col.DefaultPrefix 130 } 131 clusterID, err := getClusterID(env.GetEtcdClient()) 132 if err != nil { 133 return errors.Wrapf(err, "getClusterID") 134 } 135 var reporter *metrics.Reporter 136 if env.Metrics { 137 reporter = metrics.NewReporter(clusterID, env) 138 } 139 pfsCacheSize, err := strconv.Atoi(env.PFSCacheSize) 140 if err != nil { 141 return errors.Wrapf(err, "atoi") 142 } 143 if pfsCacheSize == 0 { 144 pfsCacheSize = defaultTreeCacheSize 145 } 146 treeCache, err := hashtree.NewCache(pfsCacheSize) 147 if err != nil { 148 return errors.Wrapf(err, "lru.New") 149 } 150 server, err := grpcutil.NewServer(context.Background(), false) 151 if err != nil { 152 return err 153 } 154 txnEnv := &txnenv.TransactionEnv{} 155 var blockAPIServer pfs_server.BlockAPIServer 156 if !env.StorageV2 { 157 blockCacheBytes, err := units.RAMInBytes(env.BlockCacheBytes) 158 if err != nil { 159 return errors.Wrapf(err, "units.RAMInBytes") 160 } 161 if err := logGRPCServerSetup("Block API", func() error { 162 var err error 163 blockAPIServer, err = pfs_server.NewBlockAPIServer(env.StorageRoot, blockCacheBytes, env.StorageBackend, net.JoinHostPort(env.EtcdHost, env.EtcdPort), false) 164 if err != nil { 165 return err 166 } 167 pfsclient.RegisterObjectAPIServer(server.Server, blockAPIServer) 168 return nil 169 }); err != nil { 170 return err 171 } 172 } 173 memoryRequestBytes, err := units.RAMInBytes(env.MemoryRequest) 174 if err != nil { 175 return err 176 } 177 var pfsAPIServer pfs_iface.APIServer 178 if err := logGRPCServerSetup("PFS API", func() error { 179 pfsAPIServer, err = pfs_server.NewAPIServer( 180 env, 181 txnEnv, 182 path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), 183 treeCache, 184 env.StorageRoot, 185 memoryRequestBytes, 186 blockAPIServer, 187 ) 188 if err != nil { 189 return err 190 } 191 pfsclient.RegisterAPIServer(server.Server, pfsAPIServer) 192 env.SetPfsServer(pfsAPIServer) 193 return nil 194 }); err != nil { 195 return err 196 } 197 var ppsAPIServer pps_iface.APIServer 198 if err := logGRPCServerSetup("PPS API", func() error { 199 ppsAPIServer, err = pps_server.NewSidecarAPIServer( 200 env, 201 txnEnv, 202 path.Join(env.EtcdPrefix, env.PPSEtcdPrefix), 203 env.Namespace, 204 env.IAMRole, 205 reporter, 206 env.PPSWorkerPort, 207 env.HTTPPort, 208 env.PeerPort, 209 ) 210 if err != nil { 211 return err 212 } 213 ppsclient.RegisterAPIServer(server.Server, ppsAPIServer) 214 env.SetPpsServer(ppsAPIServer) 215 return nil 216 }); err != nil { 217 return err 218 } 219 var authAPIServer auth_iface.APIServer 220 if err := logGRPCServerSetup("Auth API", func() error { 221 authAPIServer, err = authserver.NewAuthServer( 222 env, 223 txnEnv, 224 path.Join(env.EtcdPrefix, env.AuthEtcdPrefix), 225 false, 226 false, 227 ) 228 if err != nil { 229 return err 230 } 231 authclient.RegisterAPIServer(server.Server, authAPIServer) 232 env.SetAuthServer(authAPIServer) 233 return nil 234 }); err != nil { 235 return err 236 } 237 var transactionAPIServer txnserver.APIServer 238 if err := logGRPCServerSetup("Transaction API", func() error { 239 transactionAPIServer, err = txnserver.NewAPIServer( 240 env, 241 txnEnv, 242 path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), 243 ) 244 if err != nil { 245 return err 246 } 247 transactionclient.RegisterAPIServer(server.Server, transactionAPIServer) 248 return nil 249 }); err != nil { 250 return err 251 } 252 if err := logGRPCServerSetup("Enterprise API", func() error { 253 enterpriseAPIServer, err := eprsserver.NewEnterpriseServer( 254 env, path.Join(env.EtcdPrefix, env.EnterpriseEtcdPrefix)) 255 if err != nil { 256 return err 257 } 258 eprsclient.RegisterAPIServer(server.Server, enterpriseAPIServer) 259 env.SetEnterpriseServer(enterpriseAPIServer) 260 return nil 261 }); err != nil { 262 return err 263 } 264 if err := logGRPCServerSetup("Health", func() error { 265 healthclient.RegisterHealthServer(server.Server, health.NewHealthServer()) 266 return nil 267 }); err != nil { 268 return err 269 } 270 if err := logGRPCServerSetup("Debug", func() error { 271 debugclient.RegisterDebugServer(server.Server, debugserver.NewDebugServer( 272 env, 273 env.PachdPodName, 274 nil, 275 )) 276 return nil 277 }); err != nil { 278 return err 279 } 280 txnEnv.Initialize(env, transactionAPIServer, authAPIServer, pfsAPIServer, ppsAPIServer) 281 // The sidecar only needs to serve traffic on the peer port, as it only serves 282 // traffic from the user container (the worker binary and occasionally user 283 // pipelines) 284 if _, err := server.ListenTCP("", env.PeerPort); err != nil { 285 return err 286 } 287 return server.Wait() 288 } 289 290 func doFullMode(config interface{}) (retErr error) { 291 defer func() { 292 if retErr != nil { 293 pprof.Lookup("goroutine").WriteTo(os.Stderr, 2) 294 } 295 }() 296 switch logLevel := os.Getenv("LOG_LEVEL"); logLevel { 297 case "debug": 298 log.SetLevel(log.DebugLevel) 299 case "error": 300 log.SetLevel(log.ErrorLevel) 301 case "info", "": 302 log.SetLevel(log.InfoLevel) 303 default: 304 log.Errorf("Unrecognized log level %s, falling back to default of \"info\"", logLevel) 305 log.SetLevel(log.InfoLevel) 306 } 307 // must run InstallJaegerTracer before InitWithKube/pach client initialization 308 if endpoint := tracing.InstallJaegerTracerFromEnv(); endpoint != "" { 309 log.Printf("connecting to Jaeger at %q", endpoint) 310 } else { 311 log.Printf("no Jaeger collector found (JAEGER_COLLECTOR_SERVICE_HOST not set)") 312 } 313 env := serviceenv.InitWithKube(serviceenv.NewConfiguration(config)) 314 debug.SetGCPercent(env.GCPercent) 315 if env.EtcdPrefix == "" { 316 env.EtcdPrefix = col.DefaultPrefix 317 } 318 clusterID, err := getClusterID(env.GetEtcdClient()) 319 if err != nil { 320 return errors.Wrapf(err, "getClusterID") 321 } 322 var reporter *metrics.Reporter 323 if env.Metrics { 324 reporter = metrics.NewReporter(clusterID, env) 325 } 326 // (bryce) Do we have to use etcd client v2 here for sharder? Might want to re-visit this later. 327 etcdAddress := fmt.Sprintf("http://%s", net.JoinHostPort(env.EtcdHost, env.EtcdPort)) 328 etcdClientV2 := getEtcdClient(etcdAddress) 329 ip, err := netutil.ExternalIP() 330 if err != nil { 331 return errors.Wrapf(err, "error getting pachd external ip") 332 } 333 address := net.JoinHostPort(ip, fmt.Sprintf("%d", env.PeerPort)) 334 sharder := shard.NewSharder( 335 etcdClientV2, 336 env.NumShards, 337 env.Namespace, 338 ) 339 go func() { 340 if err := sharder.AssignRoles(address); err != nil { 341 log.Printf("error from sharder.AssignRoles: %s", grpcutil.ScrubGRPC(err)) 342 } 343 }() 344 router := shard.NewRouter( 345 sharder, 346 grpcutil.NewDialer( 347 grpc.WithInsecure(), 348 ), 349 address, 350 ) 351 pfsCacheSize, err := strconv.Atoi(env.PFSCacheSize) 352 if err != nil { 353 return errors.Wrapf(err, "atoi") 354 } 355 if pfsCacheSize == 0 { 356 pfsCacheSize = defaultTreeCacheSize 357 } 358 treeCache, err := hashtree.NewCache(pfsCacheSize) 359 if err != nil { 360 return errors.Wrapf(err, "lru.New") 361 } 362 kubeNamespace := env.Namespace 363 requireNoncriticalServers := !env.RequireCriticalServersOnly 364 // Setup internal block gRPC server first so we can reference it from both the external and internal PFS gRPC servers. 365 internalServer, err := grpcutil.NewServer(context.Background(), false) 366 if err != nil { 367 return err 368 } 369 var blockAPIServer pfs_server.BlockAPIServer 370 if !env.StorageV2 { 371 blockCacheBytes, err := units.RAMInBytes(env.BlockCacheBytes) 372 if err != nil { 373 return errors.Wrapf(err, "units.RAMInBytes") 374 } 375 if err := logGRPCServerSetup("Block API", func() error { 376 var err error 377 blockAPIServer, err = pfs_server.NewBlockAPIServer( 378 env.StorageRoot, blockCacheBytes, env.StorageBackend, etcdAddress, false) 379 if err != nil { 380 return err 381 } 382 pfsclient.RegisterObjectAPIServer(internalServer.Server, blockAPIServer) 383 return nil 384 }); err != nil { 385 return err 386 } 387 } 388 // Setup External Pachd GRPC Server. 389 externalServer, err := grpcutil.NewServer(context.Background(), true) 390 if err != nil { 391 return err 392 } 393 if err := logGRPCServerSetup("External Pachd", func() error { 394 txnEnv := &txnenv.TransactionEnv{} 395 memoryRequestBytes, err := units.RAMInBytes(env.MemoryRequest) 396 if err != nil { 397 return err 398 } 399 var pfsAPIServer pfs_iface.APIServer 400 if err := logGRPCServerSetup("PFS API", func() error { 401 pfsAPIServer, err = pfs_server.NewAPIServer(env, txnEnv, path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), treeCache, env.StorageRoot, memoryRequestBytes, blockAPIServer) 402 if err != nil { 403 return err 404 } 405 pfsclient.RegisterAPIServer(externalServer.Server, pfsAPIServer) 406 return nil 407 }); err != nil { 408 return err 409 } 410 var ppsAPIServer pps_iface.APIServer 411 if err := logGRPCServerSetup("PPS API", func() error { 412 ppsAPIServer, err = pps_server.NewAPIServer( 413 env, 414 txnEnv, 415 path.Join(env.EtcdPrefix, env.PPSEtcdPrefix), 416 kubeNamespace, 417 env.WorkerImage, 418 env.WorkerSidecarImage, 419 env.WorkerImagePullPolicy, 420 env.StorageRoot, 421 env.StorageBackend, 422 env.StorageHostPath, 423 env.CacheRoot, 424 env.IAMRole, 425 env.ImagePullSecret, 426 env.NoExposeDockerSocket, 427 reporter, 428 env.WorkerUsesRoot, 429 env.PPSWorkerPort, 430 env.Port, 431 env.HTTPPort, 432 env.PeerPort, 433 env.GCPercent, 434 ) 435 if err != nil { 436 return err 437 } 438 ppsclient.RegisterAPIServer(externalServer.Server, ppsAPIServer) 439 return nil 440 }); err != nil { 441 return err 442 } 443 if !env.StorageV2 { 444 if env.ExposeObjectAPI { 445 if err := logGRPCServerSetup("Block API", func() error { 446 // Generally the object API should not be exposed publicly, but 447 // TestGarbageCollection uses it and it may help with debugging 448 blockAPIServer, err := pfs_server.NewBlockAPIServer( 449 env.StorageRoot, 450 0 /* = blockCacheBytes (disable cache) */, env.StorageBackend, 451 etcdAddress, 452 true /* duplicate */) 453 if err != nil { 454 return err 455 } 456 pfsclient.RegisterObjectAPIServer(externalServer.Server, blockAPIServer) 457 return nil 458 }); err != nil { 459 return err 460 } 461 } 462 } 463 var authAPIServer auth_iface.APIServer 464 if err := logGRPCServerSetup("Auth API", func() error { 465 authAPIServer, err = authserver.NewAuthServer( 466 env, txnEnv, path.Join(env.EtcdPrefix, env.AuthEtcdPrefix), true, requireNoncriticalServers) 467 if err != nil { 468 return err 469 } 470 authclient.RegisterAPIServer(externalServer.Server, authAPIServer) 471 return nil 472 }); err != nil { 473 return err 474 } 475 var transactionAPIServer txnserver.APIServer 476 if err := logGRPCServerSetup("Transaction API", func() error { 477 transactionAPIServer, err = txnserver.NewAPIServer( 478 env, 479 txnEnv, 480 path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), 481 ) 482 if err != nil { 483 return err 484 } 485 transactionclient.RegisterAPIServer(externalServer.Server, transactionAPIServer) 486 return nil 487 }); err != nil { 488 return err 489 } 490 if err := logGRPCServerSetup("Enterprise API", func() error { 491 enterpriseAPIServer, err := eprsserver.NewEnterpriseServer( 492 env, path.Join(env.EtcdPrefix, env.EnterpriseEtcdPrefix)) 493 if err != nil { 494 return err 495 } 496 eprsclient.RegisterAPIServer(externalServer.Server, enterpriseAPIServer) 497 return nil 498 }); err != nil { 499 return err 500 } 501 if err := logGRPCServerSetup("Admin API", func() error { 502 adminclient.RegisterAPIServer(externalServer.Server, adminserver.NewAPIServer(address, env.StorageRoot, &adminclient.ClusterInfo{ 503 ID: clusterID, 504 DeploymentID: env.DeploymentID, 505 })) 506 return nil 507 }); err != nil { 508 return err 509 } 510 healthServer := health.NewHealthServer() 511 if err := logGRPCServerSetup("Health", func() error { 512 healthclient.RegisterHealthServer(externalServer.Server, healthServer) 513 return nil 514 }); err != nil { 515 return err 516 } 517 if err := logGRPCServerSetup("Version API", func() error { 518 versionpb.RegisterAPIServer(externalServer.Server, version.NewAPIServer(version.Version, version.APIServerOptions{})) 519 return nil 520 }); err != nil { 521 return err 522 } 523 if err := logGRPCServerSetup("Debug", func() error { 524 debugclient.RegisterDebugServer(externalServer.Server, debugserver.NewDebugServer( 525 env, 526 env.PachdPodName, 527 nil, 528 )) 529 return nil 530 }); err != nil { 531 return err 532 } 533 txnEnv.Initialize(env, transactionAPIServer, authAPIServer, pfsAPIServer, ppsAPIServer) 534 if _, err := externalServer.ListenTCP("", env.Port); err != nil { 535 return err 536 } 537 healthServer.Ready() 538 return nil 539 }); err != nil { 540 return err 541 } 542 // Setup Internal Pachd GRPC Server. 543 if err := logGRPCServerSetup("Internal Pachd", func() error { 544 txnEnv := &txnenv.TransactionEnv{} 545 cacheServer := cache_server.NewCacheServer(router, env.NumShards) 546 go func() { 547 if err := sharder.RegisterFrontends(address, []shard.Frontend{cacheServer}); err != nil { 548 log.Printf("error from sharder.RegisterFrontend %s", grpcutil.ScrubGRPC(err)) 549 } 550 }() 551 go func() { 552 if err := sharder.Register(address, []shard.Server{cacheServer}); err != nil { 553 log.Printf("error from sharder.Register %s", grpcutil.ScrubGRPC(err)) 554 } 555 }() 556 cache_pb.RegisterGroupCacheServer(internalServer.Server, cacheServer) 557 memoryRequestBytes, err := units.RAMInBytes(env.MemoryRequest) 558 if err != nil { 559 return err 560 } 561 var pfsAPIServer pfs_iface.APIServer 562 if err := logGRPCServerSetup("PFS API", func() error { 563 pfsAPIServer, err = pfs_server.NewAPIServer( 564 env, 565 txnEnv, 566 path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), 567 treeCache, 568 env.StorageRoot, 569 memoryRequestBytes, 570 blockAPIServer, 571 ) 572 if err != nil { 573 return err 574 } 575 pfsclient.RegisterAPIServer(internalServer.Server, pfsAPIServer) 576 env.SetPfsServer(pfsAPIServer) 577 return nil 578 }); err != nil { 579 return err 580 } 581 var ppsAPIServer pps_iface.APIServer 582 if err := logGRPCServerSetup("PPS API", func() error { 583 ppsAPIServer, err = pps_server.NewAPIServer( 584 env, 585 txnEnv, 586 path.Join(env.EtcdPrefix, env.PPSEtcdPrefix), 587 kubeNamespace, 588 env.WorkerImage, 589 env.WorkerSidecarImage, 590 env.WorkerImagePullPolicy, 591 env.StorageRoot, 592 env.StorageBackend, 593 env.StorageHostPath, 594 env.CacheRoot, 595 env.IAMRole, 596 env.ImagePullSecret, 597 env.NoExposeDockerSocket, 598 reporter, 599 env.WorkerUsesRoot, 600 env.PPSWorkerPort, 601 env.Port, 602 env.HTTPPort, 603 env.PeerPort, 604 env.GCPercent, 605 ) 606 if err != nil { 607 return err 608 } 609 ppsclient.RegisterAPIServer(internalServer.Server, ppsAPIServer) 610 env.SetPpsServer(ppsAPIServer) 611 return nil 612 }); err != nil { 613 return err 614 } 615 var authAPIServer auth_iface.APIServer 616 if err := logGRPCServerSetup("Auth API", func() error { 617 authAPIServer, err = authserver.NewAuthServer( 618 env, 619 txnEnv, 620 path.Join(env.EtcdPrefix, env.AuthEtcdPrefix), 621 false, 622 requireNoncriticalServers, 623 ) 624 if err != nil { 625 return err 626 } 627 authclient.RegisterAPIServer(internalServer.Server, authAPIServer) 628 env.SetAuthServer(authAPIServer) 629 return nil 630 }); err != nil { 631 return err 632 } 633 var transactionAPIServer txnserver.APIServer 634 if err := logGRPCServerSetup("Transaction API", func() error { 635 transactionAPIServer, err = txnserver.NewAPIServer( 636 env, 637 txnEnv, 638 path.Join(env.EtcdPrefix, env.PFSEtcdPrefix), 639 ) 640 if err != nil { 641 return err 642 } 643 transactionclient.RegisterAPIServer(internalServer.Server, transactionAPIServer) 644 return nil 645 }); err != nil { 646 return err 647 } 648 if err := logGRPCServerSetup("Enterprise API", func() error { 649 enterpriseAPIServer, err := eprsserver.NewEnterpriseServer( 650 env, path.Join(env.EtcdPrefix, env.EnterpriseEtcdPrefix)) 651 if err != nil { 652 return err 653 } 654 eprsclient.RegisterAPIServer(internalServer.Server, enterpriseAPIServer) 655 env.SetEnterpriseServer(enterpriseAPIServer) 656 return nil 657 }); err != nil { 658 return err 659 } 660 healthServer := health.NewHealthServer() 661 if err := logGRPCServerSetup("Health", func() error { 662 healthclient.RegisterHealthServer(internalServer.Server, healthServer) 663 return nil 664 }); err != nil { 665 return err 666 } 667 if err := logGRPCServerSetup("Version API", func() error { 668 versionpb.RegisterAPIServer(internalServer.Server, version.NewAPIServer(version.Version, version.APIServerOptions{})) 669 return nil 670 }); err != nil { 671 return err 672 } 673 if err := logGRPCServerSetup("Admin API", func() error { 674 adminclient.RegisterAPIServer(internalServer.Server, adminserver.NewAPIServer(address, env.StorageRoot, &adminclient.ClusterInfo{ 675 ID: clusterID, 676 DeploymentID: env.DeploymentID, 677 })) 678 return nil 679 }); err != nil { 680 return err 681 } 682 txnEnv.Initialize(env, transactionAPIServer, authAPIServer, pfsAPIServer, ppsAPIServer) 683 if _, err := internalServer.ListenTCP("", env.PeerPort); err != nil { 684 return err 685 } 686 healthServer.Ready() 687 return nil 688 }); err != nil { 689 return err 690 } 691 // Create the goroutines for the servers. 692 // Any server error is considered critical and will cause Pachd to exit. 693 // The first server that errors will have its error message logged. 694 errChan := make(chan error, 1) 695 go waitForError("External Pachd GRPC Server", errChan, true, func() error { 696 return externalServer.Wait() 697 }) 698 go waitForError("Internal Pachd GRPC Server", errChan, true, func() error { 699 return internalServer.Wait() 700 }) 701 go waitForError("HTTP Server", errChan, requireNoncriticalServers, func() error { 702 httpServer, err := pach_http.NewHTTPServer(address) 703 if err != nil { 704 return err 705 } 706 server := http.Server{ 707 Addr: fmt.Sprintf(":%v", env.HTTPPort), 708 Handler: httpServer, 709 } 710 711 certPath, keyPath, err := tls.GetCertPaths() 712 if err != nil { 713 log.Warnf("pfs-over-HTTP - TLS disabled: %v", err) 714 return server.ListenAndServe() 715 } 716 717 cLoader := tls.NewCertLoader(certPath, keyPath, tls.CertCheckFrequency) 718 err = cLoader.LoadAndStart() 719 if err != nil { 720 return errors.Wrapf(err, "couldn't load TLS cert for pfs-over-http: %v", err) 721 } 722 723 server.TLSConfig = &gotls.Config{GetCertificate: cLoader.GetCertificate} 724 725 return server.ListenAndServeTLS(certPath, keyPath) 726 }) 727 go waitForError("Githook Server", errChan, requireNoncriticalServers, func() error { 728 return githook.RunGitHookServer(address, etcdAddress, path.Join(env.EtcdPrefix, env.PPSEtcdPrefix)) 729 }) 730 go waitForError("S3 Server", errChan, requireNoncriticalServers, func() error { 731 server, err := s3.Server(env, s3.NewMasterDriver()) 732 if err != nil { 733 return err 734 } 735 certPath, keyPath, err := tls.GetCertPaths() 736 if err != nil { 737 log.Warnf("s3gateway TLS disabled: %v", err) 738 return server.ListenAndServe() 739 } 740 cLoader := tls.NewCertLoader(certPath, keyPath, tls.CertCheckFrequency) 741 // Read TLS cert and key 742 err = cLoader.LoadAndStart() 743 if err != nil { 744 return errors.Wrapf(err, "couldn't load TLS cert for s3gateway: %v", err) 745 } 746 server.TLSConfig = &gotls.Config{GetCertificate: cLoader.GetCertificate} 747 return server.ListenAndServeTLS(certPath, keyPath) 748 }) 749 go waitForError("Prometheus Server", errChan, requireNoncriticalServers, func() error { 750 http.Handle("/metrics", promhttp.Handler()) 751 return http.ListenAndServe(fmt.Sprintf(":%v", assets.PrometheusPort), nil) 752 }) 753 return <-errChan 754 } 755 756 func getEtcdClient(etcdAddress string) discovery.Client { 757 return discovery.NewEtcdClient(etcdAddress) 758 } 759 760 const clusterIDKey = "cluster-id" 761 762 func getClusterID(client *etcd.Client) (string, error) { 763 resp, err := client.Get(context.Background(), 764 clusterIDKey) 765 766 // if it's a key not found error then we create the key 767 if resp.Count == 0 { 768 // This might error if it races with another pachd trying to set the 769 // cluster id so we ignore the error. 770 client.Put(context.Background(), clusterIDKey, uuid.NewWithoutDashes()) 771 } else if err != nil { 772 return "", err 773 } else { 774 // We expect there to only be one value for this key 775 id := string(resp.Kvs[0].Value) 776 return id, nil 777 } 778 779 return getClusterID(client) 780 } 781 782 func logGRPCServerSetup(name string, f func() error) (retErr error) { 783 log.Printf("started setting up %v GRPC Server", name) 784 defer func() { 785 if retErr != nil { 786 retErr = errors.Wrapf(retErr, "error setting up %v GRPC Server", name) 787 } else { 788 log.Printf("finished setting up %v GRPC Server", name) 789 } 790 }() 791 return f() 792 } 793 794 func waitForError(name string, errChan chan error, required bool, f func() error) { 795 if err := f(); !errors.Is(err, http.ErrServerClosed) { 796 if !required { 797 log.Errorf("error setting up and/or running %v: %v", name, err) 798 } else { 799 errChan <- errors.Wrapf(err, "error setting up and/or running %v (use --require-critical-servers-only deploy flag to ignore errors from noncritical servers)", name) 800 } 801 } 802 }