// NewWatcher returns a new Watcher. func NewWatcher(base *mgo.Collection, modelTag names.ModelTag) *Watcher { w := &Watcher{ modelUUID: modelTag.Id(), base: base, pings: pingsC(base), beings: beingsC(base), beingKey: make(map[int64]string), beingSeq: make(map[string]int64), watches: make(map[string][]chan<- Change), request: make(chan interface{}), } go func() { err := w.loop() cause := errors.Cause(err) // tomb expects ErrDying or ErrStillAlive as // exact values, so we need to log and unwrap // the error first. if err != nil && cause != tomb.ErrDying { logger.Infof("watcher loop failed: %v", err) } w.tomb.Kill(cause) w.tomb.Done() }() return w }
// NewPinger returns a new Pinger to report that key is alive. // It starts reporting after Start is called. func NewPinger(base *mgo.Collection, modelTag names.ModelTag, key string) *Pinger { return &Pinger{ base: base, pings: pingsC(base), beingKey: key, modelUUID: modelTag.Id(), } }
func (m *modelWorkerManager) envIsDead(modelTag names.ModelTag) error { uuid := modelTag.Id() err := m.runner.StopWorker(uuid) if err != nil { return errors.Trace(err) } return nil }
// envNotFound stops all workers for that model. func (m *modelWorkerManager) modelNotFound(modelTag names.ModelTag) error { uuid := modelTag.Id() if err := m.runner.StopWorker(uuid); err != nil { return errors.Trace(err) } if err := m.runner.StopWorker(dyingModelWorkerId(uuid)); err != nil { return errors.Trace(err) } return nil }
// GetModel looks for the model identified by the uuid passed in. func (st *State) GetModel(tag names.ModelTag) (*Model, error) { models, closer := st.getCollection(modelsC) defer closer() env := &Model{st: st} if err := env.refresh(models.FindId(tag.Id())); err != nil { return nil, errors.Trace(err) } return env, nil }
// apiPath returns the given API endpoint path relative // to the given model tag. The caller is responsible // for ensuring that the model tag is valid and // that the path is slash-prefixed. func apiPath(modelTag names.ModelTag, path string) string { if !strings.HasPrefix(path, "/") { panic(fmt.Sprintf("apiPath called with non-slash-prefixed path %q", path)) } if modelTag.Id() == "" { panic("apiPath called with empty model tag") } if modelUUID := modelTag.Id(); modelUUID != "" { return "/model/" + modelUUID + path } return path }
// ResourceTags returns tags to set on an infrastructure resource // for the specified Juju environment. func ResourceTags(e names.ModelTag, taggers ...ResourceTagger) map[string]string { allTags := make(map[string]string) for _, tagger := range taggers { tags, ok := tagger.ResourceTags() if !ok { continue } for k, v := range tags { allTags[k] = v } } allTags[JujuModel] = e.Id() return allTags }
func (s *loginSuite) assertRemoteEnvironment(c *gc.C, st api.Connection, expected names.ModelTag) { // Look at what the api thinks it has. tag, err := st.ModelTag() c.Assert(err, jc.ErrorIsNil) c.Assert(tag, gc.Equals, expected) // Look at what the api Client thinks it has. client := st.Client() // ModelUUID looks at the env tag on the api state connection. c.Assert(client.ModelUUID(), gc.Equals, expected.Id()) // ModelInfo calls a remote method that looks up the environment. info, err := client.ModelInfo() c.Assert(err, jc.ErrorIsNil) c.Assert(info.UUID, gc.Equals, expected.Id()) }
// Open connects to the server described by the given // info, waits for it to be initialized, and returns a new State // representing the model connected to. // // A policy may be provided, which will be used to validate and // modify behaviour of certain operations in state. A nil policy // may be provided. // // Open returns unauthorizedError if access is unauthorized. func Open(tag names.ModelTag, info *mongo.MongoInfo, opts mongo.DialOpts, policy Policy) (*State, error) { st, err := open(tag, info, opts, policy) if err != nil { return nil, errors.Trace(err) } if _, err := st.Model(); err != nil { if err := st.Close(); err != nil { logger.Errorf("error closing state for unreadable model %s: %v", tag.Id(), err) } return nil, errors.Annotatef(err, "cannot read model %s", tag.Id()) } // State should only be Opened on behalf of a state server environ; all // other *States should be created via ForEnviron. if err := st.start(tag); err != nil { return nil, errors.Trace(err) } return st, nil }
// newState creates an incomplete *State, with a configured watcher but no // pwatcher, leadershipManager, or controllerTag. You must start() the returned // *State before it will function correctly. func newState(modelTag names.ModelTag, session *mgo.Session, mongoInfo *mongo.MongoInfo, policy Policy) (_ *State, resultErr error) { // Set up database. rawDB := session.DB(jujuDB) database, err := allCollections().Load(rawDB, modelTag.Id()) if err != nil { return nil, errors.Trace(err) } if err := InitDbLogs(session); err != nil { return nil, errors.Trace(err) } // Create State. return &State{ modelTag: modelTag, mongoInfo: mongoInfo, session: session, database: database, policy: policy, watcher: watcher.New(rawDB.C(txnLogC)), }, nil }
func (m *modelWorkerManager) modelIsDying(modelTag names.ModelTag) error { id := dyingModelWorkerId(modelTag.Id()) return m.runner.StartWorker(id, func() (worker.Worker, error) { st, err := m.st.ForModel(modelTag) if err != nil { return nil, errors.Annotatef(err, "failed to open state for model %s", modelTag.Id()) } closeState := func() { err := st.Close() if err != nil { logger.Errorf("error closing state for model %s: %v", modelTag.Id(), err) } } dyingRunner, err := m.dyingModelWorker(m.st, st) if err != nil { closeState() return nil, errors.Trace(err) } // Close State when the runner for the model is done. go func() { dyingRunner.Wait() closeState() }() return dyingRunner, nil }) }
func (c *dumpLogsCommand) dumpLogsForEnv(ctx *cmd.Context, st0 *state.State, tag names.ModelTag) error { st, err := st0.ForModel(tag) if err != nil { return errors.Annotate(err, "failed open model") } defer st.Close() logName := ctx.AbsPath(filepath.Join(c.outDir, fmt.Sprintf("%s.log", tag.Id()))) ctx.Infof("writing to %s", logName) file, err := os.Create(logName) if err != nil { return errors.Annotate(err, "failed to open output file") } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() tailer, err := state.NewLogTailer(st, &state.LogTailerParams{NoTail: true}) if err != nil { return errors.Annotate(err, "failed to create a log tailer") } logs := tailer.Logs() for { rec, ok := <-logs if !ok { break } writer.WriteString(c.format( rec.Time, rec.Level, rec.Entity, rec.Module, rec.Message, ) + "\n") } return nil }
func open(tag names.ModelTag, info *mongo.MongoInfo, opts mongo.DialOpts, policy Policy) (*State, error) { logger.Infof("opening state, mongo addresses: %q; entity %v", info.Addrs, info.Tag) logger.Debugf("dialing mongo") session, err := mongo.DialWithInfo(info.Info, opts) if err != nil { return nil, maybeUnauthorized(err, "cannot connect to mongodb") } logger.Debugf("connection established") err = mongodbLogin(session, info) if err != nil { session.Close() return nil, errors.Trace(err) } logger.Debugf("mongodb login successful") // In rare circumstances, we may be upgrading from pre-1.23, and not have the // model UUID available. In that case we need to infer what it might be; // we depend on the assumption that this is the only circumstance in which // the the UUID might not be known. if tag.Id() == "" { logger.Warningf("creating state without model tag; inferring bootstrap model") ssInfo, err := readRawStateServerInfo(session) if err != nil { session.Close() return nil, errors.Trace(err) } tag = ssInfo.ModelTag } st, err := newState(tag, session, info, policy) if err != nil { session.Close() return nil, errors.Trace(err) } return st, nil }