launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/main.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "io" 9 "net/rpc" 10 "os" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/loggo/loggo" 16 17 "launchpad.net/juju-core/cmd" 18 "launchpad.net/juju-core/utils/exec" 19 "launchpad.net/juju-core/worker/uniter/jujuc" 20 21 // Import the providers. 22 _ "launchpad.net/juju-core/provider/all" 23 ) 24 25 var jujudDoc = ` 26 juju provides easy, intelligent service orchestration on top of environments 27 such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju. 28 29 https://juju.ubuntu.com/ 30 31 The jujud command can also forward invocations over RPC for execution by the 32 juju unit agent. When used in this way, it expects to be called via a symlink 33 named for the desired remote command, and expects JUJU_AGENT_SOCKET and 34 JUJU_CONTEXT_ID be set in its environment. 35 ` 36 37 func getenv(name string) (string, error) { 38 value := os.Getenv(name) 39 if value == "" { 40 return "", fmt.Errorf("%s not set", name) 41 } 42 return value, nil 43 } 44 45 func getwd() (string, error) { 46 dir, err := os.Getwd() 47 if err != nil { 48 return "", err 49 } 50 abs, err := filepath.Abs(dir) 51 if err != nil { 52 return "", err 53 } 54 return abs, nil 55 } 56 57 // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent 58 // to execute a Command on our behalf. Individual commands should be exposed 59 // by symlinking the command name to this executable. 60 func jujuCMain(commandName string, args []string) (code int, err error) { 61 code = 1 62 contextId, err := getenv("JUJU_CONTEXT_ID") 63 if err != nil { 64 return 65 } 66 dir, err := getwd() 67 if err != nil { 68 return 69 } 70 req := jujuc.Request{ 71 ContextId: contextId, 72 Dir: dir, 73 CommandName: commandName, 74 Args: args[1:], 75 } 76 socketPath, err := getenv("JUJU_AGENT_SOCKET") 77 if err != nil { 78 return 79 } 80 client, err := rpc.Dial("unix", socketPath) 81 if err != nil { 82 return 83 } 84 defer client.Close() 85 var resp exec.ExecResponse 86 err = client.Call("Jujuc.Main", req, &resp) 87 if err != nil { 88 return 89 } 90 os.Stdout.Write(resp.Stdout) 91 os.Stderr.Write(resp.Stderr) 92 return resp.Code, nil 93 } 94 95 // Main registers subcommands for the jujud executable, and hands over control 96 // to the cmd package. 97 func jujuDMain(args []string) (code int, err error) { 98 jujud := cmd.NewSuperCommand(cmd.SuperCommandParams{ 99 Name: "jujud", 100 Doc: jujudDoc, 101 Log: &cmd.Log{Factory: &writerFactory{}}, 102 }) 103 jujud.Register(&BootstrapCommand{}) 104 jujud.Register(&MachineAgent{}) 105 jujud.Register(&UnitAgent{}) 106 jujud.Register(&cmd.VersionCommand{}) 107 code = cmd.Main(jujud, cmd.DefaultContext(), args[1:]) 108 return code, nil 109 } 110 111 // Main is not redundant with main(), because it provides an entry point 112 // for testing with arbitrary command line arguments. 113 func Main(args []string) { 114 var code int = 1 115 var err error 116 commandName := filepath.Base(args[0]) 117 if commandName == "jujud" { 118 code, err = jujuDMain(args) 119 } else if commandName == "jujuc" { 120 fmt.Fprint(os.Stderr, jujudDoc) 121 code = 2 122 err = fmt.Errorf("jujuc should not be called directly") 123 } else if commandName == "juju-run" { 124 code = cmd.Main(&RunCommand{}, cmd.DefaultContext(), args[1:]) 125 } else { 126 code, err = jujuCMain(commandName, args) 127 } 128 if err != nil { 129 fmt.Fprintf(os.Stderr, "error: %v\n", err) 130 } 131 os.Exit(code) 132 } 133 134 func main() { 135 Main(os.Args) 136 } 137 138 type writerFactory struct{} 139 140 func (*writerFactory) NewWriter(target io.Writer) loggo.Writer { 141 return &jujudWriter{target: target} 142 } 143 144 type jujudWriter struct { 145 target io.Writer 146 unitFormatter simpleFormatter 147 defaultFormatter loggo.DefaultFormatter 148 } 149 150 var _ loggo.Writer = (*jujudWriter)(nil) 151 152 func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { 153 if strings.HasPrefix(module, "unit.") { 154 fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message)) 155 } else { 156 fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message)) 157 } 158 } 159 160 type simpleFormatter struct{} 161 162 func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string { 163 ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") 164 // Just show the last element of the module. 165 lastDot := strings.LastIndex(module, ".") 166 module = module[lastDot+1:] 167 return fmt.Sprintf("%s %s %s %s", ts, level, module, message) 168 }