// runHook executes the supplied hook.Info in an appropriate hook context. If // the hook itself fails to execute, it returns errHookFailed. func (u *Uniter) runHook(hi hook.Info) (err error) { // Prepare context. if err = hi.Validate(); err != nil { return err } hookName := string(hi.Kind) relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) hctx := &HookContext{ service: u.service, unit: u.unit, id: hctxId, relationId: relationId, remoteUnitName: hi.RemoteUnit, relations: map[int]*ContextRelation{}, } for id, r := range u.relationers { hctx.relations[id] = r.Context() } // Prepare server. getCmd := func(ctxId, cmdName string) (cmd.Command, error) { // TODO: switch to long-running server with single context; // use nonce in place of context id. if ctxId != hctxId { return nil, fmt.Errorf("expected context id %q, got %q", hctxId, ctxId) } return jujuc.NewCommand(hctx, cmdName) } socketPath := filepath.Join(u.baseDir, "agent.socket") srv, err := jujuc.NewServer(getCmd, socketPath) if err != nil { return err } go srv.Run() defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } log.Printf("worker/uniter: running %q hook", hookName) if err := hctx.RunHook(hookName, u.charm.Path(), u.toolsDir, socketPath); err != nil { log.Printf("worker/uniter: hook failed: %s", err) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } log.Printf("worker/uniter: ran %q hook", hookName) return u.commitHook(hi) }
// runHook executes the supplied hook.Info in an appropriate hook context. If // the hook itself fails to execute, it returns errHookFailed. func (u *Uniter) runHook(hi hook.Info) (err error) { // Prepare context. if err = hi.Validate(); err != nil { return err } hookName := string(hi.Kind) relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) // We want to make sure we don't block forever when locking, but take the // tomb into account. checkTomb := func() error { select { case <-u.tomb.Dying(): return tomb.ErrDying default: // no-op to fall through to return. } return nil } lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.hookLock.LockWithFunc(lockMessage, checkTomb); err != nil { return err } defer u.hookLock.Unlock() ctxRelations := map[int]*ContextRelation{} for id, r := range u.relationers { ctxRelations[id] = r.Context() } apiAddrs, err := u.st.APIAddresses() if err != nil { return err } hctx := NewHookContext(u.unit, hctxId, u.uuid, relationId, hi.RemoteUnit, ctxRelations, apiAddrs) // Prepare server. getCmd := func(ctxId, cmdName string) (cmd.Command, error) { // TODO: switch to long-running server with single context; // use nonce in place of context id. if ctxId != hctxId { return nil, fmt.Errorf("expected context id %q, got %q", hctxId, ctxId) } return jujuc.NewCommand(hctx, cmdName) } socketPath := filepath.Join(u.baseDir, "agent.socket") srv, err := jujuc.NewServer(getCmd, socketPath) if err != nil { return err } go srv.Run() defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } log.Infof("worker/uniter: running %q hook", hookName) if err := hctx.RunHook(hookName, u.charm.Path(), u.toolsDir, socketPath); err != nil { log.Errorf("worker/uniter: hook failed: %s", err) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } log.Infof("worker/uniter: ran %q hook", hookName) return u.commitHook(hi) }