Skip to content

Commit

Permalink
Refactor ini loading to return change events
Browse files Browse the repository at this point in the history
  • Loading branch information
thrawn01 committed Aug 22, 2017
1 parent 8b03944 commit c382b60
Show file tree
Hide file tree
Showing 16 changed files with 872 additions and 404 deletions.
28 changes: 17 additions & 11 deletions backends.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package args

import (
"context"
"fmt"
"sync"
"time"

"github.com/pkg/errors"
"golang.org/x/net/context"
)

const MAX_BACKOFF_WAIT = 2 * time.Second
Expand Down Expand Up @@ -58,7 +58,7 @@ type Backend interface {
Set(ctx context.Context, key Key, value string) error

// Watch monitors store for changes to key.
Watch(ctx context.Context, root string) <-chan *ChangeEvent
Watch(ctx context.Context, root string) (<-chan ChangeEvent, error)

// Return the root key used to store all other keys in the backend
GetRootKey() string
Expand Down Expand Up @@ -99,38 +99,40 @@ func (self *Parser) ParseBackend(backend Backend) (*Options, error) {
}
pair, err := backend.Get(ctx, key)
if err != nil {
self.info("args.ParseBackend(): Failed to fetch key '%s' - %s", key.Name, err.Error())
// This can be a normal occurrence, and probably shouldn't be logged
//self.info("args.ParseBackend(): Failed to fetch key '%s' - %s", key.Name, err.Error())
continue
}
values.Group(pair.Key.Group).Set(pair.Key.Name, pair.Value)
}
return values, nil
}

func (self *Parser) Watch(backend Backend, callBack func(*ChangeEvent, error)) WatchCancelFunc {
func (self *Parser) Watch(backend Backend, callBack func(ChangeEvent, error)) WatchCancelFunc {
var isRunning sync.WaitGroup
var once sync.Once
done := make(chan struct{})

isRunning.Add(1)
go func() {
var event *ChangeEvent
var ok bool
for {
// Always attempt to watch, until the user tells us to stop
ctx, cancel := context.WithCancel(context.Background())

watchChan := backend.Watch(ctx, backend.GetRootKey())
watchChan, err := backend.Watch(ctx, backend.GetRootKey())
once.Do(func() { isRunning.Done() }) // Notify we are watching
if err != nil {
callBack(ChangeEvent{}, err)
goto Retry
}
for {
select {
case event, ok = <-watchChan:
case event, ok := <-watchChan:
if !ok {
goto Retry
}

if event.Err != nil {
callBack(nil, errors.Wrap(event.Err, "backends.Watch()"))
callBack(ChangeEvent{}, errors.Wrap(event.Err, "backends.Watch()"))
goto Retry
}

Expand All @@ -156,7 +158,11 @@ func (self *Parser) Watch(backend Backend, callBack func(*ChangeEvent, error)) W
// Wait until the go-routine is running before we return, this ensures any updates
// our application might need from the backend picked up by Watch()
isRunning.Wait()
return func() { close(done) }
return func() {
if done != nil {
close(done)
}
}
}

func (self *Parser) findRule(key Key) *Rule {
Expand Down
24 changes: 12 additions & 12 deletions backends_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package args_test

import (
"context"
"fmt"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
"github.com/thrawn01/args"
"golang.org/x/net/context"
)

var watchChan chan *args.ChangeEvent
var watchChan chan args.ChangeEvent

type TestBackend struct {
keys map[string]args.Pair
Expand Down Expand Up @@ -69,11 +69,11 @@ func (self *TestBackend) Set(ctx context.Context, key args.Key, value string) er
}

// Watch monitors store for changes to key.
func (self *TestBackend) Watch(ctx context.Context, key string) <-chan *args.ChangeEvent {
changeChan := make(chan *args.ChangeEvent, 2)
func (self *TestBackend) Watch(ctx context.Context, key string) (<-chan args.ChangeEvent, error) {
changeChan := make(chan args.ChangeEvent, 2)

go func() {
var event *args.ChangeEvent
var event args.ChangeEvent
select {
case event = <-watchChan:
changeChan <- event
Expand All @@ -82,7 +82,7 @@ func (self *TestBackend) Watch(ctx context.Context, key string) <-chan *args.Cha
return
}
}()
return changeChan
return changeChan, nil
}

func (self *TestBackend) Close() {
Expand All @@ -95,8 +95,8 @@ func (self *TestBackend) GetRootKey() string {
return "/root"
}

func NewChangeEvent(key args.Key, value string) *args.ChangeEvent {
return &args.ChangeEvent{
func NewChangeEvent(key args.Key, value string) args.ChangeEvent {
return args.ChangeEvent{
Key: key,
Value: value,
Deleted: false,
Expand All @@ -111,7 +111,7 @@ var _ = Describe("backend", func() {
BeforeEach(func() {
backend = NewTestBackend()
log = NewTestLogger()
watchChan = make(chan *args.ChangeEvent, 1)
watchChan = make(chan args.ChangeEvent, 1)
})

AfterEach(func() {
Expand Down Expand Up @@ -144,7 +144,7 @@ var _ = Describe("backend", func() {
"endpoint2": `{ "host": "endpoint2", "port": "3366" }`,
}))
})
It("Should return an error if config option not found in the backend", func() {
/*It("Should return an error if config option not found in the backend", func() {
parser := args.NewParser()
parser.Log(log)
parser.AddConfig("--missing")
Expand All @@ -153,7 +153,7 @@ var _ = Describe("backend", func() {
Expect(err).To(BeNil())
Expect(log.GetEntry()).To(ContainSubstring("not found"))
Expect(opts.String("missing")).To(Equal(""))
})
})*/
It("Should call Watch() to watch for new values", func() {
parser := args.NewParser()
parser.Log(log)
Expand All @@ -169,7 +169,7 @@ var _ = Describe("backend", func() {

done := make(chan struct{})

cancelWatch := parser.Watch(backend, func(event *args.ChangeEvent, err error) {
cancelWatch := parser.Watch(backend, func(event args.ChangeEvent, err error) {
// Always check for errors
if err != nil {
fmt.Printf("Watch Error - %s\n", err.Error())
Expand Down
6 changes: 5 additions & 1 deletion examples/demo/demo.go → cmd/args-example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/thrawn01/args"
"github.com/thrawn01/args/ini"
)

type Config struct {
Expand Down Expand Up @@ -144,7 +145,10 @@ func main() {
`)

// Make configuration simple by reading arguments from an INI file
opts, err := parser.FromINI(iniFile)
// Parse the ini file
backend, err := ini.NewBackend(iniFile, "")

opts, err := parser.FromBackend(backend)
if err != nil {
fmt.Println(err.Error())
os.Exit(-1)
Expand Down
File renamed without changes.
55 changes: 0 additions & 55 deletions ini.go

This file was deleted.

72 changes: 72 additions & 0 deletions ini/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.PHONY: start-containers stop-containers test examples all glide-deps
.DEFAULT_GOAL := all

# GO
GOPATH := $(shell go env | grep GOPATH | sed 's/GOPATH="\(.*\)"/\1/')
GLIDE := $(GOPATH)/bin/glide
PATH := $(GOPATH)/bin:$(PATH)
export $(PATH)

export ARGS_DOCKER_HOST=localhost
DOCKER_MACHINE_IP=$(shell docker-machine ip default 2> /dev/null)
ifneq ($(DOCKER_MACHINE_IP),)
ARGS_DOCKER_HOST=$(DOCKER_MACHINE_IP)
endif

ETCD_DOCKER_IMAGE=quay.io/coreos/etcd:latest

start-containers:
@echo Checking Docker Containers
@if [ $(shell docker ps -a | grep -ci args-etcd) -eq 0 ]; then \
echo Starting Docker Container args-etcd; \
docker run -d -v /usr/share/ca-certificates/:/etc/ssl/certs -p 4001:4001 -p 2380:2380 -p 2379:2379 \
--name args-etcd $(ETCD_DOCKER_IMAGE) /usr/local/bin/etcd \
--name etcd0 \
--advertise-client-urls http://${ARGS_DOCKER_HOST}:2379,http://${ARGS_DOCKER_HOST}:4001 \
--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 \
--initial-advertise-peer-urls http://${ARGS_DOCKER_HOST}:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd0=http://${ARGS_DOCKER_HOST}:2380 \
--initial-cluster-state new; \
elif [ $(shell docker ps | grep -ci args-etcd) -eq 0 ]; then \
echo restarting args-etcd; \
docker start args-etcd > /dev/null; \
fi

stop-containers:
@if [ $(shell docker ps -a | grep -ci args-etcd) -eq 1 ]; then \
echo Stopping Container args-etcd; \
docker stop args-etcd > /dev/null; \
fi

test: start-containers
@echo Running Tests
export ETCDCTL_ENDPOINTS=${ARGS_DOCKER_HOST}:2379
export ETCDCTL_API=3
go test . -v


all: examples

examples:
go install $(shell go list ./... | grep -v vendor)

travis-ci: glide-deps start-containers
go get -u github.com/mattn/goveralls
go get -u golang.org/x/tools/cmd/cover
go get -u golang.org/x/text/secure/bidirule
goveralls -service=travis-ci

$(GLIDE):
go get -u github.com/Masterminds/glide

go-deps:
go get golang.org/x/net/context
go get github.com/onsi/ginkgo
go get github.com/onsi/gomega
go get github.com/pborman/uuid

glide-deps: $(GLIDE) go-deps
$(GLIDE) install

53 changes: 53 additions & 0 deletions ini/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[![Coverage Status](https://img.shields.io/coveralls/thrawn01/args-etcd.svg)](https://coveralls.io/github/thrawn01/args-etcd)
[![Build Status](https://img.shields.io/travis/thrawn01/args-etcd/master.svg)](https://travis-ci.org/thrawn01/args-etcd)

## Introduction
This repo provides an ini key=value storage backend for use with
[args](http://github.com/thrawn01/args)

## Installation
```
$ go get github.com/thrawn01/argsini
```

## Usage
```go
import (
"github.com/thrawn01/args"
"github.com/thrawn01/argsini"
)

parser := args.NewParser()
parser.AddOption("listen-address").Help("Specify local address to listen on")
parser.AddOption("config").Short("c").Required().Default("/etc/app/config.ini").
Help("Specify configuration file")
parser.AddOption("cache-size").IsInt().Default(150).
Help("Specify the size of the cache in entries")

// Parse the command line args
opt := parser.ParseOrExit(nil)

// Create a new backend object
backend := argsini.NewFromFile(opt.String("config"), "")

// Load the config file specified on the command line
opts, err := parser.FromBackend()
if err != nil {
fmt.Printf("config error - %s\n", err.Error())
}

// Print out the loaded config items
fmt.Printf("listen-address: %s\n", opts.String("listen"))
fmt.Printf("cache-size: %d\n", opts.Int("listen"))

// Watch ini file for any configuration changes
cancelWatch := parser.Watch(backend, func(event *args.ChangeEvent, err error) {
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("Changed Event - %+v\n", event)
// This takes a ChangeEvent and update the opts with the latest changes
parser.Apply(opts.FromChangeEvent(event))
})
```
Loading

0 comments on commit c382b60

Please sign in to comment.