github.com/uber/kraken@v0.1.4/proxy/cmd/cmd.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package cmd
    15  
    16  import (
    17  	"flag"
    18  
    19  	"fmt"
    20  	"net/http"
    21  
    22  	"github.com/uber/kraken/build-index/tagclient"
    23  	"github.com/uber/kraken/lib/dockerregistry/transfer"
    24  	"github.com/uber/kraken/lib/healthcheck"
    25  	"github.com/uber/kraken/lib/store"
    26  	"github.com/uber/kraken/lib/upstream"
    27  	"github.com/uber/kraken/metrics"
    28  	"github.com/uber/kraken/nginx"
    29  	"github.com/uber/kraken/origin/blobclient"
    30  	"github.com/uber/kraken/proxy/proxyserver"
    31  	"github.com/uber/kraken/proxy/registryoverride"
    32  	"github.com/uber/kraken/utils/configutil"
    33  	"github.com/uber/kraken/utils/flagutil"
    34  	"github.com/uber/kraken/utils/log"
    35  
    36  	"github.com/uber-go/tally"
    37  	"go.uber.org/zap"
    38  )
    39  
    40  // Flags defines proxy CLI flags.
    41  type Flags struct {
    42  	Ports         flagutil.Ints
    43  	ServerPort    int
    44  	ConfigFile    string
    45  	KrakenCluster string
    46  	SecretsFile   string
    47  }
    48  
    49  // ParseFlags parses proxy CLI flags.
    50  func ParseFlags() *Flags {
    51  	var flags Flags
    52  	flag.Var(
    53  		&flags.Ports, "port", "port to listen on (may specify multiple)")
    54  	flag.IntVar(
    55  		&flags.ServerPort, "server-port", 0, "http server port to listen on")
    56  	flag.StringVar(
    57  		&flags.ConfigFile, "config", "", "configuration file path")
    58  	flag.StringVar(
    59  		&flags.KrakenCluster, "cluster", "", "cluster name (e.g. prod01-zone1)")
    60  	flag.StringVar(
    61  		&flags.SecretsFile, "secrets", "", "path to a secrets YAML file to load into configuration")
    62  	flag.Parse()
    63  	return &flags
    64  }
    65  
    66  type options struct {
    67  	config  *Config
    68  	metrics tally.Scope
    69  	logger  *zap.Logger
    70  }
    71  
    72  // Option defines an optional Run parameter.
    73  type Option func(*options)
    74  
    75  // WithConfig ignores config/secrets flags and directly uses the provided config
    76  // struct.
    77  func WithConfig(c Config) Option {
    78  	return func(o *options) { o.config = &c }
    79  }
    80  
    81  // WithMetrics ignores metrics config and directly uses the provided tally scope.
    82  func WithMetrics(s tally.Scope) Option {
    83  	return func(o *options) { o.metrics = s }
    84  }
    85  
    86  // WithLogger ignores logging config and directly uses the provided logger.
    87  func WithLogger(l *zap.Logger) Option {
    88  	return func(o *options) { o.logger = l }
    89  }
    90  
    91  // Run runs the proxy.
    92  func Run(flags *Flags, opts ...Option) {
    93  	if len(flags.Ports) == 0 {
    94  		panic("must specify a port")
    95  	}
    96  
    97  	var overrides options
    98  	for _, o := range opts {
    99  		o(&overrides)
   100  	}
   101  
   102  	var config Config
   103  	if overrides.config != nil {
   104  		config = *overrides.config
   105  	} else {
   106  		if err := configutil.Load(flags.ConfigFile, &config); err != nil {
   107  			panic(err)
   108  		}
   109  		if flags.SecretsFile != "" {
   110  			if err := configutil.Load(flags.SecretsFile, &config); err != nil {
   111  				panic(err)
   112  			}
   113  		}
   114  	}
   115  
   116  	if overrides.logger != nil {
   117  		log.SetGlobalLogger(overrides.logger.Sugar())
   118  	} else {
   119  		zlog := log.ConfigureLogger(config.ZapLogging)
   120  		defer zlog.Sync()
   121  	}
   122  
   123  	stats := overrides.metrics
   124  	if stats == nil {
   125  		s, closer, err := metrics.New(config.Metrics, flags.KrakenCluster)
   126  		if err != nil {
   127  			log.Fatalf("Failed to init metrics: %s", err)
   128  		}
   129  		stats = s
   130  		defer closer.Close()
   131  	}
   132  
   133  	go metrics.EmitVersion(stats)
   134  
   135  	cas, err := store.NewCAStore(config.CAStore, stats)
   136  	if err != nil {
   137  		log.Fatalf("Failed to create store: %s", err)
   138  	}
   139  
   140  	tls, err := config.TLS.BuildClient()
   141  	if err != nil {
   142  		log.Fatalf("Error building client tls config: %s", err)
   143  	}
   144  
   145  	origins, err := config.Origin.Build(upstream.WithHealthCheck(healthcheck.Default(tls)))
   146  	if err != nil {
   147  		log.Fatalf("Error building origin host list: %s", err)
   148  	}
   149  
   150  	r := blobclient.NewClientResolver(blobclient.NewProvider(blobclient.WithTLS(tls)), origins)
   151  	originCluster := blobclient.NewClusterClient(r)
   152  
   153  	buildIndexes, err := config.BuildIndex.Build(upstream.WithHealthCheck(healthcheck.Default(tls)))
   154  	if err != nil {
   155  		log.Fatalf("Error building build-index host list: %s", err)
   156  	}
   157  
   158  	tagClient := tagclient.NewClusterClient(buildIndexes, tls)
   159  
   160  	transferer := transfer.NewReadWriteTransferer(stats, tagClient, originCluster, cas)
   161  
   162  	// Open preheat function only if server-port was defined.
   163  	if flags.ServerPort != 0 {
   164  		server := proxyserver.New(stats, originCluster)
   165  		addr := fmt.Sprintf(":%d", flags.ServerPort)
   166  		log.Infof("Starting http server on %s", addr)
   167  		go func() {
   168  			log.Fatal(http.ListenAndServe(addr, server.Handler()))
   169  		}()
   170  	}
   171  
   172  	registry, err := config.Registry.Build(config.Registry.ReadWriteParameters(transferer, cas, stats))
   173  	if err != nil {
   174  		log.Fatalf("Error creating registry: %s", err)
   175  	}
   176  	go func() {
   177  		log.Info("Starting registry...")
   178  		log.Fatal(registry.ListenAndServe())
   179  	}()
   180  
   181  	ros := registryoverride.NewServer(config.RegistryOverride, tagClient)
   182  	go func() {
   183  		log.Fatal(ros.ListenAndServe())
   184  	}()
   185  
   186  	log.Info("Starting nginx...")
   187  	log.Fatal(nginx.Run(config.Nginx, map[string]interface{}{
   188  		"ports": flags.Ports,
   189  		"registry_server": nginx.GetServer(
   190  			config.Registry.Docker.HTTP.Net, config.Registry.Docker.HTTP.Addr),
   191  		"registry_override_server": nginx.GetServer(
   192  			config.RegistryOverride.Listener.Net, config.RegistryOverride.Listener.Addr)},
   193  		nginx.WithTLS(config.TLS)))
   194  }