github.com/uber/kraken@v0.1.4/agent/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 "fmt" 19 "net/http" 20 "os" 21 "time" 22 23 "github.com/uber/kraken/agent/agentserver" 24 "github.com/uber/kraken/build-index/tagclient" 25 "github.com/uber/kraken/core" 26 "github.com/uber/kraken/lib/dockerdaemon" 27 "github.com/uber/kraken/lib/dockerregistry/transfer" 28 "github.com/uber/kraken/lib/store" 29 "github.com/uber/kraken/lib/torrent/networkevent" 30 "github.com/uber/kraken/lib/torrent/scheduler" 31 "github.com/uber/kraken/metrics" 32 "github.com/uber/kraken/nginx" 33 "github.com/uber/kraken/utils/configutil" 34 "github.com/uber/kraken/utils/log" 35 "github.com/uber/kraken/utils/netutil" 36 37 "github.com/uber-go/tally" 38 "go.uber.org/zap" 39 ) 40 41 // Flags defines agent CLI flags. 42 type Flags struct { 43 PeerIP string 44 PeerPort int 45 AgentServerPort int 46 AgentRegistryPort int 47 ConfigFile string 48 Zone string 49 KrakenCluster string 50 SecretsFile string 51 } 52 53 // ParseFlags parses agent CLI flags. 54 func ParseFlags() *Flags { 55 var flags Flags 56 flag.StringVar( 57 &flags.PeerIP, "peer-ip", "", "ip which peer will announce itself as") 58 flag.IntVar( 59 &flags.PeerPort, "peer-port", 0, "port which peer will announce itself as") 60 flag.IntVar( 61 &flags.AgentServerPort, "agent-server-port", 0, "port which agent server listens on") 62 flag.IntVar( 63 &flags.AgentRegistryPort, "agent-registry-port", 0, "port which agent registry listens on") 64 flag.StringVar( 65 &flags.ConfigFile, "config", "", "configuration file path") 66 flag.StringVar( 67 &flags.Zone, "zone", "", "zone/datacenter name") 68 flag.StringVar( 69 &flags.KrakenCluster, "cluster", "", "cluster name (e.g. prod01-zone1)") 70 flag.StringVar( 71 &flags.SecretsFile, "secrets", "", "path to a secrets YAML file to load into configuration") 72 flag.Parse() 73 return &flags 74 } 75 76 type options struct { 77 config *Config 78 metrics tally.Scope 79 logger *zap.Logger 80 } 81 82 // Option defines an optional Run parameter. 83 type Option func(*options) 84 85 // WithConfig ignores config/secrets flags and directly uses the provided config 86 // struct. 87 func WithConfig(c Config) Option { 88 return func(o *options) { o.config = &c } 89 } 90 91 // WithMetrics ignores metrics config and directly uses the provided tally scope. 92 func WithMetrics(s tally.Scope) Option { 93 return func(o *options) { o.metrics = s } 94 } 95 96 // WithLogger ignores logging config and directly uses the provided logger. 97 func WithLogger(l *zap.Logger) Option { 98 return func(o *options) { o.logger = l } 99 } 100 101 // Run runs the agent. 102 func Run(flags *Flags, opts ...Option) { 103 if flags.PeerPort == 0 { 104 panic("must specify non-zero peer port") 105 } 106 if flags.AgentServerPort == 0 { 107 panic("must specify non-zero agent server port") 108 } 109 if flags.AgentRegistryPort == 0 { 110 panic("must specify non-zero agent registry port") 111 } 112 113 var overrides options 114 for _, o := range opts { 115 o(&overrides) 116 } 117 118 var config Config 119 if overrides.config != nil { 120 config = *overrides.config 121 } else { 122 if err := configutil.Load(flags.ConfigFile, &config); err != nil { 123 panic(err) 124 } 125 if flags.SecretsFile != "" { 126 if err := configutil.Load(flags.SecretsFile, &config); err != nil { 127 panic(err) 128 } 129 } 130 } 131 132 if overrides.logger != nil { 133 log.SetGlobalLogger(overrides.logger.Sugar()) 134 } else { 135 zlog := log.ConfigureLogger(config.ZapLogging) 136 defer zlog.Sync() 137 } 138 139 stats := overrides.metrics 140 if stats == nil { 141 s, closer, err := metrics.New(config.Metrics, flags.KrakenCluster) 142 if err != nil { 143 log.Fatalf("Failed to init metrics: %s", err) 144 } 145 stats = s 146 defer closer.Close() 147 } 148 149 go metrics.EmitVersion(stats) 150 151 if flags.PeerIP == "" { 152 localIP, err := netutil.GetLocalIP() 153 if err != nil { 154 log.Fatalf("Error getting local ip: %s", err) 155 } 156 flags.PeerIP = localIP 157 } 158 159 pctx, err := core.NewPeerContext( 160 config.PeerIDFactory, flags.Zone, flags.KrakenCluster, flags.PeerIP, flags.PeerPort, false) 161 if err != nil { 162 log.Fatalf("Failed to create peer context: %s", err) 163 } 164 165 cads, err := store.NewCADownloadStore(config.CADownloadStore, stats) 166 if err != nil { 167 log.Fatalf("Failed to create local store: %s", err) 168 } 169 170 netevents, err := networkevent.NewProducer(config.NetworkEvent) 171 if err != nil { 172 log.Fatalf("Failed to create network event producer: %s", err) 173 } 174 175 trackers, err := config.Tracker.Build() 176 if err != nil { 177 log.Fatalf("Error building tracker upstream: %s", err) 178 } 179 go trackers.Monitor(nil) 180 181 tls, err := config.TLS.BuildClient() 182 if err != nil { 183 log.Fatalf("Error building client tls config: %s", err) 184 } 185 186 sched, err := scheduler.NewAgentScheduler( 187 config.Scheduler, stats, pctx, cads, netevents, trackers, tls) 188 if err != nil { 189 log.Fatalf("Error creating scheduler: %s", err) 190 } 191 192 buildIndexes, err := config.BuildIndex.Build() 193 if err != nil { 194 log.Fatalf("Error building build-index upstream: %s", err) 195 } 196 197 tagClient := tagclient.NewClusterClient(buildIndexes, tls) 198 199 transferer := transfer.NewReadOnlyTransferer(stats, cads, tagClient, sched) 200 201 registry, err := config.Registry.Build(config.Registry.ReadOnlyParameters(transferer, cads, stats)) 202 if err != nil { 203 log.Fatalf("Failed to init registry: %s", err) 204 } 205 206 registryAddr := fmt.Sprintf("127.0.0.1:%d", flags.AgentRegistryPort) 207 dockerCli, err := dockerdaemon.NewDockerClient(config.DockerDaemon, registryAddr) 208 if err != nil { 209 log.Fatalf("failed to init docker client for preload: %s", err) 210 } 211 212 agentServer := agentserver.New( 213 config.AgentServer, stats, cads, sched, tagClient, dockerCli) 214 addr := fmt.Sprintf(":%d", flags.AgentServerPort) 215 log.Infof("Starting agent server on %s", addr) 216 go func() { 217 log.Fatal(http.ListenAndServe(addr, agentServer.Handler())) 218 }() 219 220 log.Info("Starting registry...") 221 go func() { 222 log.Fatal(registry.ListenAndServe()) 223 }() 224 225 go heartbeat(stats) 226 227 // Wipe log files created by the old nginx process which ran as root. 228 // TODO(codyg): Swap these with the v2 log files once they are deleted. 229 for _, name := range []string{ 230 "/var/log/kraken/kraken-agent/nginx-access.log", 231 "/var/log/kraken/kraken-agent/nginx-error.log", 232 } { 233 if err := os.Remove(name); err != nil && !os.IsNotExist(err) { 234 log.Warnf("Could not remove old root-owned nginx log: %s", err) 235 } 236 } 237 238 log.Fatal(nginx.Run(config.Nginx, map[string]interface{}{ 239 "allowed_cidrs": config.AllowedCidrs, 240 "port": flags.AgentRegistryPort, 241 "registry_server": nginx.GetServer( 242 config.Registry.Docker.HTTP.Net, config.Registry.Docker.HTTP.Addr), 243 "registry_backup": config.RegistryBackup}, 244 nginx.WithTLS(config.TLS))) 245 } 246 247 // heartbeat periodically emits a counter metric which allows us to monitor the 248 // number of active agents. 249 func heartbeat(stats tally.Scope) { 250 for { 251 stats.Counter("heartbeat").Inc(1) 252 time.Sleep(10 * time.Second) 253 } 254 }