github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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/cmd" 16 "github.com/juju/loggo" 17 18 jujucmd "github.com/juju/juju/cmd" 19 "github.com/juju/juju/worker/uniter/jujuc" 20 "github.com/juju/utils/exec" 21 22 // Import the providers. 23 _ "github.com/juju/juju/provider/all" 24 ) 25 26 var jujudDoc = ` 27 juju provides easy, intelligent service orchestration on top of environments 28 such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju. 29 30 https://juju.ubuntu.com/ 31 32 The jujud command can also forward invocations over RPC for execution by the 33 juju unit agent. When used in this way, it expects to be called via a symlink 34 named for the desired remote command, and expects JUJU_AGENT_SOCKET and 35 JUJU_CONTEXT_ID be set in its environment. 36 ` 37 38 func getenv(name string) (string, error) { 39 value := os.Getenv(name) 40 if value == "" { 41 return "", fmt.Errorf("%s not set", name) 42 } 43 return value, nil 44 } 45 46 func getwd() (string, error) { 47 dir, err := os.Getwd() 48 if err != nil { 49 return "", err 50 } 51 abs, err := filepath.Abs(dir) 52 if err != nil { 53 return "", err 54 } 55 return abs, nil 56 } 57 58 // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent 59 // to execute a Command on our behalf. Individual commands should be exposed 60 // by symlinking the command name to this executable. 61 func jujuCMain(commandName string, args []string) (code int, err error) { 62 code = 1 63 contextId, err := getenv("JUJU_CONTEXT_ID") 64 if err != nil { 65 return 66 } 67 dir, err := getwd() 68 if err != nil { 69 return 70 } 71 req := jujuc.Request{ 72 ContextId: contextId, 73 Dir: dir, 74 CommandName: commandName, 75 Args: args[1:], 76 } 77 socketPath, err := getenv("JUJU_AGENT_SOCKET") 78 if err != nil { 79 return 80 } 81 client, err := rpc.Dial("unix", socketPath) 82 if err != nil { 83 return 84 } 85 defer client.Close() 86 var resp exec.ExecResponse 87 err = client.Call("Jujuc.Main", req, &resp) 88 if err != nil { 89 return 90 } 91 os.Stdout.Write(resp.Stdout) 92 os.Stderr.Write(resp.Stderr) 93 return resp.Code, nil 94 } 95 96 // Main registers subcommands for the jujud executable, and hands over control 97 // to the cmd package. 98 func jujuDMain(args []string, ctx *cmd.Context) (code int, err error) { 99 jujud := jujucmd.NewSuperCommand(cmd.SuperCommandParams{ 100 Name: "jujud", 101 Doc: jujudDoc, 102 }) 103 jujud.Log.Factory = &writerFactory{} 104 jujud.Register(&BootstrapCommand{}) 105 jujud.Register(&MachineAgent{}) 106 jujud.Register(&UnitAgent{}) 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 }