github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/juju/cmd"
    17  	"github.com/juju/errors"
    18  	"github.com/juju/loggo"
    19  	"github.com/juju/utils/exec"
    20  
    21  	jujucmd "github.com/juju/juju/cmd"
    22  	agentcmd "github.com/juju/juju/cmd/jujud/agent"
    23  	"github.com/juju/juju/juju/names"
    24  	"github.com/juju/juju/juju/sockets"
    25  	// Import the providers.
    26  	_ "github.com/juju/juju/provider/all"
    27  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    28  )
    29  
    30  var jujudDoc = `
    31  juju provides easy, intelligent service orchestration on top of environments
    32  such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju.
    33  
    34  https://juju.ubuntu.com/
    35  
    36  The jujud command can also forward invocations over RPC for execution by the
    37  juju unit agent. When used in this way, it expects to be called via a symlink
    38  named for the desired remote command, and expects JUJU_AGENT_SOCKET and
    39  JUJU_CONTEXT_ID be set in its environment.
    40  `
    41  
    42  const (
    43  	// exit_err is the value that is returned when the user has run juju in an invalid way.
    44  	exit_err = 2
    45  	// exit_panic is the value that is returned when we exit due to an unhandled panic.
    46  	exit_panic = 3
    47  )
    48  
    49  func getenv(name string) (string, error) {
    50  	value := os.Getenv(name)
    51  	if value == "" {
    52  		return "", fmt.Errorf("%s not set", name)
    53  	}
    54  	return value, nil
    55  }
    56  
    57  func getwd() (string, error) {
    58  	dir, err := os.Getwd()
    59  	if err != nil {
    60  		return "", err
    61  	}
    62  	abs, err := filepath.Abs(dir)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	return abs, nil
    67  }
    68  
    69  // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent
    70  // to execute a Command on our behalf. Individual commands should be exposed
    71  // by symlinking the command name to this executable.
    72  func jujuCMain(commandName string, ctx *cmd.Context, args []string) (code int, err error) {
    73  	code = 1
    74  	contextId, err := getenv("JUJU_CONTEXT_ID")
    75  	if err != nil {
    76  		return
    77  	}
    78  	dir, err := getwd()
    79  	if err != nil {
    80  		return
    81  	}
    82  	req := jujuc.Request{
    83  		ContextId:   contextId,
    84  		Dir:         dir,
    85  		CommandName: commandName,
    86  		Args:        args[1:],
    87  	}
    88  	socketPath, err := getenv("JUJU_AGENT_SOCKET")
    89  	if err != nil {
    90  		return
    91  	}
    92  	client, err := sockets.Dial(socketPath)
    93  	if err != nil {
    94  		return
    95  	}
    96  	defer client.Close()
    97  	var resp exec.ExecResponse
    98  	err = client.Call("Jujuc.Main", req, &resp)
    99  	if err != nil && err.Error() == jujuc.ErrNoStdin.Error() {
   100  		req.Stdin, err = ioutil.ReadAll(os.Stdin)
   101  		if err != nil {
   102  			err = errors.Annotate(err, "cannot read stdin")
   103  			return
   104  		}
   105  		req.StdinSet = true
   106  		err = client.Call("Jujuc.Main", req, &resp)
   107  	}
   108  	if err != nil {
   109  		return
   110  	}
   111  	os.Stdout.Write(resp.Stdout)
   112  	os.Stderr.Write(resp.Stderr)
   113  	return resp.Code, nil
   114  }
   115  
   116  // Main registers subcommands for the jujud executable, and hands over control
   117  // to the cmd package.
   118  func jujuDMain(args []string, ctx *cmd.Context) (code int, err error) {
   119  	jujud := jujucmd.NewSuperCommand(cmd.SuperCommandParams{
   120  		Name: "jujud",
   121  		Doc:  jujudDoc,
   122  	})
   123  	jujud.Log.Factory = &writerFactory{}
   124  	jujud.Register(NewBootstrapCommand())
   125  
   126  	// TODO(katco-): AgentConf type is doing too much. The
   127  	// MachineAgent type has called out the seperate concerns; the
   128  	// AgentConf should be split up to follow suite.
   129  	agentConf := agentcmd.NewAgentConf("")
   130  	machineAgentFactory := agentcmd.MachineAgentFactoryFn(agentConf, agentConf)
   131  	jujud.Register(agentcmd.NewMachineAgentCmd(ctx, machineAgentFactory, agentConf, agentConf))
   132  
   133  	jujud.Register(agentcmd.NewUnitAgent(ctx))
   134  
   135  	code = cmd.Main(jujud, ctx, args[1:])
   136  	return code, nil
   137  }
   138  
   139  // Main is not redundant with main(), because it provides an entry point
   140  // for testing with arbitrary command line arguments.
   141  func Main(args []string) {
   142  	defer func() {
   143  		if r := recover(); r != nil {
   144  			buf := make([]byte, 4096)
   145  			buf = buf[:runtime.Stack(buf, false)]
   146  			logger.Criticalf("Unhandled panic: \n%v\n%s", r, buf)
   147  			os.Exit(exit_panic)
   148  		}
   149  	}()
   150  	var code int = 1
   151  	ctx, err := cmd.DefaultContext()
   152  	if err != nil {
   153  		fmt.Fprintf(os.Stderr, "error: %v\n", err)
   154  		os.Exit(exit_err)
   155  	}
   156  	commandName := filepath.Base(args[0])
   157  	if commandName == names.Jujud {
   158  		code, err = jujuDMain(args, ctx)
   159  	} else if commandName == names.Jujuc {
   160  		fmt.Fprint(os.Stderr, jujudDoc)
   161  		code = exit_err
   162  		err = fmt.Errorf("jujuc should not be called directly")
   163  	} else if commandName == names.JujuRun {
   164  		code = cmd.Main(&RunCommand{}, ctx, args[1:])
   165  	} else {
   166  		code, err = jujuCMain(commandName, ctx, args)
   167  	}
   168  	if err != nil {
   169  		fmt.Fprintf(os.Stderr, "error: %v\n", err)
   170  	}
   171  	os.Exit(code)
   172  }
   173  
   174  type writerFactory struct{}
   175  
   176  func (*writerFactory) NewWriter(target io.Writer) loggo.Writer {
   177  	return &jujudWriter{target: target}
   178  }
   179  
   180  type jujudWriter struct {
   181  	target           io.Writer
   182  	unitFormatter    simpleFormatter
   183  	defaultFormatter loggo.DefaultFormatter
   184  }
   185  
   186  var _ loggo.Writer = (*jujudWriter)(nil)
   187  
   188  func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) {
   189  	if strings.HasPrefix(module, "unit.") {
   190  		fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message))
   191  	} else {
   192  		fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message))
   193  	}
   194  }
   195  
   196  type simpleFormatter struct{}
   197  
   198  func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string {
   199  	ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05")
   200  	// Just show the last element of the module.
   201  	lastDot := strings.LastIndex(module, ".")
   202  	module = module[lastDot+1:]
   203  	return fmt.Sprintf("%s %s %s %s", ts, level, module, message)
   204  }