github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/uniter/uniter_windows.go (about)

     1  package uniter
     2  
     3  import (
     4      "math/rand"
     5      "os"
     6      "os/exec"
     7      "path/filepath"
     8      "time"
     9      "fmt"
    10  
    11      "launchpad.net/juju-core/agent/tools"
    12      "launchpad.net/juju-core/utils"
    13      "launchpad.net/juju-core/state/api/uniter"
    14      "launchpad.net/juju-core/cmd"
    15      "launchpad.net/juju-core/worker/uniter/charm"
    16      "launchpad.net/juju-core/worker/uniter/jujuc"
    17      "launchpad.net/juju-core/worker/uniter/hook"
    18  
    19      "launchpad.net/juju-core/windows"
    20  )
    21  
    22  
    23  func (u *Uniter) init(unitTag string) (err error) {
    24      defer utils.ErrorContextf(&err, "failed to initialize uniter for %q", unitTag)
    25      u.unit, err = u.st.Unit(unitTag)
    26      if err != nil {
    27          return err
    28      }
    29      if err = u.setupLocks(); err != nil {
    30          return err
    31      }
    32      u.toolsDir = tools.ToolsDir(u.dataDir, unitTag)
    33      if err := EnsureJujucSymlinks(u.toolsDir); err != nil {
    34          return err
    35      }
    36      u.baseDir = filepath.Join(u.dataDir, "agents", unitTag)
    37      u.relationsDir = filepath.Join(u.baseDir, "state", "relations")
    38      if err := os.MkdirAll(u.relationsDir, 0755); err != nil {
    39          return err
    40      }
    41      u.service, err = u.st.Service(u.unit.ServiceTag())
    42      if err != nil {
    43          return err
    44      }
    45      var env *uniter.Environment
    46      env, err = u.st.Environment()
    47      if err != nil {
    48          return err
    49      }
    50      u.uuid = env.UUID()
    51      u.envName = env.Name()
    52  
    53      runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile)
    54      //TODO: gsamfira: This is a bit hacky. Would prefer implementing
    55      //named pipes on windows
    56      u.tcpSock, err = utils.WriteSocketFile(runListenerSocketPath)
    57      if err != nil {
    58          return err
    59      }
    60  
    61      logger.Debugf("starting juju-run listener on:%s", u.tcpSock)
    62      u.runListener, err = NewRunListener(u, u.tcpSock)
    63      if err != nil {
    64          return err
    65      }
    66  
    67      u.relationers = map[int]*Relationer{}
    68      u.relationHooks = make(chan hook.Info)
    69      u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm"))
    70      deployerPath := filepath.Join(u.baseDir, "state", "deployer")
    71      bundles := charm.NewBundlesDir(filepath.Join(u.baseDir, "state", "bundles"))
    72      u.deployer = charm.NewGitDeployer(u.charm.Path(), deployerPath, bundles)
    73      u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter"))
    74      u.rand = rand.New(rand.NewSource(time.Now().Unix()))
    75      return nil
    76  }
    77  
    78  func (u *Uniter) startJujucServer(context *HookContext) (*jujuc.Server, string, error) {
    79      // Prepare server.
    80      getCmd := func(ctxId, cmdName string) (cmd.Command, error) {
    81          // TODO: switch to long-running server with single context;
    82          // use nonce in place of context id.
    83          if ctxId != context.id {
    84              return nil, fmt.Errorf("expected context id %q, got %q", context.id, ctxId)
    85          }
    86          return jujuc.NewCommand(context, cmdName)
    87      }
    88      // gsamfira: This function simply returns a free TCP socket.
    89      // TODO: Must see if this socket is used by any other process then charms
    90      socketPath, errSock := utils.GetSocket()
    91      if errSock != nil {
    92          return nil, "", errSock
    93      }
    94      srv, err := jujuc.NewServer(getCmd, socketPath)
    95      if err != nil {
    96          return nil, "", err
    97      }
    98      go srv.Run()
    99      return srv, socketPath, nil
   100  }
   101  
   102  // runHook executes the supplied hook.Info in an appropriate hook context. If
   103  // the hook itself fails to execute, it returns errHookFailed.
   104  func (u *Uniter) runHook(hi hook.Info) (err error) {
   105      // Prepare context.
   106      if err = hi.Validate(); err != nil {
   107          return err
   108      }
   109  
   110      hookName := string(hi.Kind)
   111      relationId := -1
   112      if hi.Kind.IsRelation() {
   113          relationId = hi.RelationId
   114          if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil {
   115              return err
   116          }
   117      }
   118      hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63())
   119  
   120      lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName)
   121      if err = u.acquireHookLock(lockMessage); err != nil {
   122          return err
   123      }
   124      defer u.hookLock.Unlock()
   125  
   126      hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit)
   127      if err != nil {
   128          return err
   129      }
   130      srv, socketPath, err := u.startJujucServer(hctx)
   131      if err != nil {
   132          return err
   133      }
   134      defer srv.Close()
   135  
   136      // Run the hook.
   137      if err := u.writeState(RunHook, Pending, &hi, nil); err != nil {
   138          return err
   139      }
   140      logger.Infof("running %q hook", hookName)
   141      ranHook := true
   142      err = hctx.RunHook(hookName, u.charm.Path(), u.toolsDir, socketPath)
   143      if RebootRequiredError(err){
   144          logger.Infof("hook %q requested a reboot", hookName)
   145          if err := u.writeState(RunHook, Queued, &hi, nil); err != nil {
   146              return err
   147          }
   148          time := 5
   149          logger.Infof("rebooting system in %q seconds", time)
   150          errReboot := windows.Reboot(time)
   151          if eeReboot, ok := errReboot.(*exec.Error); ok && eeReboot != nil {
   152              logger.Infof("Reboot returned error: %q", eeReboot.Err)
   153          }
   154          logger.Infof("Stopping uniter due to reboot")
   155          u.Stop()
   156      }
   157      if IsMissingHookError(err) {
   158          ranHook = false
   159      } else if err != nil {
   160          logger.Errorf("hook failed: %s", err)
   161          u.notifyHookFailed(hookName, hctx)
   162          return errHookFailed
   163      }
   164      if err := u.writeState(RunHook, Done, &hi, nil); err != nil {
   165          return err
   166      }
   167      if ranHook {
   168          logger.Infof("ran %q hook", hookName)
   169          u.notifyHookCompleted(hookName, hctx)
   170      } else {
   171          logger.Infof("skipped %q hook (missing)", hookName)
   172      }
   173      return u.commitHook(hi)
   174  }