github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/juju/names" 24 "github.com/juju/juju/juju/sockets" 25 // Import the providers. 26 _ "github.com/juju/juju/provider/all" 27 "github.com/juju/juju/worker/uniter/runner/jujuc" 28 ) 29 30 var jujudDoc = ` 31 juju provides easy, intelligent service orchestration on top of environments 32 such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju. 33 34 https://juju.ubuntu.com/ 35 36 The jujud command can also forward invocations over RPC for execution by the 37 juju unit agent. When used in this way, it expects to be called via a symlink 38 named for the desired remote command, and expects JUJU_AGENT_SOCKET and 39 JUJU_CONTEXT_ID be set in its environment. 40 ` 41 42 const ( 43 // exit_err is the value that is returned when the user has run juju in an invalid way. 44 exit_err = 2 45 // exit_panic is the value that is returned when we exit due to an unhandled panic. 46 exit_panic = 3 47 ) 48 49 func getenv(name string) (string, error) { 50 value := os.Getenv(name) 51 if value == "" { 52 return "", fmt.Errorf("%s not set", name) 53 } 54 return value, nil 55 } 56 57 func getwd() (string, error) { 58 dir, err := os.Getwd() 59 if err != nil { 60 return "", err 61 } 62 abs, err := filepath.Abs(dir) 63 if err != nil { 64 return "", err 65 } 66 return abs, nil 67 } 68 69 // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent 70 // to execute a Command on our behalf. Individual commands should be exposed 71 // by symlinking the command name to this executable. 72 func jujuCMain(commandName string, ctx *cmd.Context, args []string) (code int, err error) { 73 code = 1 74 contextId, err := getenv("JUJU_CONTEXT_ID") 75 if err != nil { 76 return 77 } 78 dir, err := getwd() 79 if err != nil { 80 return 81 } 82 req := jujuc.Request{ 83 ContextId: contextId, 84 Dir: dir, 85 CommandName: commandName, 86 Args: args[1:], 87 } 88 socketPath, err := getenv("JUJU_AGENT_SOCKET") 89 if err != nil { 90 return 91 } 92 client, err := sockets.Dial(socketPath) 93 if err != nil { 94 return 95 } 96 defer client.Close() 97 var resp exec.ExecResponse 98 err = client.Call("Jujuc.Main", req, &resp) 99 if err != nil && err.Error() == jujuc.ErrNoStdin.Error() { 100 req.Stdin, err = ioutil.ReadAll(os.Stdin) 101 if err != nil { 102 err = errors.Annotate(err, "cannot read stdin") 103 return 104 } 105 req.StdinSet = true 106 err = client.Call("Jujuc.Main", req, &resp) 107 } 108 if err != nil { 109 return 110 } 111 os.Stdout.Write(resp.Stdout) 112 os.Stderr.Write(resp.Stderr) 113 return resp.Code, nil 114 } 115 116 // Main registers subcommands for the jujud executable, and hands over control 117 // to the cmd package. 118 func jujuDMain(args []string, ctx *cmd.Context) (code int, err error) { 119 jujud := jujucmd.NewSuperCommand(cmd.SuperCommandParams{ 120 Name: "jujud", 121 Doc: jujudDoc, 122 }) 123 jujud.Log.Factory = &writerFactory{} 124 jujud.Register(NewBootstrapCommand()) 125 126 // TODO(katco-): AgentConf type is doing too much. The 127 // MachineAgent type has called out the seperate concerns; the 128 // AgentConf should be split up to follow suite. 129 agentConf := agentcmd.NewAgentConf("") 130 machineAgentFactory := agentcmd.MachineAgentFactoryFn(agentConf, agentConf) 131 jujud.Register(agentcmd.NewMachineAgentCmd(ctx, machineAgentFactory, agentConf, agentConf)) 132 133 jujud.Register(agentcmd.NewUnitAgent(ctx)) 134 135 code = cmd.Main(jujud, ctx, args[1:]) 136 return code, nil 137 } 138 139 // Main is not redundant with main(), because it provides an entry point 140 // for testing with arbitrary command line arguments. 141 func Main(args []string) { 142 defer func() { 143 if r := recover(); r != nil { 144 buf := make([]byte, 4096) 145 buf = buf[:runtime.Stack(buf, false)] 146 logger.Criticalf("Unhandled panic: \n%v\n%s", r, buf) 147 os.Exit(exit_panic) 148 } 149 }() 150 var code int = 1 151 ctx, err := cmd.DefaultContext() 152 if err != nil { 153 fmt.Fprintf(os.Stderr, "error: %v\n", err) 154 os.Exit(exit_err) 155 } 156 commandName := filepath.Base(args[0]) 157 if commandName == names.Jujud { 158 code, err = jujuDMain(args, ctx) 159 } else if commandName == names.Jujuc { 160 fmt.Fprint(os.Stderr, jujudDoc) 161 code = exit_err 162 err = fmt.Errorf("jujuc should not be called directly") 163 } else if commandName == names.JujuRun { 164 code = cmd.Main(&RunCommand{}, ctx, args[1:]) 165 } else { 166 code, err = jujuCMain(commandName, ctx, args) 167 } 168 if err != nil { 169 fmt.Fprintf(os.Stderr, "error: %v\n", err) 170 } 171 os.Exit(code) 172 } 173 174 type writerFactory struct{} 175 176 func (*writerFactory) NewWriter(target io.Writer) loggo.Writer { 177 return &jujudWriter{target: target} 178 } 179 180 type jujudWriter struct { 181 target io.Writer 182 unitFormatter simpleFormatter 183 defaultFormatter loggo.DefaultFormatter 184 } 185 186 var _ loggo.Writer = (*jujudWriter)(nil) 187 188 func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { 189 if strings.HasPrefix(module, "unit.") { 190 fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message)) 191 } else { 192 fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message)) 193 } 194 } 195 196 type simpleFormatter struct{} 197 198 func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string { 199 ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") 200 // Just show the last element of the module. 201 lastDot := strings.LastIndex(module, ".") 202 module = module[lastDot+1:] 203 return fmt.Sprintf("%s %s %s %s", ts, level, module, message) 204 }