github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/ruler/rulestore/local/local.go (about) 1 package local 2 3 import ( 4 "context" 5 "flag" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "github.com/pkg/errors" 11 promRules "github.com/prometheus/prometheus/rules" 12 13 "github.com/cortexproject/cortex/pkg/ruler/rulespb" 14 ) 15 16 const ( 17 Name = "local" 18 ) 19 20 type Config struct { 21 Directory string `yaml:"directory"` 22 } 23 24 // RegisterFlags registers flags. 25 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 26 f.StringVar(&cfg.Directory, prefix+"local.directory", "", "Directory to scan for rules") 27 } 28 29 // Client expects to load already existing rules located at: 30 // cfg.Directory / userID / namespace 31 type Client struct { 32 cfg Config 33 loader promRules.GroupLoader 34 } 35 36 func NewLocalRulesClient(cfg Config, loader promRules.GroupLoader) (*Client, error) { 37 if cfg.Directory == "" { 38 return nil, errors.New("directory required for local rules config") 39 } 40 41 return &Client{ 42 cfg: cfg, 43 loader: loader, 44 }, nil 45 } 46 47 func (l *Client) ListAllUsers(ctx context.Context) ([]string, error) { 48 root := l.cfg.Directory 49 infos, err := ioutil.ReadDir(root) 50 if err != nil { 51 return nil, errors.Wrapf(err, "unable to read dir %s", root) 52 } 53 54 var result []string 55 for _, info := range infos { 56 // After resolving link, info.Name() may be different than user, so keep original name. 57 user := info.Name() 58 59 if info.Mode()&os.ModeSymlink != 0 { 60 // ioutil.ReadDir only returns result of LStat. Calling Stat resolves symlink. 61 info, err = os.Stat(filepath.Join(root, info.Name())) 62 if err != nil { 63 return nil, err 64 } 65 } 66 67 if info.IsDir() { 68 result = append(result, user) 69 } 70 } 71 72 return result, nil 73 } 74 75 // ListAllRuleGroups implements rules.RuleStore. This method also loads the rules. 76 func (l *Client) ListAllRuleGroups(ctx context.Context) (map[string]rulespb.RuleGroupList, error) { 77 users, err := l.ListAllUsers(ctx) 78 if err != nil { 79 return nil, err 80 } 81 82 lists := make(map[string]rulespb.RuleGroupList) 83 for _, user := range users { 84 list, err := l.loadAllRulesGroupsForUser(ctx, user) 85 if err != nil { 86 return nil, errors.Wrapf(err, "failed to list rule groups for user %s", user) 87 } 88 89 lists[user] = list 90 } 91 92 return lists, nil 93 } 94 95 // ListRuleGroupsForUserAndNamespace implements rules.RuleStore. This method also loads the rules. 96 func (l *Client) ListRuleGroupsForUserAndNamespace(ctx context.Context, userID string, namespace string) (rulespb.RuleGroupList, error) { 97 if namespace != "" { 98 return l.loadAllRulesGroupsForUserAndNamespace(ctx, userID, namespace) 99 } 100 101 return l.loadAllRulesGroupsForUser(ctx, userID) 102 } 103 104 func (l *Client) LoadRuleGroups(_ context.Context, _ map[string]rulespb.RuleGroupList) error { 105 // This Client already loads the rules in its List methods, there is nothing left to do here. 106 return nil 107 } 108 109 // GetRuleGroup implements RuleStore 110 func (l *Client) GetRuleGroup(ctx context.Context, userID, namespace, group string) (*rulespb.RuleGroupDesc, error) { 111 return nil, errors.New("GetRuleGroup unsupported in rule local store") 112 } 113 114 // SetRuleGroup implements RuleStore 115 func (l *Client) SetRuleGroup(ctx context.Context, userID, namespace string, group *rulespb.RuleGroupDesc) error { 116 return errors.New("SetRuleGroup unsupported in rule local store") 117 } 118 119 // DeleteRuleGroup implements RuleStore 120 func (l *Client) DeleteRuleGroup(ctx context.Context, userID, namespace string, group string) error { 121 return errors.New("DeleteRuleGroup unsupported in rule local store") 122 } 123 124 // DeleteNamespace implements RulerStore 125 func (l *Client) DeleteNamespace(ctx context.Context, userID, namespace string) error { 126 return errors.New("DeleteNamespace unsupported in rule local store") 127 } 128 129 func (l *Client) loadAllRulesGroupsForUser(ctx context.Context, userID string) (rulespb.RuleGroupList, error) { 130 var allLists rulespb.RuleGroupList 131 132 root := filepath.Join(l.cfg.Directory, userID) 133 infos, err := ioutil.ReadDir(root) 134 if err != nil { 135 return nil, errors.Wrapf(err, "unable to read rule dir %s", root) 136 } 137 138 for _, info := range infos { 139 // After resolving link, info.Name() may be different than namespace, so keep original name. 140 namespace := info.Name() 141 142 if info.Mode()&os.ModeSymlink != 0 { 143 // ioutil.ReadDir only returns result of LStat. Calling Stat resolves symlink. 144 path := filepath.Join(root, info.Name()) 145 info, err = os.Stat(path) 146 if err != nil { 147 return nil, errors.Wrapf(err, "unable to stat rule file %s", path) 148 } 149 } 150 151 if info.IsDir() { 152 continue 153 } 154 155 list, err := l.loadAllRulesGroupsForUserAndNamespace(ctx, userID, namespace) 156 if err != nil { 157 return nil, errors.Wrapf(err, "failed to list rule group for user %s and namespace %s", userID, namespace) 158 } 159 160 allLists = append(allLists, list...) 161 } 162 163 return allLists, nil 164 } 165 166 func (l *Client) loadAllRulesGroupsForUserAndNamespace(_ context.Context, userID string, namespace string) (rulespb.RuleGroupList, error) { 167 filename := filepath.Join(l.cfg.Directory, userID, namespace) 168 169 rulegroups, allErrors := l.loader.Load(filename) 170 if len(allErrors) > 0 { 171 return nil, errors.Wrapf(allErrors[0], "error parsing %s", filename) 172 } 173 174 var list rulespb.RuleGroupList 175 176 for _, group := range rulegroups.Groups { 177 desc := rulespb.ToProto(userID, namespace, group) 178 list = append(list, desc) 179 } 180 181 return list, nil 182 }