github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/main.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "time" 15 16 "github.com/juju/cmd" 17 "github.com/juju/errors" 18 "github.com/juju/loggo" 19 "github.com/juju/utils/exec" 20 21 jujucmd "github.com/juju/juju/cmd" 22 agentcmd "github.com/juju/juju/cmd/jujud/agent" 23 "github.com/juju/juju/cmd/jujud/dumplogs" 24 "github.com/juju/juju/cmd/pprof" 25 components "github.com/juju/juju/component/all" 26 "github.com/juju/juju/juju/names" 27 "github.com/juju/juju/juju/sockets" 28 // Import the providers. 29 _ "github.com/juju/juju/provider/all" 30 "github.com/juju/juju/worker/logsender" 31 "github.com/juju/juju/worker/uniter/runner/jujuc" 32 ) 33 34 var log = loggo.GetLogger("juju.cmd.jujud") 35 36 func init() { 37 if err := components.RegisterForServer(); err != nil { 38 log.Criticalf("unabled to register server components: %v", err) 39 os.Exit(1) 40 } 41 } 42 43 var jujudDoc = ` 44 juju provides easy, intelligent service orchestration on top of models 45 such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju. 46 47 https://juju.ubuntu.com/ 48 49 The jujud command can also forward invocations over RPC for execution by the 50 juju unit agent. When used in this way, it expects to be called via a symlink 51 named for the desired remote command, and expects JUJU_AGENT_SOCKET and 52 JUJU_CONTEXT_ID be set in its model. 53 ` 54 55 const ( 56 // exit_err is the value that is returned when the user has run juju in an invalid way. 57 exit_err = 2 58 // exit_panic is the value that is returned when we exit due to an unhandled panic. 59 exit_panic = 3 60 ) 61 62 func getenv(name string) (string, error) { 63 value := os.Getenv(name) 64 if value == "" { 65 return "", fmt.Errorf("%s not set", name) 66 } 67 return value, nil 68 } 69 70 func getwd() (string, error) { 71 dir, err := os.Getwd() 72 if err != nil { 73 return "", err 74 } 75 abs, err := filepath.Abs(dir) 76 if err != nil { 77 return "", err 78 } 79 return abs, nil 80 } 81 82 // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent 83 // to execute a Command on our behalf. Individual commands should be exposed 84 // by symlinking the command name to this executable. 85 func jujuCMain(commandName string, ctx *cmd.Context, args []string) (code int, err error) { 86 code = 1 87 contextId, err := getenv("JUJU_CONTEXT_ID") 88 if err != nil { 89 return 90 } 91 dir, err := getwd() 92 if err != nil { 93 return 94 } 95 req := jujuc.Request{ 96 ContextId: contextId, 97 Dir: dir, 98 CommandName: commandName, 99 Args: args[1:], 100 } 101 socketPath, err := getenv("JUJU_AGENT_SOCKET") 102 if err != nil { 103 return 104 } 105 client, err := sockets.Dial(socketPath) 106 if err != nil { 107 return 108 } 109 defer client.Close() 110 var resp exec.ExecResponse 111 err = client.Call("Jujuc.Main", req, &resp) 112 if err != nil && err.Error() == jujuc.ErrNoStdin.Error() { 113 req.Stdin, err = ioutil.ReadAll(os.Stdin) 114 if err != nil { 115 err = errors.Annotate(err, "cannot read stdin") 116 return 117 } 118 req.StdinSet = true 119 err = client.Call("Jujuc.Main", req, &resp) 120 } 121 if err != nil { 122 return 123 } 124 os.Stdout.Write(resp.Stdout) 125 os.Stderr.Write(resp.Stderr) 126 return resp.Code, nil 127 } 128 129 // Main registers subcommands for the jujud executable, and hands over control 130 // to the cmd package. 131 func jujuDMain(args []string, ctx *cmd.Context) (code int, err error) { 132 // Assuming an average of 200 bytes per log message, use up to 133 // 200MB for the log buffer. 134 logCh, err := logsender.InstallBufferedLogWriter(1048576) 135 if err != nil { 136 return 1, errors.Trace(err) 137 } 138 139 jujud := jujucmd.NewSuperCommand(cmd.SuperCommandParams{ 140 Name: "jujud", 141 Doc: jujudDoc, 142 }) 143 144 jujud.Log.NewWriter = func(target io.Writer) loggo.Writer { 145 return &jujudWriter{target: target} 146 } 147 148 jujud.Register(NewBootstrapCommand()) 149 150 // TODO(katco-): AgentConf type is doing too much. The 151 // MachineAgent type has called out the separate concerns; the 152 // AgentConf should be split up to follow suit. 153 agentConf := agentcmd.NewAgentConf("") 154 machineAgentFactory := agentcmd.MachineAgentFactoryFn(agentConf, logCh, "") 155 jujud.Register(agentcmd.NewMachineAgentCmd(ctx, machineAgentFactory, agentConf, agentConf)) 156 157 jujud.Register(agentcmd.NewUnitAgent(ctx, logCh)) 158 159 jujud.Register(NewUpgradeMongoCommand()) 160 161 code = cmd.Main(jujud, ctx, args[1:]) 162 return code, nil 163 } 164 165 // This function exists to preserve test functionality. 166 // On windows we need to catch the return code from main for 167 // service functionality purposes, but on unix we can just os.Exit 168 func MainWrapper(args []string) { 169 os.Exit(Main(args)) 170 } 171 172 // Main is not redundant with main(), because it provides an entry point 173 // for testing with arbitrary command line arguments. 174 func Main(args []string) int { 175 defer func() { 176 if r := recover(); r != nil { 177 buf := make([]byte, 4096) 178 buf = buf[:runtime.Stack(buf, false)] 179 logger.Criticalf("Unhandled panic: \n%v\n%s", r, buf) 180 os.Exit(exit_panic) 181 } 182 }() 183 184 ctx, err := cmd.DefaultContext() 185 if err != nil { 186 fmt.Fprintf(os.Stderr, "error: %v\n", err) 187 os.Exit(exit_err) 188 } 189 190 code := 1 191 commandName := filepath.Base(args[0]) 192 switch commandName { 193 case names.Jujud: 194 // start pprof server and defer cleanup 195 stop := pprof.Start() 196 defer stop() 197 198 code, err = jujuDMain(args, ctx) 199 case names.Jujuc: 200 fmt.Fprint(os.Stderr, jujudDoc) 201 code = exit_err 202 err = fmt.Errorf("jujuc should not be called directly") 203 case names.JujuRun: 204 code = cmd.Main(&RunCommand{}, ctx, args[1:]) 205 case names.JujuDumpLogs: 206 code = cmd.Main(dumplogs.NewCommand(), ctx, args[1:]) 207 default: 208 code, err = jujuCMain(commandName, ctx, args) 209 } 210 if err != nil { 211 fmt.Fprintf(os.Stderr, "error: %v\n", err) 212 } 213 return code 214 } 215 216 type jujudWriter struct { 217 target io.Writer 218 unitFormatter simpleFormatter 219 defaultFormatter loggo.DefaultFormatter 220 } 221 222 func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { 223 if strings.HasPrefix(module, "unit.") { 224 fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message)) 225 } else { 226 fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message)) 227 } 228 } 229 230 type simpleFormatter struct{} 231 232 func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string { 233 ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") 234 // Just show the last element of the module. 235 lastDot := strings.LastIndex(module, ".") 236 module = module[lastDot+1:] 237 return fmt.Sprintf("%s %s %s %s", ts, level, module, message) 238 }