github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "github.com/juju/juju/agent" 22 jujucmd "github.com/juju/juju/cmd" 23 agentcmd "github.com/juju/juju/cmd/jujud/agent" 24 "github.com/juju/juju/cmd/jujud/dumplogs" 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://jujucharms.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 defer logger.Debugf("jujud complete, code %d, err %v", code, err) 135 logCh, err := logsender.InstallBufferedLogWriter(1048576) 136 if err != nil { 137 return 1, errors.Trace(err) 138 } 139 140 jujud := jujucmd.NewSuperCommand(cmd.SuperCommandParams{ 141 Name: "jujud", 142 Doc: jujudDoc, 143 }) 144 145 jujud.Log.NewWriter = func(target io.Writer) loggo.Writer { 146 return &jujudWriter{target: target} 147 } 148 149 jujud.Register(NewBootstrapCommand()) 150 151 // TODO(katco-): AgentConf type is doing too much. The 152 // MachineAgent type has called out the separate concerns; the 153 // AgentConf should be split up to follow suit. 154 agentConf := agentcmd.NewAgentConf("") 155 machineAgentFactory := agentcmd.MachineAgentFactoryFn(agentConf, logCh, "") 156 jujud.Register(agentcmd.NewMachineAgentCmd(ctx, machineAgentFactory, agentConf, agentConf)) 157 158 jujud.Register(agentcmd.NewUnitAgent(ctx, logCh)) 159 160 jujud.Register(NewUpgradeMongoCommand()) 161 162 code = cmd.Main(jujud, ctx, args[1:]) 163 return code, nil 164 } 165 166 // This function exists to preserve test functionality. 167 // On windows we need to catch the return code from main for 168 // service functionality purposes, but on unix we can just os.Exit 169 func MainWrapper(args []string) { 170 os.Exit(Main(args)) 171 } 172 173 // Main is not redundant with main(), because it provides an entry point 174 // for testing with arbitrary command line arguments. 175 func Main(args []string) int { 176 defer func() { 177 if r := recover(); r != nil { 178 buf := make([]byte, 4096) 179 buf = buf[:runtime.Stack(buf, false)] 180 logger.Criticalf("Unhandled panic: \n%v\n%s", r, buf) 181 os.Exit(exit_panic) 182 } 183 }() 184 185 ctx, err := cmd.DefaultContext() 186 if err != nil { 187 fmt.Fprintf(os.Stderr, "error: %v\n", err) 188 os.Exit(exit_err) 189 } 190 191 code := 1 192 commandName := filepath.Base(args[0]) 193 switch commandName { 194 case names.Jujud: 195 code, err = jujuDMain(args, ctx) 196 case names.Jujuc: 197 fmt.Fprint(os.Stderr, jujudDoc) 198 code = exit_err 199 err = fmt.Errorf("jujuc should not be called directly") 200 case names.JujuRun: 201 run := &RunCommand{ 202 MachineLockName: agent.MachineLockName, 203 } 204 code = cmd.Main(run, 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 } 219 220 func (w *jujudWriter) Write(entry loggo.Entry) { 221 if strings.HasPrefix(entry.Module, "unit.") { 222 fmt.Fprintln(w.target, w.unitFormat(entry)) 223 } else { 224 fmt.Fprintln(w.target, loggo.DefaultFormatter(entry)) 225 } 226 } 227 228 func (w *jujudWriter) unitFormat(entry loggo.Entry) string { 229 ts := entry.Timestamp.In(time.UTC).Format("2006-01-02 15:04:05") 230 // Just show the last element of the module. 231 lastDot := strings.LastIndex(entry.Module, ".") 232 module := entry.Module[lastDot+1:] 233 return fmt.Sprintf("%s %s %s %s", ts, entry.Level, module, entry.Message) 234 }