// InstallBufferedLogWriter creates a new BufferedLogWriter, registers // it with Loggo and returns its output channel. func InstallBufferedLogWriter(maxLen int) (LogRecordCh, error) { if !feature.IsDbLogEnabled() { return nil, nil } writer := NewBufferedLogWriter(maxLen) err := loggo.RegisterWriter(writerName, writer, loggo.TRACE) if err != nil { return nil, errors.Annotate(err, "failed to set up log buffering") } return writer.Logs(), nil }
// UninstallBufferedLogWriter removes the BufferedLogWriter previously // installed by InstallBufferedLogWriter and closes it. func UninstallBufferedLogWriter() error { if !feature.IsDbLogEnabled() { return nil } writer, _, err := loggo.RemoveWriter(writerName) if err != nil { return errors.Annotate(err, "failed to uninstall log buffering") } bufWriter, ok := writer.(*BufferedLogWriter) if !ok { return errors.New("unexpected writer installed as buffered log writer") } bufWriter.Close() return nil }
// Manifold returns a dependency manifold that runs a logger // worker, using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.APICallerName, }, Start: func(getResource dependency.GetResourceFunc) (worker.Worker, error) { if !feature.IsDbLogEnabled() { logger.Warningf("log sender manifold disabled by feature flag") return nil, dependency.ErrMissing } var apiCaller base.APICaller if err := getResource(config.APICallerName, &apiCaller); err != nil { return nil, err } return New(config.LogSource, logsender.NewAPI(apiCaller)), nil }, } }
// Manifold returns a dependency manifold that runs a logger // worker, using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.AgentName, config.APIInfoGateName, }, Start: func(getResource dependency.GetResourceFunc) (worker.Worker, error) { if !feature.IsDbLogEnabled() { logger.Warningf("log sender manifold disabled by feature flag") return nil, dependency.ErrMissing } var gate gate.Waiter if err := getResource(config.APIInfoGateName, &gate); err != nil { return nil, err } var agent agent.Agent if err := getResource(config.AgentName, &agent); err != nil { return nil, err } return New(config.LogSource, gate, agent), nil }, } }
func (srv *Server) run(lis net.Listener) { defer func() { srv.state.HackLeadership() // Break deadlocks caused by BlockUntil... calls. srv.wg.Wait() // wait for any outstanding requests to complete. srv.tomb.Done() srv.statePool.Close() }() srv.wg.Add(1) go func() { err := srv.mongoPinger() // Before killing the tomb, inform the API handlers that // Mongo is unavailable. API handlers can use this to decide // not to perform non-critical Mongo-related operations when // tearing down. atomic.AddUint32(&srv.mongoUnavailable, 1) srv.tomb.Kill(err) srv.wg.Done() }() // for pat based handlers, they are matched in-order of being // registered, first match wins. So more specific ones have to be // registered first. mux := pat.New() srvDying := srv.tomb.Dying() httpCtxt := httpContext{ srv: srv, } if feature.IsDbLogEnabled() { handleAll(mux, "/environment/:envuuid/logsink", newLogSinkHandler(httpCtxt, srv.logDir)) handleAll(mux, "/environment/:envuuid/log", newDebugLogDBHandler(httpCtxt, srvDying)) } else { handleAll(mux, "/environment/:envuuid/log", newDebugLogFileHandler(httpCtxt, srvDying, srv.logDir)) } handleAll(mux, "/environment/:envuuid/charms", &charmsHandler{ ctxt: httpCtxt, dataDir: srv.dataDir}, ) // TODO: We can switch from handleAll to mux.Post/Get/etc for entries // where we only want to support specific request methods. However, our // tests currently assert that errors come back as application/json and // pat only does "text/plain" responses. handleAll(mux, "/environment/:envuuid/tools", &toolsUploadHandler{ ctxt: httpCtxt, }, ) handleAll(mux, "/environment/:envuuid/tools/:version", &toolsDownloadHandler{ ctxt: httpCtxt, }, ) strictCtxt := httpCtxt strictCtxt.strictValidation = true strictCtxt.stateServerEnvOnly = true handleAll(mux, "/environment/:envuuid/backups", &backupHandler{ ctxt: strictCtxt, }, ) handleAll(mux, "/environment/:envuuid/api", http.HandlerFunc(srv.apiHandler)) handleAll(mux, "/environment/:envuuid/images/:kind/:series/:arch/:filename", &imagesDownloadHandler{ ctxt: httpCtxt, dataDir: srv.dataDir, state: srv.state, }, ) // For backwards compatibility we register all the old paths if feature.IsDbLogEnabled() { handleAll(mux, "/log", newDebugLogDBHandler(httpCtxt, srvDying)) } else { handleAll(mux, "/log", newDebugLogFileHandler(httpCtxt, srvDying, srv.logDir)) } handleAll(mux, "/charms", &charmsHandler{ ctxt: httpCtxt, dataDir: srv.dataDir, }, ) handleAll(mux, "/tools", &toolsUploadHandler{ ctxt: httpCtxt, }, ) handleAll(mux, "/tools/:version", &toolsDownloadHandler{ ctxt: httpCtxt, }, ) handleAll(mux, "/", http.HandlerFunc(srv.apiHandler)) go func() { // The error from http.Serve is not interesting. http.Serve(lis, mux) }() <-srv.tomb.Dying() lis.Close() }
func (a *UnitAgent) APIWorkers() (_ worker.Worker, err error) { agentConfig := a.CurrentConfig() dataDir := agentConfig.DataDir() hookLock, err := cmdutil.HookExecutionLock(dataDir) if err != nil { return nil, err } st, entity, err := OpenAPIState(agentConfig, a) if err != nil { return nil, err } unitTag, err := names.ParseUnitTag(entity.Tag()) if err != nil { return nil, errors.Trace(err) } // Ensure that the environment uuid is stored in the agent config. // Luckily the API has it recorded for us after we connect. if agentConfig.Environment().Id() == "" { err := a.ChangeConfig(func(setter agent.ConfigSetter) error { environTag, err := st.EnvironTag() if err != nil { return errors.Annotate(err, "no environment uuid set on api") } return setter.Migrate(agent.MigrateParams{ Environment: environTag, }) }) if err != nil { logger.Warningf("unable to save environment uuid: %v", err) // Not really fatal, just annoying. } } defer func() { if err != nil { st.Close() reportClosedUnitAPI(st) } }() // Before starting any workers, ensure we record the Juju version this unit // agent is running. currentTools := &tools.Tools{Version: version.Current} apiStateUpgrader := a.getUpgrader(st) if err := apiStateUpgrader.SetVersion(agentConfig.Tag().String(), currentTools.Version); err != nil { return nil, errors.Annotate(err, "cannot set unit agent version") } runner := worker.NewRunner(cmdutil.ConnectionIsFatal(logger, st), cmdutil.MoreImportant) // start proxyupdater first to ensure proxy settings are correct runner.StartWorker("proxyupdater", func() (worker.Worker, error) { return proxyupdater.New(st.Environment(), false), nil }) if feature.IsDbLogEnabled() { runner.StartWorker("logsender", func() (worker.Worker, error) { return logsender.New(a.bufferedLogs, agentConfig.APIInfo()), nil }) } runner.StartWorker("upgrader", func() (worker.Worker, error) { return upgrader.NewAgentUpgrader( st.Upgrader(), agentConfig, agentConfig.UpgradedToVersion(), func() bool { return false }, a.initialAgentUpgradeCheckComplete, ), nil }) runner.StartWorker("logger", func() (worker.Worker, error) { return workerlogger.NewLogger(st.Logger(), agentConfig), nil }) runner.StartWorker("uniter", func() (worker.Worker, error) { uniterFacade, err := st.Uniter() if err != nil { return nil, errors.Trace(err) } uniterParams := uniter.UniterParams{ uniterFacade, unitTag, leadership.NewClient(st), dataDir, hookLock, uniter.NewMetricsTimerChooser(), uniter.NewUpdateStatusTimer(), nil, } return uniter.NewUniter(&uniterParams), nil }) runner.StartWorker("apiaddressupdater", func() (worker.Worker, error) { uniterFacade, err := st.Uniter() if err != nil { return nil, errors.Trace(err) } return apiaddressupdater.NewAPIAddressUpdater(uniterFacade, a), nil }) if !featureflag.Enabled(feature.DisableRsyslog) { runner.StartWorker("rsyslog", func() (worker.Worker, error) { return cmdutil.NewRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslog.RsyslogModeForwarding) }) } return cmdutil.NewCloseWorker(logger, runner, st), nil }