eve

package module
v0.0.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2018 License: MIT Imports: 9 Imported by: 0

README

E.V.E.

GoDoc Build Status Code Coverage Go Report Card

E.V.E. is a environment variables management tool based on a friendly user interface named Environment Variables Editor.

  • A HTTP web interface to manage server nodes, projects, environments and variables.
  • One or more RPC servers used to store the deployed variables values.
  • A library to retrieve environment variables from various handlers and schedule the get order.

Installation

eve requires Go 1.5 or later. (os.LookupEnv is required) It uses go dep to manage dependencies.

$ go get -u github.com/rvflash/eve
$ cd $GOPATH/src/github.com/rvflash/eve
$ dep ensure
Quick start
Launches the web interface

By default, the editor is available on the net address localhost:8080. The database is a BoltDB file named eve.db created in the launch directory. This interface is based on Bootstrap v4 and jQuery 3.2 to propose a simple interface.

cd $GOPATH/src/github.com/rvflash/eve/server/http/
go build && ./http

You can change its behavior by using the command flags:

./http --help
Usage of ./http:
  -dsn string
    	database's filepath (default "eve.db")
  -host string
    	host addr to listen on
  -port int
    	service port (default 8080)
Launches one instance of RPC cache where ever who want.

By default, the RPC server is available on the port 9090.

E.V.E. exposes all environment variables behind /vars. Thereby, to deploy a new instance of cache with all managed variables loaded on starting, we can use the from option to specify this URL.

cd $GOPATH/src/github.com/rvflash/eve/server/tcp/
go build && ./tcp -from "http://localhost:8080/vars"

You can change it with :

./tcp --help
Usage of ./tcp:
  -from string
    	URL to fetch to get JSON data to use as default values
  -host string
    	host addr to listen on
  -port int
    	service port (default 9090)

Now, you can open your favorite browser and go to http://localhost:8080 to create your first project. Its name: Alpha.

After, you can if necessary add until two environments to vary the variable's values in case of these. By example, you can create one environment named Envwith dev, qa or prod as values. For each variable afterwards, you can vary the value.

To finalize your discovery, you should add the net address of the RPC server as your first node cache. Then, deploy the variables for the environment of your choice in this remote or local cache.

Usage

Now you can use the E.V.E. library to access to your environment variables. You can schedule as you want the client to use.

By default, the handler is defined to lookup in its local cache, then in the OS environment and finally in the cache servers added with the New method.

Uses the data getters
// Import the E.V.E. library.
import "github.com/rvflash/eve"

// ...

// Use the net address of the RPC cache started before. 
caches, err := eve.Servers(":9090")
if err != nil {
    fmt.Println(err)
    return
}

// Launch the E.V.E. handler.
// The names of project, environment or variable are not case sensitive.
// Moreover, dash will be internally replace with an underscore. 
// Start by setting the name of the project: alpha.
vars := eve.New("alpha", caches...)

// Alpha is defined to have one environment.
// Here we set the current environment value.
if err := vars.Envs("qa"); err != nil {
    fmt.Println(err)
    return
}

// Now, we suppose to have created 3 variables named: enabled, keyword and value.
// With this configuration, E.V.E. will try to lookup the following variables:
// ALPHA_QA_ENABLED, ALPHA_QA_KEYWORD and ALPHA_QA_VALUE.
if vars.MustBool("enabled") {
    str, err := vars.String("keyword")
    if err != nil {
    	fmt.Println(err)
        return
    }
    fmt.Print(str)
}
if data, ok := vars.Lookup("value"); ok {
    fmt.Printf(": %d", data.(int))
}
// Output: rv: 42
Processes the struct's fields.

E.V.E. supports the use of struct tags to specify alternate name and required environment variables.

eve can be used to specify an alternate name and required with true as value, marks as mandatory the field.

E.V.E has automatic support for CamelCased structure fields. In the following example, in the continuity of the previous sample, it searches for the variables named ALPHA_QA_OTHER_NAME and ALPHA_QA_REQUIRED_VAR. If the last variable can not be found, as the field is tag as mandatory, the Process will return in error.

// MyCnf is sample struct to feed.
type MyCnf struct {
    AliasVar    string  `eve:"OTHER_NAME"`
    RequiredVar int     `required:"true"`
}

// ...

var conf MyCnf
if err := vars.Process(&conf); err != nil {
    fmt.Println(err)
    return
}
Supported structure field types
  • string
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • bool
  • float32, float64
  • time.Duration

Soon, E.V.E. will manage time.Time, slices and maps of any supported type.

More features

  • You can use your own client to supply the environment variables by implementing the client.Getter interface.
  • More client interfaces can be used: one to check the client's availability to disable the internal cache recycle.
  • Another interface named client.Asserter can be used to realize assertion on data of your client.

Documentation

Overview

Example
package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/rvflash/eve"
)

const (
	boolVal  = true
	intVal   = 42
	floatVal = 3.14
	strVal   = "rv"

	hostVal = "http://sh01.prod"
	portVal = 8080
	toVal   = "300ms"
)

func main() {
	vars := eve.New("test", server)
	if err := vars.Envs("qa", "fr"); err != nil {
		fmt.Println(err)
		return
	}
	if vars.MustBool("bool") {
		str, _ := vars.String("str")
		fmt.Print(str)
	}
	if d, ok := vars.Lookup("int"); ok {
		fmt.Printf(": %d", d.(int))
	}
}

// newClient returns a test client to fake a RPC cache.
func newClient(d time.Duration) *handler {
	c := &handler{}
	c.timer = time.AfterFunc(d, func() {
		c.mu.Lock()
		c.offline = true
		c.mu.Unlock()
	})
	return c
}

type handler struct {
	timer   *time.Timer
	offline bool
	mu      sync.Mutex
}

// Lookup implements the client.Getter interface.
func (c *handler) Lookup(key string) (interface{}, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.offline {
		return nil, false
	}
	switch key {
	case "TEST_BOOL", "TEST_QA_FR_BOOL":
		return boolVal, true
	case "TEST_INT", "TEST_QA_FR_INT":
		return intVal, true
	case "TEST_FLOAT", "TEST_QA_FR_FLOAT":
		return floatVal, true
	case "TEST_STR", "TEST_QA_FR_STR":
		return strVal, true
	case "TEST_QA_FR_HOST":
		return hostVal, true
	case "TEST_QA_FR_PORT":
		return portVal, true
	case "TEST_QA_FR_TO":
		return toVal, true
	}
	return nil, false
}

// Lookup implements the client.Checker interface.
func (c *handler) Available() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return !c.offline
}

var server = newClient(time.Minute)
Output:

rv: 42

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalid is returned is the data is not well formed.
	ErrInvalid = errors.New("invalid data")
	// ErrNotFound is returned if the variable is not found.
	ErrNotFound = errors.New("not found")
	// ErrDataSource is returned if the RPC service is not available.
	ErrDataSource = errors.New("no available rpc service")
	// ErrNoPointer is returned if the element to manage is not pointer.
	ErrNoPointer = errors.New("mandatory struct pointer")
)

Error messages.

View Source
var (
	// Cache represents a local in-memory cache.
	Cache = client.NewCache(client.DefaultCacheDuration)
	// OS gives access to the environment variables.
	OS = &client.OS{}
)

Initializes the data sources.

View Source
var Tick = time.Second * 30

Tick is the time duration to sleep before checking if at least one RPC cache is available.

Functions

func PartialServers added in v0.0.9

func PartialServers(addr ...string) (caches []client.Getter, partial bool, err error)

PartialServers tries to connect to each net address and do not care of connection errors. If a connection error occurred, the error is discarded and the process continues.

func Servers

func Servers(addr ...string) ([]client.Getter, error)

Servers tries to connect to each net address and returns connection to each of them. If an error occurred, the rest of the stack is ignored and the first error is returned.

Types

type Client

type Client struct {
	Handler
	// contains filtered or unexported fields
}

Client represents the EVE client to handle the data sources.

func New

func New(project string, servers ...client.Getter) *Client

New returns an instance of a Client. The first parameter is the project's identifier. The second, optional, represents a list of data getter. By default, eve tries to get the variable's value: > In its own memory cache. > In the list of available environment variables. > in the other date getter like RPC cache. The Eve client only sets variables in its own cache.

func (*Client) Bool

func (c *Client) Bool(key string) (bool, error)

Bool uses the key to get the variable's value behind as a boolean.

func (*Client) Envs

func (c *Client) Envs(envs ...string) error

Envs allows to define until 2 environments. The adding's order is important, the first must be the first environment defined in the EVE's project. It returns an error if the number of environment is unexpected.

func (*Client) Float64

func (c *Client) Float64(key string) (float64, error)

Float64 uses the key to get the variable's value behind as a float64.

func (*Client) Get added in v0.0.1

func (c *Client) Get(key string) interface{}

Get retrieves the value of the environment variable named by the key. If it not exists, a nil value is returned.

func (*Client) Int

func (c *Client) Int(key string) (int, error)

Int uses the key to get the variable's value behind as an int.

func (*Client) Lookup

func (c *Client) Lookup(key string) (interface{}, bool)

Lookup retrieves the value of the environment variable named by the key. If it not exists, the second boolean will be false.

func (*Client) MustBool

func (c *Client) MustBool(key string) bool

MustBool is like Bool but panics if the variable cannot be retrieved.

func (*Client) MustFloat64

func (c *Client) MustFloat64(key string) float64

MustFloat64 is like Float64 but panics if the variable cannot be retrieved.

func (*Client) MustInt

func (c *Client) MustInt(key string) int

MustInt is like Int but panics if the variable cannot be retrieved.

func (*Client) MustProcess added in v0.0.2

func (c *Client) MustProcess(spec interface{})

MustProcess is like Process but panics if it fails to feed the spec.

func (*Client) MustString

func (c *Client) MustString(key string) string

MustString is like String but panics if the variable cannot be retrieved.

func (*Client) Process added in v0.0.2

func (c *Client) Process(spec interface{}) error

Process uses the reflection to assign values on each element. It returns on error if one on its fields can not be retrieved.

Example
package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/rvflash/eve"
)

const (
	boolVal  = true
	intVal   = 42
	floatVal = 3.14
	strVal   = "rv"

	hostVal = "http://sh01.prod"
	portVal = 8080
	toVal   = "300ms"
)

type exFields struct {
	Addr    string `eve:"host" required:"true"`
	Port    int
	Timeout time.Duration `eve:"to"`
	Retry   bool
}

func main() {
	vars := eve.New("test", server)
	if err := vars.Envs("qa", "fr"); err != nil {
		fmt.Println(err)
		return
	}
	var mycnf exFields
	if err := vars.Process(&mycnf); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf(
		"%s:%d, to=%.1fs, retry=%v",
		mycnf.Addr, mycnf.Port, mycnf.Timeout.Seconds(), mycnf.Retry,
	)
}

// newClient returns a test client to fake a RPC cache.
func newClient(d time.Duration) *handler {
	c := &handler{}
	c.timer = time.AfterFunc(d, func() {
		c.mu.Lock()
		c.offline = true
		c.mu.Unlock()
	})
	return c
}

type handler struct {
	timer   *time.Timer
	offline bool
	mu      sync.Mutex
}

// Lookup implements the client.Getter interface.
func (c *handler) Lookup(key string) (interface{}, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.offline {
		return nil, false
	}
	switch key {
	case "TEST_BOOL", "TEST_QA_FR_BOOL":
		return boolVal, true
	case "TEST_INT", "TEST_QA_FR_INT":
		return intVal, true
	case "TEST_FLOAT", "TEST_QA_FR_FLOAT":
		return floatVal, true
	case "TEST_STR", "TEST_QA_FR_STR":
		return strVal, true
	case "TEST_QA_FR_HOST":
		return hostVal, true
	case "TEST_QA_FR_PORT":
		return portVal, true
	case "TEST_QA_FR_TO":
		return toVal, true
	}
	return nil, false
}

// Lookup implements the client.Checker interface.
func (c *handler) Available() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return !c.offline
}

var server = newClient(time.Minute)
Output:

http://sh01.prod:8080, to=0.3s, retry=false

func (*Client) String

func (c *Client) String(key string) (string, error)

String uses the key to get the variable's value behind as a string.

func (*Client) UseHandler

func (c *Client) UseHandler(h Handler) *Client

UseHandler defines a new handler to use. It returns the updated client.

type Handler

type Handler map[int]client.Getter

Handler returns the list of data sources in the order in which they are used.

func (Handler) AddHandler added in v0.0.1

func (h Handler) AddHandler(src client.Getter) Handler

AddHandler adds a client to the scheduler.

Directories

Path Synopsis
server
tcp

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL