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  }