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