]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - cmd/safcm/config/groups.go
Use SPDX license identifiers
[safcm/safcm.git] / cmd / safcm / config / groups.go
index 7f7cb3f635c346663808e69e990aac020040b345..bb18b84e3a73a4d33ba521b92d336746ab51016e 100644 (file)
@@ -1,19 +1,7 @@
 // Config: parse groups.yaml
 
-// Copyright (C) 2021  Simon Ruderich
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright (C) 2021-2024  Simon Ruderich
 
 package config
 
@@ -34,7 +22,7 @@ const (
        GroupRemoveSuffix     = GroupSpecialSeparator + "remove"
 )
 
-// Keep in sync with sync_info.go:infoGroupDetectedRegexp
+// Keep in sync with cmd/safcm/sync_info.go:infoGroupDetectedRegexp
 var groupNameRegexp = regexp.MustCompile(`^[a-z0-9_-]+$`)
 
 func LoadGroups(cfg *Config, hosts *Hosts) (map[string][]string, error) {
@@ -60,7 +48,9 @@ func LoadGroups(cfg *Config, hosts *Hosts) (map[string][]string, error) {
                                "%s conflict with pre-defined group %q",
                                errPrefix, name)
                }
-               if hosts.Map[name] != nil {
+               if hosts.Map[name] != nil ||
+                       hosts.Map[strings.TrimSuffix(name,
+                               GroupRemoveSuffix)] != nil {
                        return nil, fmt.Errorf(
                                "%s conflict with existing host",
                                errPrefix)
@@ -84,6 +74,8 @@ func LoadGroups(cfg *Config, hosts *Hosts) (map[string][]string, error) {
                        if x == GroupAll {
                                continue
                        }
+                       // Don't validate against groupNameRegexp because
+                       // hosts have less strict restrictions.
                        if strings.Contains(x, GroupSpecialSeparator) {
                                return nil, fmt.Errorf(
                                        "%s member %q must not contain %q",
@@ -95,14 +87,14 @@ func LoadGroups(cfg *Config, hosts *Hosts) (map[string][]string, error) {
                        if hosts.Map[x] != nil || groups[x] != nil {
                                continue
                        }
-                       return nil, fmt.Errorf("%s group %q not found",
+                       return nil, fmt.Errorf("%s member %q not found",
                                errPrefix, x)
                }
        }
 
        // Sanity check for global configuration
-       for _, x := range cfg.GroupOrder {
-               const errPrefix = "config.yaml: group_order:"
+       for _, x := range cfg.GroupPriority {
+               const errPrefix = "config.yaml: group_priority:"
 
                if x == GroupAll {
                        continue
@@ -124,11 +116,10 @@ func LoadGroups(cfg *Config, hosts *Hosts) (map[string][]string, error) {
        return groups, nil
 }
 
-func ResolveHostGroups(host string,
-       groups map[string][]string,
+func ResolveHostGroups(host string, groups map[string][]string,
        detectedGroups []string) ([]string, error) {
 
-       const maxDepth = 100
+       const maxRecursionDepth = 100
 
        detectedGroupsMap := make(map[string]bool)
        for _, x := range detectedGroups {
@@ -140,7 +131,7 @@ func ResolveHostGroups(host string,
        // groups).
        var lookup func(string, int) bool
        lookup = func(group string, depth int) bool {
-               if depth > maxDepth {
+               if depth > maxRecursionDepth {
                        cycle = &group
                        return false
                }
@@ -186,3 +177,36 @@ func ResolveHostGroups(host string,
        sort.Strings(res)
        return res, nil
 }
+
+// TransitivelyDetectedGroups returns all groups which depend on "detected"
+// groups, either directly or by depending on groups which transitively depend
+// on "detected" groups.
+func TransitivelyDetectedGroups(groups map[string][]string) map[string]bool {
+       work := make(map[string][]string)
+       for k, v := range groups {
+               work[k] = v
+       }
+
+       // Mark all groups which contain "detected" groups as long as new
+       // (transitive) "detected" groups are found.
+       detected := make(map[string]bool)
+       for {
+               change := false
+               for group, members := range work {
+                       for _, x := range members {
+                               if !detected[x] && !strings.HasPrefix(x,
+                                       GroupDetectedPrefix) {
+                                       continue
+                               }
+                               detected[strings.TrimSuffix(group,
+                                       GroupRemoveSuffix)] = true
+                               delete(work, group)
+                               change = true
+                       }
+               }
+               if !change {
+                       break
+               }
+       }
+       return detected
+}