github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 "net/rpc" 10 "os" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/juju/loggo" 16 17 "github.com/juju/juju/cmd" 18 "github.com/juju/juju/worker/uniter/jujuc" 19 "github.com/juju/utils/exec" 20 21 // Import the providers. 22 _ "github.com/juju/juju/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, ctx *cmd.Context) (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, ctx, 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 ctx, err := cmd.DefaultContext() 116 if err != nil { 117 fmt.Fprintf(os.Stderr, "error: %v\n", err) 118 os.Exit(2) 119 } 120 commandName := filepath.Base(args[0]) 121 if commandName == "jujud" { 122 code, err = jujuDMain(args, ctx) 123 } else if commandName == "jujuc" { 124 fmt.Fprint(os.Stderr, jujudDoc) 125 code = 2 126 err = fmt.Errorf("jujuc should not be called directly") 127 } else if commandName == "juju-run" { 128 code = cmd.Main(&RunCommand{}, ctx, args[1:]) 129 } else { 130 code, err = jujuCMain(commandName, args) 131 } 132 if err != nil { 133 fmt.Fprintf(os.Stderr, "error: %v\n", err) 134 } 135 os.Exit(code) 136 } 137 138 func main() { 139 Main(os.Args) 140 } 141 142 type writerFactory struct{} 143 144 func (*writerFactory) NewWriter(target io.Writer) loggo.Writer { 145 return &jujudWriter{target: target} 146 } 147 148 type jujudWriter struct { 149 target io.Writer 150 unitFormatter simpleFormatter 151 defaultFormatter loggo.DefaultFormatter 152 } 153 154 var _ loggo.Writer = (*jujudWriter)(nil) 155 156 func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { 157 if strings.HasPrefix(module, "unit.") { 158 fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message)) 159 } else { 160 fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message)) 161 } 162 } 163 164 type simpleFormatter struct{} 165 166 func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string { 167 ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") 168 // Just show the last element of the module. 169 lastDot := strings.LastIndex(module, ".") 170 module = module[lastDot+1:] 171 return fmt.Sprintf("%s %s %s %s", ts, level, module, message) 172 }