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  }