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  }