HeavyHorst / remco

remco is a lightweight configuration management tool

Home Page:https://heavyhorst.github.io/remco/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Are multiple file backends possible? Can i add an additional file-backend for specific resource?

Argelbargel opened this issue · comments

Hi,

i'd like to have a default (file-)backend for all my resources. For a specific resource i'd like to add another, additional (file-)backend with keys specific for this resource. Is this possible? For now, the backend-definition in the resource seems to overwrite the default-backend(s)?

So in my remco.toml i've got this:

[default_backends]
    [default_backends.file]
        watch = true
        interval = 60
        keys = [ "/" ]
        filepath = "default-config.yml"

And in one of my resource-definitions (separate file) i got this:

[backend]
    [backend.file]
        onetime = true
        keys = [ "/" ]
        prefix = "/my-service/"
        filepath = "specific-resource-config.yml"

In the templates for this resource i'd like to be able to access the keys of the default-config and the keys for this specific resource, ideally without having to re-declare the default-file-backend in the resource.

Kind regards!

Hi there,

is this a really simple (or stupid question)? Or where my many edits just to annoying? How can i make amends? ;-)

Kind regards!

Sorry for the late response! :)

Currently, each backend can only be used once per resource (except the backend plugins).

Technically it would be easy to support multiple backends.
But I am not sure if it is possible to do this without changing the format of the current configuration file.

[backend]
    [backend.file]
        onetime = true
        keys = [ "/" ]
        prefix = "/my-service/"
        filepath = "specific-resource-config.yml"

this would turn to

[backend]
   [[backend.file]]
        onetime = true
        keys = [ "/" ]
        prefix = "/my-service/"
        filepath = "specific-resource-config.yml"

what would break everyones config files.

You could of course rewrite the file backend as a plugin and use this instead.

Hi,

thanks for the replay and, yeah, i get that. Changing the datastructure would break things...

I'm mostly a java developer so my solution would be to allow arbitrary suffixes (e.g. [backend.mycustomuniqueid]) and then have an attribute "class" or "implementation" which has the name of the class implementing the backend as value which i'd instantiate by reflection.

So either the suffix matches a "well known" backend or the "implementation" attribute must be present or, to stay more in line with the current syntax for plugins, when the suffix of backend.suffix does not match a known backend and the "path"-attribute does not start with a dot or a slash then try to match it with a known/built-in backend)?

Could that be a backwards compatible way to implement my feature request? If yes, then i could try to create a pr, but that would take some time as i've got no expirience with go whatsoever...

That could work, but let me think about it a little bit before you put too much work into it.

Here is the file backend wrapped as a plugin in the meantime:

package main

import (
	"context"
	"fmt"
	"log"
	"net/rpc/jsonrpc"

	"github.com/HeavyHorst/easykv"
	"github.com/HeavyHorst/easykv/file"
	"github.com/HeavyHorst/remco/pkg/backends/plugin"
	"github.com/natefinch/pie"
)

func NewFileClient(fpath string) (*file.Client, error) {
	c, err := file.New(fpath)
	if err != nil {
		return nil, err
	}
	return c, nil
}

type FileRPCServer struct {
	client *file.Client
}

func main() {
	p := pie.NewProvider()
	if err := p.RegisterName("Plugin", &FileRPCServer{}); err != nil {
		log.Fatalf("failed to register Plugin: %s", err)
	}
	p.ServeCodec(jsonrpc.NewServerCodec)
}

func (c *FileRPCServer) Init(args map[string]string, resp *bool) error {
	var err error
	if fpath, ok := args["fpath"]; ok {
		c.client, err = NewFileClient(fpath)
		if err != nil {
			return err
		}
		*resp = true
		return nil
	}
	return fmt.Errorf("I need an File Path !")
}

func (c *FileRPCServer) GetValues(args []string, resp *map[string]string) error {
	r, err := c.client.GetValues(args)
	if err != nil {
		return err
	}

	*resp = r
	return nil
}

func (c *FileRPCServer) Close(args interface{}, resp *interface{}) error {
	c.client.Close()
	return nil
}

func (c *FileRPCServer) WatchPrefix(args plugin.WatchConfig, resp *uint64) error {
	r, err := c.client.WatchPrefix(context.Background(), args.Prefix, easykv.WithKeys(args.Opts.Keys), easykv.WithWaitIndex(args.Opts.WaitIndex))
	*resp = r
	return err
}

The config would look like this:

[backend]
    [[backend.plugin]]
      path = "/etc/remco/plugins/fileplug"
      onetime = true
      keys = [ "/" ]
      prefix = "/my-service/"
      [backend.plugin.config]
      	fpath = "specific-resource-config.yml"