env

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2020 License: MIT Imports: 16 Imported by: 0

README

ENV PACKAGE

This is a handy configuration package that populates a struct with a limited set of type values, and provides graceful management controls for controlled startup and shutdown sequences as well as an ability to run a program like a daemon start|stop enabled process.

env package recognizes the following stuct field tags:

  • env:"name" - name to use for the field value configuration; when omitted the exported field name is used.
  • default:"value" - sets a default value for the struct field.
  • help:"description" - helpful description text.

The tag:env processor also recognizes these special tag modifiers, and have the following effects:

  • require - will ensure that a value has been provided to poputate the struct field. Setting a default tag meets this requirement and is duplicative, except where a required string value can not be left empty.
  • order - will read args sequentialy from os.Args to populate the struct field in the native struct order, however order args must always appear before flagged args and follow the struct sequence order. Only a configured default tag value or an os.Args value will be recognized as valid value source.
  • environ - will mirror and render the specific struct field to the os environment. This is unnecessary if env.Env() is toggled on since all fields would be mirrored to the os environment.

Sample use

From example folder.


// Example struct
type Example struct {
  File  string `env:"file,require,order,environ" default:"sample.dat" help:"filename to use"`
  Block bool   `env:"B,require" help:"blocking flag"`
  X     int    `help:"x is int"`
  Y     int    `help:"y is int"`
  z     int
}

// Start is a the Graceful interface initializer
func (ex *Example) Start() env.GracefulFunc {
  // init code here
  return func(ctx context.Context) {
    <-ctx.Done()
    // shutdown code here
  }
}

func main() {

  var example Example
  env.Init(&example)
  go env.Shutdown()

  env.Summary(&example)
  env.Manage(&example)

  // service loop example
  if example.Block {
    env.Ready()
    for {
      log.Println("...")
      time.Sleep(time.Second)
    }
    return
  }

  // once through example
  env.Ready()
  log.Println("...")
  env.Stop()

}

Note: The env package parser only supports string, bool, int, int64, uint, uint64 struct field types. The tag parser understands boolean values stated as true|false, yes|no, on|off, or 1|0. If more complex types are needed the developer will need to parse and/or type cast struct values to get them locally.

Order of operations

The order of the application of the sources follows a lowest-to-highest archtype pattern and will overload previous values when the source key is present in the current processing source, this allows defaults to be applied and overloaded by higher order sources:

  • apply tag default:"value" from the struct; when exists
  • apply key:value settings from /etc/{identity}/{identity}.conf; when exists
  • apply current os.Environ matching KEY= settings; when exists
  • apply os.Args command line switches; when present

When the env.Env() toggle is called and set to true, all env:tag key=value pairs will be rendered and mirrored back to the os environment following the struct field archtype. Using the sample Ab struct from above, the following tag:env KEY=value pairs would be mirrored to the environment as:

A=string B=string(bool) C=string(int)

Regardless of the key-value source for the order of operations, the Parser recognizes key value, key=value, or key:value to be valid key-value formats, as ahown below:


$ A=abc ./prog -a xyz -b:on -c=42

2020/10/08 10:13:11 |----------------------------------------|
2020/10/08 10:13:11 | DEVELOPMENT :::::::::::::::: event log |
2020/10/08 10:13:11 |-----//o--------------------------------|
2020/10/08 10:13:11                     development version
2020/10/08 10:13:11                     development build
2020/10/08 10:13:11                             pid 20628
2020/10/08 10:13:11 |---- configuration ---------o//---------|
2020/10/08 10:13:11   A              | xyz
2020/10/08 10:13:11   B              | true
2020/10/08 10:13:11   C              | 42
2020/10/08 10:13:11 |---- service ---------------o//---------|

os environment [ A=xyz B=string(true) C=string(42) ]

Variables

  • Identity - of the app, os.Args[0] by default
  • Version - information, set by a builder.sh
  • Build - information, set by builder.sh
  • Description - program description, license info, etc
  • EtcPath, SrvPath, VarPath are base paths of Dir type

Types

  • Dir - type provides Join,Create path methods
  • GracefulFunc - type is func(ctx context.Context)
  • Graceful - interface type for Start() GracefulFunc
  • Expire - path based file expiration manager

Funcs

  • Context - returns the background env package context
  • DevOS - specify the development OS runtime environment; darwin by default
  • Developement - toggle developement flag on|off; autodetected based on DevOS value
  • Env - toggle env flag on|off to write finalized env:tag struct fields to os.Environ table
  • Init - process, parse, and populate structs with values
  • Fork - alternate Init that also allows a program to run like a daemon start|stop enabled process
  • Summary - reports all exportable struct field values as well as graceful startup/shutdown information

exposed only for a customized Initialization alernative

  • Info - provides version and help information
  • Args - reads os.Args; use with Parse
  • Conf - read ini style file; use with Parse
  • Parser - populates all structs passed following the order or operations; structs must be pointer

Graceful and GracefulFunc Management

The purpose of writing graceful functions and interfaces is to assure that packages and services are completely initialized before allowing the program to being normal operation, and to cleanly shutdown the same packages and services before exiting to avoid data loss and any other clean up tasks. All gracefully managed processes are go rountine wrapped so they will all start at the same time.

  • Manage - wraps a GracefulFunc or Graceful interface to cleanly control startup/shutdown sequences
  • Ready - blocks until all startup process have completed, the proceeeds
  • Stop - signal all gracefully managed items to shutdown
  • Shutdown - blocks and waits for a termination signal; Stop() or os.Interrupt, os.Kill signal
// GracefulFunc controller type
type GracefulFunc func(ctx context.Context)

// Graceful controller interface type
type Graceful interface { Start() GracefulFunc }

A gracefully managed item that needs params passed just needs to return a env.GracefulFunc, but it will block further execution until the GracefulFunc is returned. When lock-step initialization is desired, blocking is the desired behavior, however With a long or slow start sequence these may be go func(){...}() wrapped internally to prevent blocking of other initializations, see example below:


func pokey(path string, n int) env.GracefulFunc {
 go func() {
  time.Sleep(time.Second * 5)
 }()
 return func(ctx context.Context) {
  <-ctx.Done()
 }
}

Sample graceful sequence and output:


env.Manage(&ab)
env.Manage(test)
env.Wait()

2020/10/09 10:15:17 |---- service ---------------o//---------|
2020/10/09 10:15:17 ab: start
2020/10/09 10:15:17 test: start
2020/10/09 10:15:20 |---- log -------------------o//---------|
2020/10/09 10:15:20 test: blah...
2020/10/09 10:15:24 |---- interrupt -------------o//---------|
2020/10/09 10:15:24 ab: stop
2020/10/09 10:15:24 test: stop
2020/10/09 10:15:24 |---- bye -------------------o//---------|

Fork

Using Fork instead of Init allows a program to operate normally as well as run like a start|stop daemon process. This fires before Init would otherwise run, so the pidPath must be defined explicitly. If pidPath is not defined or is nil, the pid file will we written into the current directory.

 var cfg Ab
 Fork(nil,&cfg)

Usage: ./example is normal foreground operation, while ./example start and ./example stop are the daemonized background forms. Any additional command line paramaters are just appending following the start keyword, and the start keyword will be removed during the forking process.

$ ./example start -B:on
4209
$ ./example stop

Version and Help Information

The package provides version information as well as a help configuration information drawn from the env:help tag when it is configured. Struct fields without the tag:help will not be displayed.


$ ./example help

 development
-----------------------
 version development
 build   development

 -file           | filename to use [sample.dat]
                 |  :: require,sequence,environ
 -B              | blocking flag
                 |  :: require
 -x              | x is int

Expiration manager

The file expiration manager, the Expire type, demonstrates the use of a graceful struct design and provides a configurable timebase automated directory cleanup service or process.


expire := env.NewExpire("path1","path2")
env.Manage(expire)

Considerations
  • All structs passed to Init, Fork, Summary, and Parser must be passed by pointer reference or the package will panic and report an interface misconfiguration.

  • All log output is directed to stdout by default. To redirect output to a log file call env.Init(), configure log.SetOutput(...), then call env.Summary() and the such. See example/fork.go for an example.

  • Use env.Shutdown() and env.Ready() for service based programs, and incorporate env.Stop() with once-and-done based programs. See example/main.go for an example of each use case and configuration.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Identity    = filepath.Base(os.Args[0])          // Identity of app, as configured here
	Version     string                               // Version information, set by a builder.sh
	Build       string                               // Build information, set by a builder.sh
	Description string                               // Brief description, license, copyright
	EtcPath     Dir                         = "/etc" // EtcPath base path
	SrvPath     Dir                         = "/srv" // SrvPath base path
	VarPath     Dir                         = "/var" // VarPath base path

)

default configuration setting

Functions

func Args

func Args(m map[string]string) map[string]string

Args processes os.Args and builds a m map[string]string; support for single reference switches -a aa -b=bb -c:cc formats; pass nil to create new

func Conf

func Conf(path string, m map[string]string) map[string]string

Conf processes a basic ini style file to build m map[string]string from the file; supports single reference k=v, k:v or k v setting; ignores comments and empty values; pass nil to create new

func Context

func Context() context.Context

Context returns the master graceful context.Context

func DevOS added in v1.0.1

func DevOS(os string)

DevOS sets the development runtime environment for autodetection

func Development

func Development() bool

Development flag toggle; apply development setting in Init()

func Env

func Env() bool

Env flag toggle; mirror all struct env:TAG=value to os environment via Parser()

func Fork

func Fork(pidPath *Dir, cfg ...interface{})

Fork is an alternative Init that enables a program to run normally or like a daemon start|stop process; pidPath directory must exist and the user must have r/w file level permissions for proper operation; pass nil for default

func Info

func Info(cfg ...interface{})

Info on version or help request processor

prog version|-version|--version
prog help|-help|--help

func Init

func Init(cfg ...interface{})

Init processe populates cfg structs by applying cfg struct default tag values, then any conf file (/etc/{identity}/{identity}.conf) values, then environment settings, followed by command line os.Args values to fill supported struct type fields; pass nil to load args or conf automatically; Env() will toggle env=true to write struct field final KEY=value to os.Environ table

func Manage

func Manage(g interface{}, name ...string)

Manage start/stop gracefully requires Graceful interface signature or a graceful function with with a graceful signature following func(ctx context.Context) format; the optional name is automatically extracted from Graceful interface types or is randomly generated when a name is not supplied with a graceful function

func Parser

func Parser(args, conf map[string]string, env bool, cfg ...interface{})

Parser will apply cfg struct default tag values, then any conf file (/etc/{identity}/{identity}.conf) values, then environment settings, followed by command line args values to fill supported struct type fields; pass nil to load args or conf automatically; set env=true to write KEY=value to os.Environ table

tag: env - name to use for configuration setting
tag: default - set default value
tag: help - help description

supports string, bool, int, int64, uint, uint64 struct types

func Ready

func Ready()

Ready blocks until all Initializations have completed.

func Shutdown

func Shutdown()

Shutdown waits for a termination signal, initiates graceful cleanup, then calls os.Exit; Stop() or an os.Interrupt or os.Kill signal is trigger event; configure only once

func Stop

func Stop()

Stop immediately signals all graceful controllers to begin the shutdown sequence, blocking until all have existed.

Stop() should only be called as the last item in a main() process.

func Summary

func Summary(cfg ...interface{})

Summary of cfg settings; log

Types

type Dir

type Dir string

Dir type

func (Dir) Create

func (d Dir) Create(a ...string) string

Create appends a... and return an updated string; create the directory tree when it does not exist and return a string representation of the full composite path. A file is presumend when the last element contains any of the following ._- characters.

conf.VarPath.Create() -> /var/log
conf.VarPath.Create("insite") -> /var/log/insite
conf.VarPath.Create("insite.log") ->  /var/log/insite.log
conf.VarPath.Create("insite","insite.log") -> /var/log/insite/iniste.log

func (Dir) Join

func (d Dir) Join(a ...string) string

Join appends a... and returns an updated string; no directory tree creation

type Expire added in v1.1.0

type Expire struct {
	CheckOn time.Duration // default hourly
	// contains filtered or unexported fields
}

Expire is an file expiration manager

func NewExpire added in v1.1.0

func NewExpire(path ...string) *Expire

NewExpire configurator will apply default settings and configure path items when provided

func (*Expire) Add added in v1.1.0

func (ex *Expire) Add(path string, age time.Duration) *Expire

Add will register a directory path and file age timeframe

func (*Expire) Expire added in v1.1.0

func (ex *Expire) Expire() *Expire

Expire will run the registered expiration processes

func (*Expire) Report added in v1.1.0

func (ex *Expire) Report() bool

Report toggles expiratin reporting on/off and returns new current setting; default off

func (*Expire) Start added in v1.1.0

func (ex *Expire) Start(ctx context.Context)

Start expire service checks for expired files periodically at the beginning of each CheckOn period; graceful

type Graceful

type Graceful interface {
	Start() GracefulFunc
}

Graceful controller interface type

type GracefulFunc

type GracefulFunc func(ctx context.Context)

GracefulFunc controller type

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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