// 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()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true err = hctx.RunHook(hookName, u.charmPath, u.toolsDir, socketPath) if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) }
// NewRunHook is part of the Factory interface. func (f *factory) NewRunHook(hookInfo hook.Info) (Operation, error) { if err := hookInfo.Validate(); err != nil { return nil, err } return &runHook{ info: hookInfo, callbacks: f.config.Callbacks, runnerFactory: f.config.RunnerFactory, }, nil }
// NewHookRunner exists to satisfy the Factory interface. func (f *factory) NewHookRunner(hookInfo hook.Info) (Runner, error) { if err := hookInfo.Validate(); err != nil { return nil, errors.Trace(err) } ctx, err := f.contextFactory.HookContext(hookInfo) if err != nil { return nil, errors.Trace(err) } runner := NewRunner(ctx, f.paths) return runner, nil }
// 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) actionParams := map[string]interface{}(nil) relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } else if hi.Kind == hooks.ActionRequested { action, err := u.st.Action(names.NewActionTag(hi.ActionId)) if err != nil { return err } actionParams = action.Params() hookName = action.Name() } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit, actionParams) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true // The reason for the switch at this point is that once inside RunHook, // we don't know whether we're running an Action or a regular Hook. // RunAction simply calls the exact same method as RunHook, but with // the location as "actions" instead of "hooks". if hi.Kind == hooks.ActionRequested { err = hctx.RunAction(hookName, u.charmPath, u.toolsDir, socketPath) } else { err = hctx.RunHook(hookName, u.charmPath, u.toolsDir, socketPath) } if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) }
// NewHookRunner exists to satisfy the Factory interface. func (f *factory) NewHookRunner(hookInfo hook.Info) (Runner, error) { if err := hookInfo.Validate(); err != nil { return nil, errors.Trace(err) } ctx, err := f.coreContext() if err != nil { return nil, errors.Trace(err) } hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { ctx.relationId = hookInfo.RelationId ctx.remoteUnitName = hookInfo.RemoteUnit relation, found := ctx.relations[hookInfo.RelationId] if !found { return nil, errors.Errorf("unknown relation id: %v", hookInfo.RelationId) } if hookInfo.Kind == hooks.RelationDeparted { relation.cache.RemoveMember(hookInfo.RemoteUnit) } else if hookInfo.RemoteUnit != "" { // Clear remote settings cache for changing remote unit. relation.cache.InvalidateMember(hookInfo.RemoteUnit) } hookName = fmt.Sprintf("%s-%s", relation.Name(), hookInfo.Kind) } if hookInfo.Kind.IsStorage() { ctx.storageTag = names.NewStorageTag(hookInfo.StorageId) if _, found := ctx.storage.Storage(ctx.storageTag); !found { return nil, errors.Errorf("unknown storage id: %v", hookInfo.StorageId) } storageName, err := names.StorageName(hookInfo.StorageId) if err != nil { return nil, errors.Trace(err) } hookName = fmt.Sprintf("%s-%s", storageName, hookName) } // Metrics are only sent from the collect-metrics hook. if hookInfo.Kind == hooks.CollectMetrics { ch, err := f.getCharm() if err != nil { return nil, errors.Trace(err) } ctx.definedMetrics = ch.Metrics() chURL, err := f.unit.CharmURL() if err != nil { return nil, errors.Trace(err) } charmMetrics := map[string]charm.Metric{} if ch.Metrics() != nil { charmMetrics = ch.Metrics().Metrics } ctx.metricsRecorder, err = metrics.NewJSONMetricRecorder( f.paths.GetMetricsSpoolDir(), charmMetrics, chURL.String()) if err != nil { return nil, errors.Trace(err) } } ctx.id = f.newId(hookName) runner := NewRunner(ctx, f.paths) return runner, nil }
// 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) actionParams := map[string]interface{}(nil) // This value is needed to pass results of Action param validation // in case of error or invalidation. This is probably bad form; it // will be corrected in PR refactoring HookContext. // TODO(binary132): handle errors before grabbing hook context. var actionParamsErr error = nil relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } else if hi.Kind == hooks.ActionRequested { action, err := u.st.Action(names.NewActionTag(hi.ActionId)) if err != nil { return err } actionParams = action.Params() hookName = action.Name() _, actionParamsErr = u.validateAction(hookName, actionParams) } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit, actionParams) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true // The reason for the conditional at this point is that once inside // RunHook, we don't know whether we're running an Action or a regular // Hook. RunAction simply calls the exact same method as RunHook, but // with the location as "actions" instead of "hooks". if hi.Kind == hooks.ActionRequested { if actionParamsErr != nil { logger.Errorf("action %q param validation failed: %s", hookName, actionParamsErr.Error()) u.notifyHookFailed(hookName, hctx) return u.commitHook(hi) } err = hctx.RunAction(hookName, u.charmPath, u.toolsDir, socketPath) } else { err = hctx.RunHook(hookName, u.charmPath, u.toolsDir, socketPath) } // Since the Action validation error was separated, regular error pathways // will still occur correctly. if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) }