func (s *S) TestManaged(c *C) { conns := make([]*ldapConn, 0, 3) dials := 0 ldap.TestDial = func(c *ldap.Config) (ldap.Conn, error) { dials++ if dials == 1 { return nil, fmt.Errorf("temporary error") } conn := &ldapConn{config: c} if dials == 2 { conn.fail = true } conns = append(conns, conn) return conn, nil } defer func() { ldap.TestDial = nil }() mconn := ldap.DialManaged(config) conn := mconn.Conn() defer conn.Close() res, err := conn.Search(&ldap.Search{Filter: "test-filter1"}) c.Assert(err, ErrorMatches, "test-error") c.Assert(res, IsNil) c.Assert(conn.Close(), IsNil) c.Assert(conn.Close(), IsNil) _, err = conn.Search(&ldap.Search{}) c.Assert(err, ErrorMatches, "LDAP connection already closed") conn = mconn.Conn() defer conn.Close() c.Assert(mconn.Close(), IsNil) c.Assert(mconn.Close(), IsNil) res, err = conn.Search(&ldap.Search{Filter: "test-filter2"}) c.Assert(err, IsNil) c.Assert(res, HasLen, 1) c.Assert(res[0].DN, Equals, "test-dn") c.Assert(conn.Close(), IsNil) c.Assert(func() { mconn.Conn() }, PanicMatches, "ManagedConn.Conn called after closing connection") c.Assert(conns, HasLen, 2) c.Assert(conns[0].closed, Equals, true) c.Assert(conns[0].config, DeepEquals, config) c.Assert(conns[0].search.Filter, Equals, "test-filter1") c.Assert(conns[1].closed, Equals, true) c.Assert(conns[1].config, DeepEquals, config) c.Assert(conns[1].search.Filter, Equals, "test-filter2") }
func (m *pluginManager) refreshLdaps() { changed := false defer func() { if changed { m.ldapConnsMutex.Lock() m.ldapConns = make(map[string]*ldap.ManagedConn) for name, state := range m.ldaps { m.ldapConns[name] = state.conn } m.ldapConnsMutex.Unlock() } }() // Start new LDAP instances, and stop/restart updated ones. var raw bson.Raw var infos = make([]ldapInfo, 0, len(m.ldaps)) var found int var known = len(m.ldaps) iter := m.database.C("ldap").Find(nil).Iter() for iter.Next(&raw) { var info ldapInfo if err := raw.Unmarshal(&info); err != nil { logf("Cannot unmarshal LDAP document: %v", err) continue } infos = append(infos, info) if state, ok := m.ldaps[info.Name]; ok { found++ if bytes.Equal(state.raw.Data, raw.Data) { continue } logf("LDAP connection %q changed. Closing and restarting it.", info.Name) err := state.conn.Close() if err != nil { logf("LDAP connection %q closed with an error: %v", info.Name, err) } delete(m.ldaps, info.Name) } else { logf("LDAP %q starting.", info.Name) } m.ldaps[info.Name] = &ldapState{ raw: raw, info: info, conn: ldap.DialManaged(&info.Config), } changed = true } if iter.Err() != nil { // TODO Reduce frequency of logged messages if the database goes down. logf("Cannot fetch LDAP connection information from the database: %v", iter.Err()) return } // If there are known LDAPs that were not observed in the current // set of LDAPs, they must be stopped and removed. if known != found { NextLDAP: for name, state := range m.ldaps { for i := range infos { if infos[i].Name == name { continue NextLDAP } } logf("LDAP connection %q removed. Closing it.", state.info.Name) err := state.conn.Close() if err != nil { logf("LDAP connection %q closed with an error: %v", state.info.Name, err) } delete(m.ldaps, name) changed = true } } }