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