forked from Deuxfleurs/bottin
Browse Source
V2 the test end-to-end, Tests made similar to V1.0, Add the possibility to pararellize the tests, Create an environnement for easy integration of news test,test-go-Bottin

17 changed files with 840 additions and 490 deletions
@ -1,4 +1,4 @@ |
|||
bottin |
|||
bottin.static |
|||
config.json |
|||
test_automatic/integration |
|||
test/test |
|||
|
@ -0,0 +1,160 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"testing" |
|||
) |
|||
|
|||
func TestAddThenDelete(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
func TestConfirmAddAttributes(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Test search_attribute to confirm the Add
|
|||
if ok, err := inst.CompareOurDataWithConsul(); !ok { |
|||
t.Error(err) |
|||
} |
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
//Modifyrequest Test
|
|||
func TestModifyRequest(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Test modify all data (groups and users)
|
|||
err = inst.ModifyRandomAllData() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
func TestModifyRequestAndCheck(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Test modify all data (groups and users)
|
|||
err = inst.ModifyRandomAllData() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Check if the data was modify on Consul
|
|||
if ok, err := inst.CompareOurDataWithConsul(); !ok { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
func TestAddUserInGroup(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Add users in group
|
|||
err = inst.AddAllUsersInGroup() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
func TestDeleteGroupsAfterAddedUsers(t *testing.T) { |
|||
t.Parallel() |
|||
//SetUp - Create Users and Groups
|
|||
inst, err := Init() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Add users in group
|
|||
err = inst.AddAllUsersInGroup() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
|
|||
//Delete the half groups
|
|||
number := len(inst.dataGroups) / 2 |
|||
err = inst.clean(inst.dataGroups[0:number]) |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
inst.dataGroups = inst.dataGroups[number:len(inst.dataGroups)] |
|||
|
|||
//Check all the groups in memberOf exist
|
|||
ok, err := inst.CheckMemberOf() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
if !ok { |
|||
t.Errorf("Found group in memberOf that isn't in Consul.") |
|||
} |
|||
|
|||
//TearDown - Delete all the users and groups created
|
|||
err = inst.Clean() |
|||
if err != nil { |
|||
t.Error(err) |
|||
} |
|||
} |
|||
|
|||
//Example of paralellism Test
|
|||
func TestPrincipal(t *testing.T) { |
|||
|
|||
t.Run("A=1", TestAddThenDelete) |
|||
t.Run("A=2", TestModifyRequest) |
|||
if !testing.Short() { |
|||
t.Run("B=1", TestConfirmAddAttributes) |
|||
t.Run("B=2", TestModifyRequestAndCheck) |
|||
t.Run("C=1", TestAddUserInGroup) |
|||
t.Run("C=2", TestDeleteGroupsAfterAddedUsers) |
|||
} |
|||
|
|||
} |
@ -0,0 +1,281 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
"sync" |
|||
|
|||
"github.com/go-ldap/ldap/v3" |
|||
"github.com/sirupsen/logrus" |
|||
) |
|||
|
|||
//Mux value, this value permits do not have two identicals values in the parallel instances
|
|||
type StoreAllCN struct { |
|||
mu sync.Mutex |
|||
cn map[string]struct{} |
|||
} |
|||
|
|||
var allNames = StoreAllCN{cn: make(map[string]struct{})} |
|||
|
|||
//Type used for the tests
|
|||
type attributes struct { |
|||
Name string |
|||
Data []string |
|||
} |
|||
|
|||
type data_DN struct { |
|||
DN string |
|||
Attributes []attributes |
|||
} |
|||
|
|||
type instance struct { |
|||
numberUsers, numberGroups int |
|||
dataGroups, dataUsers []data_DN |
|||
logging *ldap.Conn |
|||
} |
|||
|
|||
//Create a new object instance
|
|||
//With this instance, we can obtain an isolated container where
|
|||
//we have our users and groups. It allows to run tests in parallel.
|
|||
func NewInstance(numberUsers, numberGroups int) (*instance, error) { |
|||
l, err := Connect() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
logging.Level = logrus.InfoLevel |
|||
|
|||
inst := instance{ |
|||
numberUsers: numberUsers, |
|||
numberGroups: numberGroups, |
|||
dataGroups: []data_DN{}, |
|||
dataUsers: []data_DN{}, |
|||
logging: l, |
|||
} |
|||
|
|||
err = inst.createOrganizationnalUnit() |
|||
if ldap.IsErrorWithCode(err, uint16(68)) { |
|||
logging.Warn("OrganizationnalUnit already created") |
|||
err = nil |
|||
} |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
err = inst.CreateGroups() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
err = inst.CreateUsers() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return &inst, nil |
|||
} |
|||
|
|||
//Part: Created users or groups or OU
|
|||
|
|||
func (inst *instance) createOrganizationnalUnit() error { |
|||
dn := []string{"ou=groups,dc=deuxfleurs,dc=fr", "ou=users,dc=deuxfleurs,dc=fr"} |
|||
attributes := []map[string][]string{{ |
|||
"description": []string{"OrganizationalUnit qui regroupe tous les groupes"}, |
|||
"objectclass": []string{"organizationalUnit", "top"}, |
|||
"ou": []string{"groups"}, |
|||
"structuralobjectclass": []string{"organizationalUnit"}, |
|||
}, |
|||
{ |
|||
"description": []string{"OrganizationalUnit qui regroupe tous les users"}, |
|||
"objectclass": []string{"organizationalUnit", "top"}, |
|||
"ou": []string{"users"}, |
|||
"structuralobjectclass": []string{"organizationalUnit"}, |
|||
}, |
|||
} |
|||
|
|||
for index := range dn { |
|||
err := inst.Add_Request(dn[index], attributes[index]) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
return nil |
|||
|
|||
} |
|||
|
|||
//Part: Create User or group
|
|||
|
|||
func (inst *instance) CreateUsers() (err error) { |
|||
|
|||
dn := "cn=%s,ou=users,dc=deuxfleurs,dc=fr" |
|||
attributes := map[string][]string{ |
|||
"displayname": {}, |
|||
"objectclass": {"inetOrgPerson", "organizationalPerson", "person", "top"}, |
|||
"structuralobjectclass": {"inetOrgPerson"}, |
|||
} |
|||
|
|||
du, err := inst.create(dn, []string{"displayname"}, inst.numberUsers, attributes, inst.dataUsers) |
|||
if err == nil { |
|||
inst.dataUsers = du |
|||
} |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) CreateGroups() error { |
|||
dn := "cn=%s,ou=groups,dc=deuxfleurs,dc=fr" |
|||
attributes := map[string][]string{ |
|||
"description": {}, |
|||
"objectclass": {"groupOfNames", "top"}, |
|||
"structuralobjectclass": {"groupOfNames"}, |
|||
} |
|||
|
|||
dg, err := inst.create(dn, []string{"description"}, inst.numberGroups, attributes, inst.dataGroups) |
|||
if err == nil { |
|||
inst.dataGroups = dg |
|||
} |
|||
|
|||
return err |
|||
} |
|||
|
|||
//Hard Function: She does:
|
|||
//- generate an unique name
|
|||
//- store the Data of each AddRequest in instance struct
|
|||
//- send AddRequest to Bottin
|
|||
func (inst *instance) create(dn string, unique_attr []string, number int, attributes map[string][]string, data []data_DN) ([]data_DN, error) { |
|||
for i := 0; i < number; i++ { |
|||
name := inst.GenerateName() |
|||
|
|||
datDn := data_DN{DN: fmt.Sprintf(dn, name)} |
|||
|
|||
for _, value := range unique_attr { |
|||
attributes[value] = []string{name} |
|||
} |
|||
|
|||
datDn.Attributes = MapAttToStruct(attributes) |
|||
data = append(data, datDn) |
|||
|
|||
err := inst.Add_Request(fmt.Sprintf(dn, name), attributes) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
return data, nil |
|||
} |
|||
|
|||
//Part: clean
|
|||
|
|||
func (inst *instance) Clean() error { |
|||
err := inst.CleanGroups() |
|||
if err != nil { |
|||
return err |
|||
} |
|||
err = inst.CleanUsers() |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) CleanUsers() error { |
|||
err := inst.clean(inst.dataUsers) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
inst.dataUsers = []data_DN{} |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) CleanGroups() error { |
|||
err := inst.clean(inst.dataGroups) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
inst.dataGroups = []data_DN{} |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) clean(stock []data_DN) error { |
|||
logging.Debugf("Delete %d elements.", len(stock)) |
|||
for _, value := range stock { |
|||
err := inst.Delete_Request(value.DN) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
//Part: Verify if a data_Dn is a group or an user
|
|||
func (inst *instance) VerifyUser(user data_DN) (bool, error) { |
|||
dn := "ou=users,dc=deuxfleurs,dc=fr" |
|||
cn := strings.Split(user.DN, ",")[0] |
|||
filter := fmt.Sprintf("(%s)", cn) |
|||
|
|||
res, err := inst.Search_Request(dn, filter, []string{"cn"}) |
|||
|
|||
return len(res.Entries) == 1, err |
|||
} |
|||
|
|||
func (inst *instance) VerifyGroup(group data_DN) (bool, error) { |
|||
dn := "ou=groups,dc=deuxfleurs,dc=fr" |
|||
cn := strings.Split(group.DN, ",")[0] |
|||
filter := fmt.Sprintf("(%s)", cn) |
|||
|
|||
res, err := inst.Search_Request(dn, filter, []string{"cn"}) |
|||
|
|||
return len(res.Entries) == 1, err |
|||
} |
|||
|
|||
//Part: Add user in a group
|
|||
func (inst *instance) AddUserInGroup(user, group data_DN) error { |
|||
|
|||
err := inst.Modify_Request(group.DN, nil, nil, map[string][]string{ |
|||
"member": {user.DN}, |
|||
}) |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) AddUserSliceInGroup(users_cn []string, group_dn string) error { |
|||
|
|||
err := inst.Modify_Request(group_dn, nil, nil, map[string][]string{ |
|||
"member": users_cn, |
|||
}) |
|||
return err |
|||
} |
|||
|
|||
//Part: modify, add, delete data_DN struct
|
|||
|
|||
func AddAtt(name string, data []string, dat data_DN) data_DN { |
|||
dat.Attributes = append(dat.Attributes, attributes{ |
|||
Name: name, |
|||
Data: data, |
|||
}) |
|||
|
|||
logging.Debug(fmt.Sprintf("Attributes %s add from %s.", name, dat.DN)) |
|||
return dat |
|||
} |
|||
|
|||
func DelAtt(name string, dat data_DN) data_DN { |
|||
for index, value := range dat.Attributes { |
|||
if value.Name == name { |
|||
dat.Attributes[index] = dat.Attributes[len(dat.Attributes)-1] |
|||
//tmp := dat.Attributes[:len(dat.Attributes)-1]
|
|||
dat.Attributes = []attributes{} |
|||
logging.Debugf("Attributes %s delete from %s.", name, dat.DN) |
|||
return dat |
|||
} |
|||
} |
|||
logging.Debugf("Can't delete attribute %s from %s.", name, dat.DN) |
|||
return dat |
|||
} |
|||
|
|||
func ReplaceAtt(name string, data []string, dat data_DN) data_DN { |
|||
for index, value := range dat.Attributes { |
|||
if value.Name == name { |
|||
dat.Attributes[index] = attributes{ |
|||
Name: name, |
|||
Data: data, |
|||
} |
|||
logging.Debugf("Replace attributes %s from %s succesful..", name, dat.DN) |
|||
return dat |
|||
} |
|||
} |
|||
logging.Debugf("Can't replace attributes %s from %s.", name, dat.DN) |
|||
return dat |
|||
} |
@ -0,0 +1,173 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
|
|||
"github.com/go-ldap/ldap/v3" |
|||
) |
|||
|
|||
const default_users, default_groups = 1000, 1000 |
|||
|
|||
func Init() (*instance, error) { |
|||
inst, err := NewInstance(default_users, default_groups) |
|||
return inst, err |
|||
} |
|||
|
|||
//Part to compare our datas
|
|||
func (inst *instance) CompareOurDataWithConsul() (bool, error) { |
|||
if ok, err := inst.VerifyOurData(inst.dataUsers); !ok { |
|||
return false, err |
|||
} |
|||
if ok, err := inst.VerifyOurData(inst.dataGroups); !ok { |
|||
return false, err |
|||
} |
|||
return true, nil |
|||
} |
|||
|
|||
func (inst *instance) VerifyOurData(tabData []data_DN) (bool, error) { |
|||
for _, value := range tabData { |
|||
names := getNamesAtt(value) |
|||
cn := strings.Split(value.DN, ",")[0] |
|||
res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), names) |
|||
if err != nil { |
|||
return false, err |
|||
} |
|||
if len(res.Entries) != 1 { |
|||
return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries)) |
|||
} |
|||
if !Compare(value, res.Entries[0]) { |
|||
return false, fmt.Errorf("no match with the DN: %s", value.DN) |
|||
} |
|||
} |
|||
return true, nil |
|||
} |
|||
|
|||
func Compare(dat data_DN, ent *ldap.Entry) bool { |
|||
for _, value := range dat.Attributes { |
|||
logging.Debugf("Attributes from %s is now: %s.", dat.DN, dat.Attributes) |
|||
entVal := GetAttributeValuesBottin(ent, value.Name) |
|||
logging.Debugf("Values of the Entry: attributName: %s, Values: %s.", value.Name, entVal) |
|||
if !CompareSliceString(entVal, value.Data) { |
|||
logging.Debugf("Values expected: %s, values found: %s.", value.Data, entVal) |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
//Part modify datas
|
|||
func (inst *instance) ModifyRandomAllData() error { |
|||
dg, err := inst.ModifyRandom(inst.dataGroups, []string{"description"}) |
|||
if err != nil { |
|||
return err |
|||
} else { |
|||
inst.dataGroups = dg |
|||
} |
|||
|
|||
dg, err = inst.ModifyRandom(inst.dataUsers, []string{"displayname"}) |
|||
if err != nil { |
|||
return err |
|||
} else { |
|||
inst.dataUsers = dg |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
//Function which modify random way the attributes in attName of a data_DN's slice, it can delete, replace and delete
|
|||
//The function modify also in the dat object
|
|||
func (inst *instance) ModifyRandom(dat []data_DN, attName []string) ([]data_DN, error) { |
|||
for index, value := range dat { |
|||
del := make(map[string][]string) |
|||
add := make(map[string][]string) |
|||
replace := make(map[string][]string) |
|||
|
|||
for _, att := range attName { |
|||
|
|||
switch selNumber := R.Intn(3); selNumber { |
|||
case 0: |
|||
del[att] = []string{} |
|||
value = DelAtt(att, value) |
|||
logging.Debug(fmt.Sprintf("Delete the attribute %s of the DN %s.", att, value.DN)) |
|||
case 1: |
|||
name := inst.GenerateName() |
|||
value = AddAtt(name, []string{name}, value) |
|||
add[name] = []string{name} |
|||
logging.Debug(fmt.Sprintf("Add the attribute %s with value %s of the DN %s.", name, name, value.DN)) |
|||
case 2: |
|||
name := inst.GenerateName() |
|||
value = ReplaceAtt(att, []string{name}, value) |
|||
replace[att] = []string{name} |
|||
logging.Debug(fmt.Sprintf("Replace the attribute %s with value %s of the DN %s.", att, name, value.DN)) |
|||
} |
|||
|
|||
} |
|||
|
|||
err := inst.Modify_Request(value.DN, add, del, replace) |
|||
if err != nil { |
|||
return dat, err |
|||
} |
|||
dat[index] = value |
|||
} |
|||
return dat, nil |
|||
} |
|||
|
|||
//Add all users in a random group
|
|||
func (inst *instance) AddAllUsersInGroup() error { |
|||
for _, value := range inst.dataGroups { |
|||
valueRand := (len(inst.dataUsers) + 1) / 30 |
|||
if valueRand == 0 { |
|||
valueRand = 1 |
|||
} |
|||
numberOfMembers := R.Intn(valueRand) + 1 |
|||
logging.Debugf("%s will be have %d members.", value.DN, numberOfMembers) |
|||
|
|||
groupMemory := make(map[int]struct{}) |
|||
users_cn := []string{} |
|||
|
|||
for i := 0; i < numberOfMembers; i++ { |
|||
selectGroup := R.Intn(len(inst.dataUsers)) |
|||
for _, ok := groupMemory[selectGroup]; ok; _, ok = groupMemory[selectGroup] { |
|||
selectGroup = R.Intn(len(inst.dataUsers)) |
|||
|
|||
logging.Debugf("Search an other member. The value is %d , and we have %d members available.", selectGroup, len(inst.dataUsers)) |
|||
} |
|||
groupMemory[selectGroup] = struct{}{} |
|||
|
|||
users_cn = append(users_cn, inst.dataGroups[selectGroup].DN) |
|||
|
|||
} |
|||
err := inst.AddUserSliceInGroup(users_cn, value.DN) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
} |
|||
return nil |
|||
} |
|||
|
|||
//Check if the groups in memberOf exist in Consul
|
|||
func (inst *instance) CheckMemberOf() (bool, error) { |
|||
for _, value := range inst.dataUsers { |
|||
cn := strings.Split(value.DN, ",")[0] |
|||
res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), []string{"memberOf"}) |
|||
if err != nil { |
|||
return false, err |
|||
} |
|||
if len(res.Entries) != 1 { |
|||
return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries)) |
|||
} |
|||
attValues := GetAttributeValuesBottin(res.Entries[0], "memberOf") |
|||
for _, dnGroup := range attValues { |
|||
logging.Debugf("Verify if the group %s exist...", dnGroup) |
|||
ok, err := inst.VerifyGroup(data_DN{DN: dnGroup}) |
|||
if err != nil { |
|||
return false, err |
|||
} |
|||
if !ok { |
|||
return false, fmt.Errorf("don't found the group: %s", dnGroup) |
|||
} |
|||
} |
|||
} |
|||
return true, nil |
|||
} |
@ -0,0 +1,138 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"math/rand" |
|||
"os" |
|||
|
|||
ldap "github.com/go-ldap/ldap/v3" |
|||
"github.com/sirupsen/logrus" |
|||
) |
|||
|
|||
const maxlength_generateName, minlength_generateName = 25, 3 |
|||
|
|||
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr" |
|||
const adresse = "127.0.0.1" |
|||
const port = 1389 |
|||
|
|||
var logging = logrus.New() |
|||
|
|||
const seed = 654258 |
|||
|
|||
var R = rand.New(rand.NewSource(seed)) |
|||
|
|||
var bindpassword = "sf7yO52NCuE" |
|||
|
|||
//Handler just to facilite the print error
|
|||
func PrintError(LDAPError error) { |
|||
if LDAPError != nil { |
|||
logging.Fatal(LDAPError) |
|||
} |
|||
} |
|||
|
|||
//Generate an unique name, which store in all_names
|
|||
func (inst *instance) GenerateName() (name string) { |
|||
alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
|||
length := R.Intn(maxlength_generateName) + minlength_generateName |
|||
|
|||
//Check if this name not exist already
|
|||
//Lock thhis variable because she is hared with other goroutine
|
|||
allNames.mu.Lock() |
|||
for only_one := true; only_one; _, only_one = allNames.cn[name] { |
|||
//Create the name
|
|||
for i := 0; i < length; i++ { |
|||
name += string(alphabet[R.Intn(len(alphabet))]) |
|||
} |
|||
|
|||
} |
|||
//Add the new name in the map to store this one
|
|||
allNames.cn[name] = struct{}{} |
|||
allNames.mu.Unlock() |
|||
logging.Debug(fmt.Sprintf("Name generated: %s.", name)) |
|||
return |
|||
} |
|||
|
|||
//Handler to around the bug with MessageId
|
|||
func (inst *instance) Reconnect() (err error) { |
|||
inst.logging.Close() |
|||
inst.logging, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) |
|||
if err != nil { |
|||
return |
|||
} |
|||
err = inst.logging.Bind(bindusername, bindpassword) |
|||
//logging.Debug("Reconnect succesful")
|
|||
return |
|||
} |
|||
|
|||
//Transform attributes in map format to the struct attributes
|
|||
func MapAttToStruct(att map[string][]string) []attributes { |
|||
resultat := []attributes{} |
|||
for key, value := range att { |
|||
logging.Debug(fmt.Sprintf("Transform: key: %s, values: %s to attributes struct.\n", key, value)) |
|||
resultat = append(resultat, attributes{ |
|||
Name: key, |
|||
Data: value, |
|||
}) |
|||
} |
|||
return resultat |
|||
} |
|||
|
|||
func Connect() (*ldap.Conn, error) { |
|||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok { |
|||
bindpassword = key |
|||
} |
|||
|
|||
err = l.Bind(bindusername, bindpassword) |
|||
logging.Debug("Connection succesful") |
|||
return l, err |
|||
} |
|||
|
|||
//Handler to get only attributes names
|
|||
func getNamesAtt(dat data_DN) []string { |
|||
resultat := []string{} |
|||
for _, values := range dat.Attributes { |
|||
resultat = append(resultat, values.Name) |
|||
} |
|||
return resultat |
|||
} |
|||
|
|||
//Handler to compare slice string
|
|||
func CompareSliceString(string1, string2 []string) bool { |
|||
if len(string1) != len(string2) { |
|||
return false |
|||
} else { |
|||
for index := range string1 { |
|||
if string1[index] != string2[index] { |
|||
return false |
|||
} |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
//Handler to remove an element in slice string
|
|||
func DeleteElementSliceString(s string, sSlice []string) []string { |
|||
|
|||
for index, value := range sSlice { |
|||
if value == s { |
|||
sSlice[index] = sSlice[len(sSlice)-1] |
|||
return sSlice[:len(sSlice)-1] |
|||
} |
|||
} |
|||
return sSlice |
|||
} |
|||
|
|||
//Get attributes entry values bottin bug
|
|||
func GetAttributeValuesBottin(ent *ldap.Entry, name string) (res []string) { |
|||
for _, val := range ent.Attributes { |
|||
if val.Name == name { |
|||
res = append(res, val.Values...) |
|||
} |
|||
} |
|||
return |
|||
} |
@ -0,0 +1,67 @@ |
|||
package main |
|||
|
|||
import ( |
|||
ldap "github.com/go-ldap/ldap/v3" |
|||
) |
|||
|
|||
func (inst *instance) Add_Request(dn string, attributes map[string][]string) error { |
|||
//Create the AddRequest
|
|||
req := ldap.NewAddRequest(dn, nil) |
|||
for key, value := range attributes { |
|||
req.Attribute(key, value) |
|||
} |
|||
|
|||
//Send the request
|
|||
err := inst.logging.Add(req) |
|||
//@FIXME: Remove when you try to correct the bug MessageID
|
|||
inst.Reconnect() |
|||
return err |
|||
|
|||
} |
|||
|
|||
//Use enum to select Replace,Delete,Modify
|
|||
func (inst *instance) Modify_Request(dn string, add_attributes, delete_attributes, replace_attributes map[string][]string) error { |
|||
modifyReq := ldap.NewModifyRequest(dn, nil) |
|||
|
|||
for key, value := range add_attributes { |
|||
modifyReq.Add(key, value) |
|||
} |
|||
|
|||
for key, value := range delete_attributes { |
|||
modifyReq.Delete(key, value) |
|||
} |
|||
|
|||
for key, value := range replace_attributes { |
|||
modifyReq.Replace(key, value) |
|||
} |
|||
|
|||
err := inst.logging.Modify(modifyReq) |
|||
//@FIXME: Remove when you try to correct the bug MessageID
|
|||
inst.Reconnect() |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) Delete_Request(dn string) error { |
|||
del := ldap.NewDelRequest(dn, nil) |
|||
|
|||
err := inst.logging.Del(del) |
|||
//@FIXME: Remove when you try to correct the bug MessageID
|
|||
inst.Reconnect() |
|||
return err |
|||
} |
|||
|
|||
func (inst *instance) Search_Request(dn, filter string, name_attributes []string) (*ldap.SearchResult, error) { |
|||
searchReq := ldap.NewSearchRequest( |
|||
dn, |
|||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
|||
filter, |
|||
name_attributes, |
|||
nil, |
|||
) |
|||
|
|||
res, err := inst.logging.Search(searchReq) |
|||
logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes) |
|||
//@FIXME: Remove when you try to correct the bug MessageID
|
|||
inst.Reconnect() |
|||
return res, err |
|||
} |
@ -0,0 +1,12 @@ |
|||
#!/bin/sh |
|||
|
|||
set -ex |
|||
|
|||
echo $BOTTIN_DEFAULT_ADMIN_PW |
|||
consul agent -dev > /dev/null 2>&1 & |
|||
sleep 2 |
|||
cp test/config.json.test config.json |
|||
./bottin > /dev/null 2>&1 & |
|||
sleep 1 |
|||
./test/test -test.v -test.failfast -test.short -test.run TestPrincipal |
|||
./test/test -test.v -test.failfast -test.run TestPrincipal/B= |
Binary file not shown.
Binary file not shown.
@ -1,431 +0,0 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"github.com/go-ldap/ldap/v3" |
|||
"fmt" |
|||
log "github.com/sirupsen/logrus" |
|||
"math/rand" |
|||
"strings" |
|||
"errors" |
|||
"os" |
|||
) |
|||
|
|||
|
|||
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr" |
|||
const adresse = "127.0.0.1" |
|||
const port = 1389 |
|||
var bindpassword string |
|||
|
|||
var all_names = make(map[string]struct{}) |
|||
|
|||
|
|||
|
|||
func printError(LDAPError error) { |
|||
if LDAPError != nil { |
|||
log.Fatal(LDAPError) |
|||
} |
|||
} |
|||
|
|||
func createOU(l *ldap.Conn) error { |
|||
|
|||
req := ldap.NewAddRequest("ou=groups,dc=deuxfleurs,dc=fr",nil) |
|||
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les groupes"}) |
|||
req.Attribute("objectclass",[]string{"organizationalUnit", "top"}) |
|||
req.Attribute("ou",[]string{"groups"}) |
|||
req.Attribute("structuralobjectclass", []string{"organizationalUnit"}) |
|||
|
|||
err := l.Add(req) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
req = ldap.NewAddRequest("ou=users,dc=deuxfleurs,dc=fr",nil) |
|||
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les utilisateurs"}) |
|||
req.Attribute("objectclass",[]string{"organizationalUnit", "top"}) |
|||
req.Attribute("ou",[]string{"users"}) |
|||
req.Attribute("structuralobjectclass", []string{"organizationalUnit"}) |
|||
|
|||
err = l.Add(req) |
|||
return err |
|||
} |
|||
|
|||
func generateName(r *rand.Rand) (name string) { |
|||
for only_one := true; only_one; _, only_one = all_names[name]{ |
|||
name = fmt.Sprintf("%d",r.Int()) |
|||
} |
|||
all_names[name] = struct{}{} |
|||
log.Debug(fmt.Sprintf("Name generated: %s.\n", name)) |
|||
return |
|||
} |
|||
|
|||
func createGroup(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) { |
|||
StructuralObjectClass := []string{"groupOfNames"} |
|||
ObjectClass := []string{"groupOfNames","top"} |
|||
|
|||
|
|||
|
|||
for i := 0; i<20; i++ { |
|||
//Generate name and check if he is unique
|
|||
name := generateName(r) |
|||
|
|||
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=groups,dc=deuxfleurs,dc=fr",name),nil) |
|||
req.Attribute("description",[]string{generateName(r)}) |
|||
req.Attribute("objectclass",ObjectClass) |
|||
req.Attribute("structuralobjectclass",StructuralObjectClass) |
|||
|
|||
err = l.Add(req) |
|||
if err != nil { |
|||
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] groupe.\n",i)) |
|||
return nil, err |
|||
} |
|||
tab_AddRequest = append(tab_AddRequest, *req) |
|||
|
|||
} |
|||
return |
|||
} |
|||
|
|||
func createUser(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) { |
|||
StructuralObjectClass := []string{"inetOrgPerson"} |
|||
ObjectClass := []string{"inetOrgPerson","organizationalPerson","person","top"} |
|||
|
|||
for i := 0; i<20; i++ { |
|||
name := generateName(r) |
|||
|
|||
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=users,dc=deuxfleurs,dc=fr",name),nil) |
|||
req.Attribute("displayname",[]string{generateName(r)}) |
|||
req.Attribute("objectclass",ObjectClass) |
|||
req.Attribute("structuralobjectclass",StructuralObjectClass) |
|||
|
|||
err = l.Add(req) |
|||
if err != nil { |
|||
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] user.\n",i)) |
|||
return nil, err |
|||
} |
|||
tab_AddRequest = append(tab_AddRequest, *req) |
|||
|
|||
} |
|||
return |
|||
} |
|||
|
|||
func search_attributes(tab_Attributes []ldap.Attribute, tipe string) (*ldap.Attribute) { |
|||
for _,att := range tab_Attributes { |
|||
if att.Type == tipe { |
|||
return &att |
|||
} |
|||
} |
|||
return nil |
|||
// return ldap.Attribute{}
|
|||
} |
|||
|
|||
func test_attributes(l *ldap.Conn, tab_AddRequest []ldap.AddRequest, filter_objectclass, user_or_group string) (err error) { |
|||
for _, addRequest := range tab_AddRequest { |
|||
|
|||
//On prend le cn en supposant qu'il est unique
|
|||
cn := strings.Split(addRequest.DN,",")[0] |
|||
|
|||
//On crée la requête pour la recherche
|
|||
search_req := ldap.NewSearchRequest( |
|||
fmt.Sprintf("ou=%s,dc=deuxfleurs,dc=fr",user_or_group), |
|||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
|||
fmt.Sprintf("(&(objectclass=%s)(%s))", filter_objectclass,cn), |
|||
[]string{"displayname","objectclass","structuralobjectclass"}, |
|||
nil, |
|||
) |
|||
|
|||
//On lance la recherche
|
|||
result, err := l.Search(search_req) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if len(result.Entries) != 1 { |
|||
return errors.New("Test a trouvé plusieurs displaynames en commun ou en a trouvé aucun") |
|||
} |
|||
|
|||
//On compare les attributs qu'on a reçu avec les attributs qu'on a envoyé
|
|||
result_attributes := result.Entries[0].Attributes |
|||
log.Debug(fmt.Sprintf("La longueur est de %d, contient : \n %s.\n",len(result_attributes), result_attributes)) |
|||
|
|||
//Notre recherche crée un attribut par valeur, même si les valeurs viennent du même nom d'attribut
|
|||
//Par exemple: objectclass possède 4 valeurs. Alors on aura 4 EntryAttribute qui contient chacune une des 4 valeurs de l'attribut objectclass
|
|||
|
|||
//j est l'indice qui représente la j-ème valeur de notre attribut
|
|||
var j int |
|||
var att *ldap.Attribute |
|||
for i,attributes := range result_attributes { |
|||
//On cherche l'attribut de l'user i qui a le même nom que celui qu'on a reçu et qu'on traite dans cette boucle
|
|||
if j == 0 { |
|||
att = search_attributes(addRequest.Attributes, attributes.Name) |
|||
if att == nil { |
|||
return errors.New(fmt.Sprintf("Error: test_attributes - Don't find match name attributes. We search %s.\n", attributes.Name)) |
|||
} |
|||
} |
|||
log.Debug(fmt.Sprintf("Le nom de l'attribut est %s, sa valeur est: \n %s.", att.Type, att)) |
|||
|
|||
if j >= len(att.Vals) || att.Vals[j] != attributes.Values[0] { |
|||
return errors.New(fmt.Sprintf("Error: test_attributes - Theses values aren't the same: %d, %d",att.Vals, attributes.Values)) |
|||
} |
|||
|
|||
if i+1 < len(result_attributes) && result_attributes[i+1].Name == attributes.Name { |
|||
j += 1 |
|||
} else { j = 0} |
|||
} |
|||
|
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func clean(l *ldap.Conn, AddReq_users, AddReq_groups []ldap.AddRequest,user, group bool) (err error){ |
|||
log.Debug("Debut clean") |
|||
if(user) { |
|||
for _,req := range AddReq_users { |
|||
delReq := ldap.NewDelRequest(req.DN,nil) |
|||
err = l.Del(delReq) |
|||
if err != nil { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
if group { |
|||
for _,req := range AddReq_groups { |
|||
delReq := ldap.NewDelRequest(req.DN, nil) |
|||
err = l.Del(delReq) |
|||
if err != nil { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
defer log.Debug("Fin clean") |
|||
return |
|||
} |
|||
|
|||
func test_modify_attributes(l *ldap.Conn, r *rand.Rand, tab_AddReq []ldap.AddRequest, tab_type_name []string) (err error) { |
|||
for _, AddReq := range tab_AddReq { |
|||
modReq := ldap.NewModifyRequest(AddReq.DN,nil) |
|||
for _, type_name := range tab_type_name { |
|||
newName := generateName(r) |
|||
modReq.Replace(type_name, []string{newName}) |
|||
att := search_attributes(AddReq.Attributes, type_name) |
|||
att.Vals[0] = newName |
|||
} |
|||
err = l.Modify(modReq) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
} |
|||
return |
|||
} |
|||
|
|||
func add_user_in_groups(l *ldap.Conn, r *rand.Rand, users, groups []ldap.AddRequest) (err error) { |
|||
for _,group := range groups { |
|||
numberUsers := r.Intn(19) + 1 //Always a minimum of 1 user
|
|||
list_users := []string{} |
|||
for i:=0; i < numberUsers; i++ { |
|||
list_users = append(list_users, users[i].DN) |
|||
} |
|||
modifyReq := ldap.NewModifyRequest( group.DN, nil) |
|||
modifyReq.Add("member", list_users) |
|||
|
|||
err = l.Modify(modifyReq) |
|||
if err != nil { |
|||
log.Warn(fmt.Sprintf("Error: ModifyReq failed, func:add_users_in_groups from group:\n %d",group)) |
|||
return |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
func delete_groups(l *ldap.Conn, groups []ldap.AddRequest) (list map[string][]string ,err error) { |
|||
list = make(map[string][]string) |
|||
for _, group := range groups { |
|||
//Get lists_users
|
|||
cn := strings.Split(group.DN,",")[0] |
|||
search_req := ldap.NewSearchRequest( |
|||
"ou=groups,dc=deuxfleurs,dc=fr", |
|||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
|||
fmt.Sprintf("(&(objectclass=groupOfNames)(%s))",cn), |
|||
[]string{"member"}, |
|||
nil, |
|||
) |
|||
res , err := l.Search(search_req) |
|||
if err != nil { |
|||
log.Warn(fmt.Sprintf("Error Search: func: delete_groups_and_check_memberOf, from group: \n %d", group)) |
|||
return list, err |
|||
} |
|||
if len(res.Entries) != 1 { |
|||
err = errors.New(fmt.Sprintf("SearchResult get: %s, SearchResult wanted: 1", len(res.Entries))) |
|||
return list, err |
|||
} |
|||
EntryAtt := res.Entries[0].Attributes |
|||
list_users := []string{} |
|||
for _, att := range EntryAtt { |
|||
list_users = append(list_users ,att.Values[0]) |
|||
} |
|||
|
|||
//Del group
|
|||
del := ldap.NewDelRequest( group.DN, nil) |
|||
err = l.Del(del) |
|||
if err != nil { |
|||
return list, err |
|||
} |
|||
list[group.DN] = list_users |
|||
} |
|||
return |
|||
} |
|||
|
|||
func check_memberOf(l *ldap.Conn, list map[string][]string) (err error) { |
|||
//Check the memberOf of all users
|
|||
for groupeDN,_ := range list{ |
|||
search_req := ldap.NewSearchRequest( |
|||
"ou=users,dc=deuxfleurs,dc=fr", |
|||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,0 ,0, false, |
|||
fmt.Sprintf("(&(objectclass=inetOrgPerson)(memberOf=%s))",groupeDN), |
|||
[]string{"cn"}, |
|||
nil, |
|||
) |
|||
res, err := l.Search(search_req) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if len(res.Entries) != 0 { |
|||
err = errors.New(fmt.Sprintf("L'user '%s' a encore le DN d'un groupe supprimé: %s",res.Entries[0].Attributes[0].Values[0],groupeDN)) |
|||
return err |
|||
} |
|||
} |
|||
return err |
|||
} |
|||
|
|||
func reconnect(l *ldap.Conn) (l_nouv *ldap.Conn, err error){ |
|||
l.Close() |
|||
l_nouv, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse,port)) |
|||
if err != nil { |
|||
return |
|||
} |
|||
err = l_nouv.Bind(bindusername, bindpassword) |
|||
return |
|||
} |
|||
|
|||
|
|||
func main() { |
|||
var ok bool |
|||
bindpassword, ok = os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW") |
|||
if !ok { |
|||
if len(os.Args) == 2 { |
|||
bindpassword = os.Args[1] |
|||
} else { |
|||
bindpassword = "" |
|||
} |
|||
} |
|||
|
|||
log.Info(fmt.Sprintf("Password selected: %s",bindpassword)) |
|||
//log.SetLevel(log.TraceLevel)
|
|||
|
|||
//Create a connection with Bottin server
|
|||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) |
|||
//l.Debug = true
|
|||
printError(err) |
|||
|
|||
//Bind with the admin account generated
|
|||
err = l.Bind(bindusername, bindpassword) |
|||
printError(err) |
|||
|
|||
//Create our object Rand, it's important to always have the same values
|
|||
source := rand.NewSource(666475745) |
|||
r := rand.New(source) |
|||
log.Info(fmt.Sprintf("The seed of the rand object is %d.\n",r.Seed)) |
|||
|
|||
//Create user and groups OrgaUnit
|
|||
err = createOU(l) |
|||
if ldap.IsErrorWithCode(err, uint16(68)) { |
|||
log.Warn("Les OrganizationalUnit users et groups sont déjà présents.") |
|||
}else { |
|||
printError(err) |
|||
log.Info("Création des OU de groups et users") |
|||
} |
|||
|
|||
//Create random groups
|
|||
tab_AddRequest_groups, err := createGroup(r, l) |
|||
printError(err) |
|||
log.Info(fmt.Sprintf("Création des groupes aléatoirement réussi: %d\n", len(tab_AddRequest_groups))) |
|||
|
|||
//Create random users
|
|||
tab_AddRequest_users, err := createUser(r, l) |
|||
printError(err) |
|||
log.Info(fmt.Sprintf("Création des users aléatoirement réussi: %d\n", len(tab_AddRequest_users))) |
|||
|
|||
//Search and compare attribute Users. (We keep Attribute object from 'Create random users' and compare with the result of our search)
|
|||
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson","users") |
|||
printError(err) |
|||
log.Info("Tous les attributs users insérés dans Consul ont été vérifiés..\n") |
|||
|
|||
//Search and compare attributes Groups
|
|||
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames","groups") |
|||
printError(err) |
|||
log.Info("Tous les attributs groups insérés dans Consul ont été vérifiés.\n") |
|||
|
|||
|
|||
//Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
|
|||
l,err = reconnect(l) |
|||
printError(err) |
|||
//Modify attributes users and groups.
|
|||
|
|||
//Modify users' attributes and check them
|
|||
|
|||
log.Debug(fmt.Sprintf("Les valeurs sont:\n %s", tab_AddRequest_users)) |
|||
err = test_modify_attributes(l, r, tab_AddRequest_users, []string{"displayname"}) |
|||
printError(err) |
|||
log.Debug("Modifications users faites") |
|||
|
|||
//Check if the attributes are correct:
|
|||
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson", "users") |
|||
printError(err) |
|||
log.Info("Les modifications ont bien été prises en compte") |
|||
log.Debug(fmt.Sprintf("Les nouvelles valeurs sont:\n %s", tab_AddRequest_users)) |
|||
|
|||
|
|||
|
|||
|
|||
//Modify users' attributes and check them
|
|||
err = test_modify_attributes(l, r, tab_AddRequest_groups, []string{"description"}) |
|||
printError(err) |
|||
log.Info("Modifications groups faites") |
|||
|
|||
//Check if the attributes are correct:
|
|||
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames", "groups") |
|||
printError(err) |
|||
log.Info("Les modifications ont bien été prises en compte") |
|||
|
|||
//Close the connection
|
|||
l, err = reconnect(l) |
|||
printError(err) |
|||
|
|||
//Add users in group, search them, delete several samples and search again to be sur it's good
|
|||
err = add_user_in_groups(l, r, tab_AddRequest_users, tab_AddRequest_groups) |
|||
printError(err) |
|||
log.Info("Ajout d'users dans les groupes fait") |
|||
|
|||
//Close the connection
|
|||
l, err = reconnect(l) |
|||
printError(err) |
|||
|
|||
list, err := delete_groups(l, tab_AddRequest_groups) |
|||
printError(err) |
|||
log.Info("groupe supprimé") |
|||
|
|||
|
|||
l,err = reconnect(l) |
|||
printError(err) |
|||
|
|||
err = check_memberOf(l, list) |
|||
printError(err) |
|||
log.Info("Le memberOf a été correctement vidé") |
|||
|
|||
//Clean: Delete all users and groups (not OU users and groups)
|
|||
err = clean(l, tab_AddRequest_users, tab_AddRequest_groups, true, false) |
|||
printError(err) |
|||
log.Info("Clean succes") |
|||
|
|||
defer os.Exit(0) |
|||
return |
|||
|
|||
} |
@ -1,40 +0,0 @@ |
|||
Introduction et Observation premières: |
|||
|
|||
Lors de la réalisation de mon code Go, j'ai trouvé un beug qui provoquait l'arrêt de mon programme car Bottin envoyé une réponse erronée à mon programme. |
|||
Dans logs de Bottin je ne voyais aucune erreur, Bottin n'avait pas cessé de fonctionner. Il s'arrêtait à chaque fois sur mes requêtes Del (mes dernières). |
|||
|
|||
|
|||
Reproduction du beug: |
|||
|
|||
Pour reproduire le beug, il suffit de lancer le programme interrogation.go et de commenter les lignes suivantes : |
|||
|
|||
260 //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this |
|||
261 l.Close() |
|||
262 l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse, port)) |
|||
263 printError(err) |
|||
264 err = l.Bind(bindusername, bindpassword) |
|||
265 printError(err) |
|||
|
|||
Ainsi on obtient l'erreur suivante: |
|||
|
|||
2021/07/07 01:25:51 Received unexpected message -128, false |
|||
|
|||
|
|||
Test réalisé pour comprendre la source du problème: |
|||
|
|||
Ma première hypothèses fut que j'envoyais trop de requêtes dans un court laps de temps et ainsi Bottin ou Consul ne pouvait pas suivre. |
|||
J'ai placé un sleep de 50 puis de 100 Millisecondes entre chaque requête, j'obtenais toujours la même erreur. |
|||
J'ai essayé de mettre un sleep de 10 secondes avant mes requêtes de suppression mais j'obtenais toujours la même chose. |
|||
|
|||
Hack pour résoudre: |
|||
|
|||
La première solution qui a fonctionné était de réduire le nombre de requêtes en n'exécutant pas certains tests random. |
|||
La dernière solutions qui est utilisée: |
|||
Fermée la connexion puis la réouvrir permet de palier à ce problème |
|||
|
|||
Hypothèses du problème ?: |
|||
|
|||
Existerait-il un Buffer par Bind qui se remplirait ? Et lorsque celui-ci est plein, ne renvoie pas d'erreur juste n'arrive plus à répondre. |
|||
|
|||
Erwan DUFOUR |
|||
Deuxfleurs |
@ -1,12 +0,0 @@ |
|||
#!/bin/sh |
|||
|
|||
#export BOTTIN_DEFAULT_ADMIN_PW=$(openssl rand -base64 24) |
|||
echo $BOTTIN_DEFAULT_ADMIN_PW |
|||
consul agent -dev > /dev/null 2>&1 & |
|||
sleep 2 |
|||
cp test_automatic/config.json.test config.json |
|||
./bottin > /dev/null 2>&1 & |
|||
sleep 1 |
|||
./test_automatic/integration |
|||
rm config.json |
|||
exit 0 |
Loading…
Reference in new issue