From b4070b3f8050d9caaa73aed770c19dfb04a5e3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20BERSAC?= Date: Wed, 22 Oct 2025 15:56:13 +0200 Subject: [PATCH 1/5] cmd: More profiling and tracing --- .gitignore | 2 ++ go.mod | 3 +++ go.sum | 15 +++++++++++++++ internal/cmd/ldap2pg.go | 30 ++---------------------------- internal/cmd/profile.go | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 internal/cmd/profile.go diff --git a/.gitignore b/.gitignore index f1bdf175..b1ddd107 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ dist/ docker-compose.override.yml # test/conftest.py creates .env files .env +trace.out +*.pprof diff --git a/go.mod b/go.mod index 8d3b6466..9822c4e2 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/lmittmann/tint v1.1.3 github.com/mattn/go-isatty v0.0.22 github.com/mitchellh/mapstructure v1.5.0 + github.com/pkg/profile v1.7.0 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 @@ -29,8 +30,10 @@ require ( require ( github.com/Azure/go-ntlmssp v0.1.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/felixge/fgprof v0.9.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/google/uuid v1.6.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/go.sum b/go.sum index 7e0bb454..8eaa700c 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,18 @@ github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktp github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio= github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.9.0 h1:prva4eP9UysWagLyKrtn074ughi0NnkIf0A4M5yOCKI= github.com/deckarep/golang-set/v2 v2.9.0/go.mod h1:EWknQXbs0mcFpat2QOoXV0Ee57cD+w6ZEN76BR2JVrM= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= @@ -18,6 +24,8 @@ github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPE github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= @@ -26,6 +34,7 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6 github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -75,6 +84,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -83,8 +94,11 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU= @@ -95,6 +109,7 @@ golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= diff --git a/internal/cmd/ldap2pg.go b/internal/cmd/ldap2pg.go index d738a880..d37c5839 100644 --- a/internal/cmd/ldap2pg.go +++ b/internal/cmd/ldap2pg.go @@ -9,7 +9,6 @@ import ( "os" "runtime" "runtime/debug" - "runtime/pprof" "slices" "strings" "time" @@ -69,12 +68,9 @@ func ldap2pg() (err error) { start := time.Now() - stop, err := startProfiling() - if err != nil { - return - } + stop := startProfiling() if stop != nil { - defer stop() + defer stop.Stop() } defer postgres.CloseConn(ctx) @@ -314,25 +310,3 @@ func logPanic() { slog.Error("Please file an issue at https://github.com/dalibo/ldap2pg/issue/new with verbose log.") os.Exit(1) } - -func startProfiling() (stop func(), err error) { - if !slices.Contains(os.Environ(), "CPUPROFILE=1") { - return - } - slog.Debug("Starting CPU profiling.") - f, err := os.Create("default.pgo") - if err != nil { - return - } - err = pprof.StartCPUProfile(f) - if err != nil { - _ = f.Close() - return - } - stop = func() { - slog.Debug("Stopping profiling.") - pprof.StopCPUProfile() - _ = f.Close() - } - return -} diff --git a/internal/cmd/profile.go b/internal/cmd/profile.go new file mode 100644 index 00000000..ed84f201 --- /dev/null +++ b/internal/cmd/profile.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "log/slog" + "os" + + "github.com/pkg/profile" +) + +func startProfiling() stopper { + pprof := os.Getenv("LDAP2PG_PROFILE") + if pprof == "" { + return nil + } + options := []func(*profile.Profile){profile.ProfilePath(".")} + switch pprof { + case "block": + options = append(options, profile.BlockProfile) + case "clock": + options = append(options, profile.ClockProfile) + case "goroutine": + options = append(options, profile.GoroutineProfile) + case "mem": + options = append(options, profile.MemProfile, profile.MemProfileRate(1024)) + case "mutex": + options = append(options, profile.MutexProfile) + case "trace": + options = append(options, profile.TraceProfile) + case "cpu": + options = append(options, profile.CPUProfile) + default: + slog.Warn("Unknown profile type.", "type", pprof) + } + return profile.Start(options...) +} + +type stopper interface { + Stop() +} From d07ba428a4d66724f9ecc916140c7ca0b8e9a507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20BERSAC?= Date: Wed, 22 Oct 2025 16:10:06 +0200 Subject: [PATCH 2/5] Fallback description to rules item number --- internal/wanted/{step.go => item.go} | 83 ++++++++++--------- .../wanted/{step_test.go => item_test.go} | 0 internal/wanted/map.go | 11 +-- 3 files changed, 52 insertions(+), 42 deletions(-) rename internal/wanted/{step.go => item.go} (70%) rename internal/wanted/{step_test.go => item_test.go} (100%) diff --git a/internal/wanted/step.go b/internal/wanted/item.go similarity index 70% rename from internal/wanted/step.go rename to internal/wanted/item.go index ea7d573d..92f6c733 100644 --- a/internal/wanted/step.go +++ b/internal/wanted/item.go @@ -1,6 +1,7 @@ package wanted import ( + "fmt" "log/slog" "slices" "strings" @@ -13,26 +14,34 @@ import ( ldap3 "github.com/go-ldap/ldap/v3" ) -type Step struct { +type RulesItem struct { + pos int Description string LdapSearch ldap.Search RoleRules []RoleRule `mapstructure:"roles"` GrantRules []privileges.GrantRule `mapstructure:"grants"` } -func (s Step) HasLDAPSearch() bool { - return len(s.LdapSearch.Attributes) > 0 +func (item RulesItem) String() string { + if item.Description == "" { + return fmt.Sprintf("%d", item.pos) + } + return item.Description +} + +func (item RulesItem) HasLDAPSearch() bool { + return len(item.LdapSearch.Attributes) > 0 } -func (s Step) HasSubsearch() bool { - return len(s.LdapSearch.Subsearches) > 0 +func (item RulesItem) HasSubsearch() bool { + return len(item.LdapSearch.Subsearches) > 0 } -func (s *Step) InferAttributes() { +func (item *RulesItem) InferAttributes() { attributes := mapset.NewSet[string]() subsearchAttributes := make(map[string]mapset.Set[string]) - for field := range s.IterFields() { + for field := range item.IterFields() { attribute, field, found := strings.Cut(field.FieldName, ".") // dn is the primary key of the entry, not a real attribute. if attribute == "dn" { @@ -58,19 +67,19 @@ func (s *Step) InferAttributes() { return } - s.LdapSearch.Attributes = attributes.ToSlice() + item.LdapSearch.Attributes = attributes.ToSlice() slog.Debug("Collected LDAP search attributes.", - "item", s.Description, "base", s.LdapSearch.Base, "attributes", s.LdapSearch.Attributes) + "item", item, "base", item.LdapSearch.Base, "attributes", item.LdapSearch.Attributes) if len(subsearchAttributes) == 0 { return } - if s.LdapSearch.Subsearches == nil { - s.LdapSearch.Subsearches = make(map[string]ldap.Subsearch) + if item.LdapSearch.Subsearches == nil { + item.LdapSearch.Subsearches = make(map[string]ldap.Subsearch) } for attribute, subAttributes := range subsearchAttributes { - subsearch, ok := s.LdapSearch.Subsearches[attribute] + subsearch, ok := item.LdapSearch.Subsearches[attribute] if !ok { subsearch = ldap.Subsearch{ Filter: "(objectClass=*)", @@ -79,15 +88,15 @@ func (s *Step) InferAttributes() { } subsearch.Attributes = subAttributes.ToSlice() slog.Debug("Collected LDAP sub-search attributes.", - "item", s.Description, "base", s.LdapSearch.Base, + "item", item, "base", item.LdapSearch.Base, "fkey", attribute, "attributes", subsearch.Attributes) - s.LdapSearch.Subsearches[attribute] = subsearch + item.LdapSearch.Subsearches[attribute] = subsearch } } -func (s *Step) ReplaceAttributeAsSubentryField() { - subsearchAttr := s.LdapSearch.SubsearchAttribute() - for field := range s.IterFields() { +func (item *RulesItem) ReplaceAttributeAsSubentryField() { + subsearchAttr := item.LdapSearch.SubsearchAttribute() + for field := range item.IterFields() { attribute, _, found := strings.Cut(field.FieldName, ".") if attribute != subsearchAttr { continue @@ -104,18 +113,18 @@ func (s *Step) ReplaceAttributeAsSubentryField() { } // Yields all {attr} from all formats in item. -func (s Step) IterFields() <-chan *pyfmt.Field { +func (item RulesItem) IterFields() <-chan *pyfmt.Field { ch := make(chan *pyfmt.Field) go func() { defer close(ch) - for _, rule := range s.RoleRules { + for _, rule := range item.RoleRules { for _, f := range rule.Formats() { for _, field := range f.Fields { ch <- field } } } - for _, rule := range s.GrantRules { + for _, rule := range item.GrantRules { for _, f := range rule.Formats() { for _, field := range f.Fields { ch <- field @@ -126,9 +135,9 @@ func (s Step) IterFields() <-chan *pyfmt.Field { return ch } -func (s Step) SplitStaticItems() (items []Step) { +func (item RulesItem) SplitStaticItems() (items []RulesItem) { var staticRoles, dynamicRoles []RoleRule - for _, rule := range s.RoleRules { + for _, rule := range item.RoleRules { if rule.IsStatic() { staticRoles = append(staticRoles, rule) } else { @@ -136,7 +145,7 @@ func (s Step) SplitStaticItems() (items []Step) { } } var staticGrants, dynamicGrants []privileges.GrantRule - for _, rule := range s.GrantRules { + for _, rule := range item.GrantRules { if rule.IsStatic() { staticGrants = append(staticGrants, rule) } else { @@ -146,18 +155,18 @@ func (s Step) SplitStaticItems() (items []Step) { if (len(staticRoles) == 0 && len(staticGrants) == 0) || (len(dynamicRoles) == 0 && len(dynamicGrants) == 0) { - items = append(items, s) + items = append(items, item) return } - items = append(items, Step{ - Description: s.Description, - LdapSearch: s.LdapSearch, + items = append(items, RulesItem{ + Description: item.Description, + LdapSearch: item.LdapSearch, RoleRules: dynamicRoles, GrantRules: dynamicGrants, }) - items = append(items, Step{ + items = append(items, RulesItem{ // Avoid duplicating log message, use a silent item. Description: "", RoleRules: staticRoles, @@ -174,23 +183,23 @@ type SearchResult struct { // search directory, returning each entry or error. Sub-searches are done // concurrently and returned for each sub-key. -func (s Step) search(ldapc ldap.Client) <-chan SearchResult { +func (item RulesItem) search(ldapc ldap.Client) <-chan SearchResult { ch := make(chan SearchResult) go func() { defer close(ch) - if !s.HasLDAPSearch() { + if !item.HasLDAPSearch() { // Use a dumb empty result. ch <- SearchResult{} return } - search := s.LdapSearch + search := item.LdapSearch res, err := ldapc.Search(search.Base, search.Scope, search.Filter, search.Attributes) if err != nil { ch <- SearchResult{err: err} return } - subsearchAttr := s.LdapSearch.SubsearchAttribute() + subsearchAttr := item.LdapSearch.SubsearchAttribute() for _, entry := range res.Entries { slog.Debug("Got LDAP entry.", "dn", entry.DN) result := ldap.Result{ @@ -203,7 +212,7 @@ func (s Step) search(ldapc ldap.Client) <-chan SearchResult { } bases := entry.GetEqualFoldAttributeValues(subsearchAttr) for _, base := range bases { - s := s.LdapSearch.Subsearches[subsearchAttr] + s := item.LdapSearch.Subsearches[subsearchAttr] res, err = ldapc.Search(base, s.Scope, s.Filter, s.Attributes) if err != nil { ch <- SearchResult{err: err} @@ -220,11 +229,11 @@ func (s Step) search(ldapc ldap.Client) <-chan SearchResult { return ch } -func (s Step) generateRoles(results *ldap.Result) <-chan role.Role { +func (item RulesItem) generateRoles(results *ldap.Result) <-chan role.Role { ch := make(chan role.Role) go func() { defer close(ch) - for _, rule := range s.RoleRules { + for _, rule := range item.RoleRules { for role := range rule.Generate(results) { ch <- role } @@ -233,11 +242,11 @@ func (s Step) generateRoles(results *ldap.Result) <-chan role.Role { return ch } -func (s Step) generateGrants(results *ldap.Result) <-chan privileges.Grant { +func (item RulesItem) generateGrants(results *ldap.Result) <-chan privileges.Grant { ch := make(chan privileges.Grant) go func() { defer close(ch) - for _, rule := range s.GrantRules { + for _, rule := range item.GrantRules { for grant := range rule.Generate(results) { ch <- grant } diff --git a/internal/wanted/step_test.go b/internal/wanted/item_test.go similarity index 100% rename from internal/wanted/step_test.go rename to internal/wanted/item_test.go diff --git a/internal/wanted/map.go b/internal/wanted/map.go index 52e27652..6b6e08d1 100644 --- a/internal/wanted/map.go +++ b/internal/wanted/map.go @@ -12,7 +12,7 @@ import ( ) // Rules holds a set of rules to generate wanted state. -type Rules []Step +type Rules []RulesItem func (m Rules) HasLDAPSearches() bool { for _, item := range m { @@ -23,10 +23,10 @@ func (m Rules) HasLDAPSearches() bool { return false } -func (m Rules) SplitStaticRules() (newMap Rules) { - newMap = make(Rules, 0) +func (m Rules) SplitStaticRules() (newRules Rules) { + newRules = make(Rules, 0) for _, item := range m { - newMap = append(newMap, item.SplitStaticItems()...) + newRules = append(newRules, item.SplitStaticItems()...) } return } @@ -58,10 +58,11 @@ func (m Rules) Run(blacklist lists.Blacklist) (roles role.Map, grants map[string roles = make(map[string]role.Role) grants = make(map[string][]privileges.Grant) for i, item := range m { + item.pos = i if item.Description != "" { slog.Info(item.Description) } else { - slog.Debug("Processing sync map item.", "item", i) + slog.Debug("Processing sync map step.", "item", item) } for res := range item.search(ldapc) { From a9881a7dce02a1d0142bf8eebe80bb00e8bb5e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20BERSAC?= Date: Fri, 24 Oct 2025 11:24:55 +0200 Subject: [PATCH 3/5] sql: Reindent creators --- internal/inspect/sql/creators.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/inspect/sql/creators.sql b/internal/inspect/sql/creators.sql index c93c5139..f212b29f 100644 --- a/internal/inspect/sql/creators.sql +++ b/internal/inspect/sql/creators.sql @@ -1,6 +1,6 @@ SELECT nspname, array_agg(rolname ORDER BY rolname) AS creators -FROM pg_catalog.pg_namespace AS nsp -CROSS JOIN pg_catalog.pg_roles AS creator -WHERE has_schema_privilege(creator.oid, nsp.oid, 'CREATE') - AND rolcanlogin -GROUP BY nspname; + FROM pg_catalog.pg_namespace AS nsp + CROSS JOIN pg_catalog.pg_roles AS creator + WHERE has_schema_privilege(creator.oid, nsp.oid, 'CREATE') + AND rolcanlogin + GROUP BY 1; From ef3277e2fdbc587bfae860194ee0ab5473b3c2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20BERSAC?= Date: Tue, 9 Jun 2026 08:34:16 +0200 Subject: [PATCH 4/5] dev: Increase big case 16K roles. 512 schemas. --- test/fixtures/big.sh | 2 +- test/genbigconfig.sh | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/test/fixtures/big.sh b/test/fixtures/big.sh index 7275e963..8a3a0e6b 100755 --- a/test/fixtures/big.sh +++ b/test/fixtures/big.sh @@ -16,7 +16,7 @@ CREATE DATABASE "big0" WITH OWNER "bigowner"; EOSQL queries=() -for i in {0..255} ; do +for i in {0..512} ; do printf -v i "%03d" "$i" queries+=("CREATE SCHEMA nsp$i AUTHORIZATION bigowner") for j in {0..3} ; do diff --git a/test/genbigconfig.sh b/test/genbigconfig.sh index 4f88f467..a8840a29 100755 --- a/test/genbigconfig.sh +++ b/test/genbigconfig.sh @@ -26,6 +26,7 @@ privileges: - __usage_on_schemas__ - __select_on_tables__ - __select_on_sequences__ + - __default_select_on_tables__ write: - __temporary__ @@ -49,7 +50,7 @@ rules: comment: All roles managed by ldap2pg EOF -for n in {0..255} ; do +for n in {0..512} ; do printf -v n "%03d" "$n" cat <<-EOF @@ -75,18 +76,15 @@ for n in {0..255} ; do - privilege: define role: big${n}_d schemas: nsp$n + + - description: "Define roles from directory for group big${n}." + ldapsearch: + base: cn=users,dc=bridoulou,dc=fr + filter: (cn=big${n}*) + roles: + name: "{member.cn}" + parents: + - ldap_roles + - "{cn}" EOF done - -cat <<-EOF - -- description: "Define roles from directory." - ldapsearch: - base: cn=users,dc=bridoulou,dc=fr - filter: (cn=big*) - roles: - name: "{member.cn}" - parents: - - ldap_roles - - "{cn}" -EOF From f404ea4e33363af67e7eff6bc00150b8edf2edc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20BERSAC?= Date: Tue, 9 Jun 2026 15:11:33 +0200 Subject: [PATCH 5/5] dev: Dont wait on localhost Docker proxy is always up. --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3beb33df..1af5edca 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,11 @@ YUM_LABS?=$(wildcard ../yum-labs) default: @echo ldap2pg $(VERSION) -big: +big-directory: test/fixtures/genbigldif.sh | ldapmodify -xw $$LDAPPASSWORD $(MAKE) reset-big -reset-big: reset-postgres - while ! bash -c "echo -n > /dev/tcp/$${PGHOST}/5432" ; do sleep 1; done +big-postgres: test/fixtures/big.sh reset-%: