<commit_before>package prscrape

import (
	"fmt"
	"github.com/bcampbell/eventsource"
	"github.com/golang/glog"
	//	"github.com/gorilla/mux"
	"errors"
	"flag"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

// helper to fetch and scrape an individual press release
func scrape(scraper Scraper, pr *PressRelease) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("%v", e))
		}
	}()
	resp, err := http.Get(pr.Permalink)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		err = errors.New(fmt.Sprintf("HTTP code %d (%s)", resp.StatusCode, pr.Permalink))
		return
	}
	html, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// TODO: collect redirects

	err = scraper.Scrape(pr, string(html))
	if err != nil {
		return
	}
	return
}

// run a scraper
func doit(scraper Scraper, store *Store, sseSrv *eventsource.Server) {

	pressReleases, err := scraper.FetchList()
	if err != nil {
		glog.Errorf("%s: FetchList failed: %s", scraper.Name(), err)
		return
	}

	// cull out the ones we've already got
	oldCount := len(pressReleases)
	pressReleases = store.WhichAreNew(pressReleases)
	glog.Infof("%s: %d releases (%d new)", scraper.Name(), oldCount, len(pressReleases))
	// for all the new ones:
	for _, pr := range pressReleases {
		if !pr.complete {
			err = scrape(scraper, pr)
			if err != nil {
				glog.Errorf("%s: %s %s\n", scraper.Name(), err, pr.Permalink)
				continue
			}
			pr.complete = true
		}

		// stash the new press release
		ev, err := store.Stash(pr)
		if err != nil {
			glog.Errorf("%s: failed to stash %s (%s)", scraper.Name(), pr.Permalink, err)
		} else {
			glog.Infof("%s: added %s", scraper.Name(), pr.Permalink)
		}

		// broadcast it to any connected clients
		sseSrv.Publish([]string{pr.Source}, ev)
	}
}

// ServerMain is the entry point for running the server.
// handles commandline flags and all that stuff - the idea is that you can
// easily write a new server with a different bunch of scrapers. The real
// main() would just be a small stub which instantiates a bunch of scrapers,
// then passes control over to here. See ukpr/main.go for an example
func ServerMain(scraperList []Scraper) {
	var port = flag.Int("port", 9998, "port to run server on")
	var interval = flag.Int("interval", 60*10, "interval at which to poll source sites for new releases (in seconds)")
	var testScraper = flag.String("t", "", "Test run an individual scraper, dumping to stdout. Doesn't run server or alter the database.")
	var briefFlag = flag.Bool("b", false, "Brief (testing mode output)")
	var listFlag = flag.Bool("l", false, "List scrapers and exit")

	flag.Parse()

	scrapers := make(map[string]Scraper)

	for _, scraper := range scraperList {
		name := scraper.Name()
		scrapers[name] = scraper
	}

	if *listFlag {
		for name, _ := range scrapers {
			fmt.Println(name)
		}
		return
	}

	if *testScraper != "" {
		// run a single scraper, without server or store
		// TODO: merge the test implementation with doit()
		scraper, ok := scrapers[*testScraper]
		if !ok {
			glog.Fatal("Unknown scraper %s", *testScraper)
		}
		pressReleases, err := scraper.FetchList()
		if err != nil {
			glog.Fatal(err)
		}
		for _, pr := range pressReleases {
			if !pr.complete {
				//log.Printf("%s: scrape %s", scraper.Name(), pr.Permalink)
				err = scrape(scraper, pr)
				if err != nil {
					glog.Errorf("%s: '%s' %s\n", scraper.Name(), err, pr.Permalink)
					continue
				}
				pr.complete = true
			}

			if !*briefFlag {
				fmt.Printf("%s\n %s\n %s\n", pr.Title, pr.PubDate, pr.Permalink)
				fmt.Println("")
				fmt.Println(pr.Content)
				fmt.Println("------------------------------")
			} else {
				fmt.Printf("%s %s\n", pr.Title, pr.Permalink)
			}
		}
		return
	}

	// set up as server
	// using a common store for all scrapers
	// but no reason they couldn't all have their own store
	store := NewStore("./prstore.db")
	sseSrv := eventsource.NewServer()
	for name, _ := range scrapers {
		sseSrv.Register(name, store)
		http.Handle("/"+name+"/", sseSrv.Handler(name))
	}

	//
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		glog.Fatal(err)
	}
	defer l.Close()

	// cheesy task to periodically run the scrapers
	go func() {
		for {
			for _, scraper := range scrapers {
				doit(scraper, store, sseSrv)
			}
			time.Sleep(time.Duration(*interval) * time.Second)
		}
	}()

	glog.Infof("running on port %d", *port)
	http.Serve(l, nil)
}
<commit_msg>Fix another old import<commit_after>package prscrape

import (
	"fmt"
	"github.com/donovanhide/eventsource"
	"github.com/golang/glog"
	//	"github.com/gorilla/mux"
	"errors"
	"flag"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

// helper to fetch and scrape an individual press release
func scrape(scraper Scraper, pr *PressRelease) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("%v", e))
		}
	}()
	resp, err := http.Get(pr.Permalink)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		err = errors.New(fmt.Sprintf("HTTP code %d (%s)", resp.StatusCode, pr.Permalink))
		return
	}
	html, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// TODO: collect redirects

	err = scraper.Scrape(pr, string(html))
	if err != nil {
		return
	}
	return
}

// run a scraper
func doit(scraper Scraper, store *Store, sseSrv *eventsource.Server) {

	pressReleases, err := scraper.FetchList()
	if err != nil {
		glog.Errorf("%s: FetchList failed: %s", scraper.Name(), err)
		return
	}

	// cull out the ones we've already got
	oldCount := len(pressReleases)
	pressReleases = store.WhichAreNew(pressReleases)
	glog.Infof("%s: %d releases (%d new)", scraper.Name(), oldCount, len(pressReleases))
	// for all the new ones:
	for _, pr := range pressReleases {
		if !pr.complete {
			err = scrape(scraper, pr)
			if err != nil {
				glog.Errorf("%s: %s %s\n", scraper.Name(), err, pr.Permalink)
				continue
			}
			pr.complete = true
		}

		// stash the new press release
		ev, err := store.Stash(pr)
		if err != nil {
			glog.Errorf("%s: failed to stash %s (%s)", scraper.Name(), pr.Permalink, err)
		} else {
			glog.Infof("%s: added %s", scraper.Name(), pr.Permalink)
		}

		// broadcast it to any connected clients
		sseSrv.Publish([]string{pr.Source}, ev)
	}
}

// ServerMain is the entry point for running the server.
// handles commandline flags and all that stuff - the idea is that you can
// easily write a new server with a different bunch of scrapers. The real
// main() would just be a small stub which instantiates a bunch of scrapers,
// then passes control over to here. See ukpr/main.go for an example
func ServerMain(scraperList []Scraper) {
	var port = flag.Int("port", 9998, "port to run server on")
	var interval = flag.Int("interval", 60*10, "interval at which to poll source sites for new releases (in seconds)")
	var testScraper = flag.String("t", "", "Test run an individual scraper, dumping to stdout. Doesn't run server or alter the database.")
	var briefFlag = flag.Bool("b", false, "Brief (testing mode output)")
	var listFlag = flag.Bool("l", false, "List scrapers and exit")

	flag.Parse()

	scrapers := make(map[string]Scraper)

	for _, scraper := range scraperList {
		name := scraper.Name()
		scrapers[name] = scraper
	}

	if *listFlag {
		for name, _ := range scrapers {
			fmt.Println(name)
		}
		return
	}

	if *testScraper != "" {
		// run a single scraper, without server or store
		// TODO: merge the test implementation with doit()
		scraper, ok := scrapers[*testScraper]
		if !ok {
			glog.Fatal("Unknown scraper %s", *testScraper)
		}
		pressReleases, err := scraper.FetchList()
		if err != nil {
			glog.Fatal(err)
		}
		for _, pr := range pressReleases {
			if !pr.complete {
				//log.Printf("%s: scrape %s", scraper.Name(), pr.Permalink)
				err = scrape(scraper, pr)
				if err != nil {
					glog.Errorf("%s: '%s' %s\n", scraper.Name(), err, pr.Permalink)
					continue
				}
				pr.complete = true
			}

			if !*briefFlag {
				fmt.Printf("%s\n %s\n %s\n", pr.Title, pr.PubDate, pr.Permalink)
				fmt.Println("")
				fmt.Println(pr.Content)
				fmt.Println("------------------------------")
			} else {
				fmt.Printf("%s %s\n", pr.Title, pr.Permalink)
			}
		}
		return
	}

	// set up as server
	// using a common store for all scrapers
	// but no reason they couldn't all have their own store
	store := NewStore("./prstore.db")
	sseSrv := eventsource.NewServer()
	for name, _ := range scrapers {
		sseSrv.Register(name, store)
		http.Handle("/"+name+"/", sseSrv.Handler(name))
	}

	//
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		glog.Fatal(err)
	}
	defer l.Close()

	// cheesy task to periodically run the scrapers
	go func() {
		for {
			for _, scraper := range scrapers {
				doit(scraper, store, sseSrv)
			}
			time.Sleep(time.Duration(*interval) * time.Second)
		}
	}()

	glog.Infof("running on port %d", *port)
	http.Serve(l, nil)
}
<|endoftext|>


<commit_before>package application

import (
	"flag"
	"github.com/photoshelf/photoshelf-storage/infrastructure/container"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/boltdb_storage"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/file_storage"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/leveldb_storage"
	"github.com/photoshelf/photoshelf-storage/presentation/controller"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"testing"
)

func TestLoad(t *testing.T) {
	t.Run("with no args, can load default", func(t *testing.T) {
		resetFlag()

		configuration := load()
		assert.EqualValues(t, 1213, configuration.Server.Port)
		assert.EqualValues(t, "leveldb", configuration.Storage.Type)
		assert.EqualValues(t, "./photos", configuration.Storage.Path)
	})

	t.Run("with specify c flag, can load from file", func(t *testing.T) {
		resetFlag()

		configurationPath := path.Join(os.Getenv("GOPATH"), "src/github.com/photoshelf/photoshelf-storage", "testdata", "test.yml")
		os.Args = append(os.Args, "-c", configurationPath)

		configuration := load()
		assert.EqualValues(t, configuration.Server.Port, 12345)
		assert.EqualValues(t, configuration.Storage.Type, "hoge")
		assert.EqualValues(t, configuration.Storage.Path, "fuga")
	})

	t.Run("with flags, can parse from flags", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-p", "54321", "-t", "foo", "-s", "bar")

		configuration := load()
		assert.EqualValues(t, configuration.Server.Port, 54321)
		assert.EqualValues(t, configuration.Storage.Type, "foo")
		assert.EqualValues(t, configuration.Storage.Path, "bar")
	})
}

func TestConfiguration_Set(t *testing.T) {
	t.Run("with no file, returns error", func(t *testing.T) {
		wrongPath := path.Join(os.TempDir(), "wrong_path")
		if err := os.RemoveAll(wrongPath); err != nil {
			t.Fatal(err)
		}

		conf := &Configuration{}
		err := conf.Set(wrongPath)
		assert.Error(t, err)
	})

	t.Run("with wrong data, returns error", func(t *testing.T) {
		wrongDataPath := path.Join(os.TempDir(), "wrong_data")
		if err := ioutil.WriteFile(wrongDataPath, []byte("This is not yml format"), 0700); err != nil {
			t.Fatal(err)
		}

		conf := &Configuration{}
		err := conf.Set(wrongDataPath)
		assert.Error(t, err)
	})
}

func TestConfiguration_String(t *testing.T) {
	t.Run("when not empty, returns value", func(t *testing.T) {
		resetFlag()

		conf := load()
		assert.NotEmpty(t, conf.String())
	})
}

func TestConfigure(t *testing.T) {
	t.Run("with leveldb type, returns instance specify", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "leveldb")
		os.RemoveAll(dbPath)

		os.Args = append(os.Args, "-t", "leveldb", "-s", dbPath)

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(leveldb_storage.LeveldbStorage), actualRepository())
		}
	})

	t.Run("when fail to load leveldb, returns error", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "readonly")
		if err := os.Remove(dbPath); err != nil {
			t.Fatal(err)
		}
		if err := ioutil.WriteFile(dbPath, nil, 0200); err != nil {
			t.Fatal(err)
		}

		os.Args = append(os.Args, "-t", "leveldb", "-s", path.Join(os.TempDir(), "readonly"))

		_, err := Configure()
		assert.Error(t, err)
	})

	t.Run("with file type, returns instance specify", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-t", "file")

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(file_storage.FileStorage), actualRepository())
		}
	})

	t.Run("with boltdb type, returns instance specify", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "boltdb")
		os.RemoveAll(dbPath)

		os.Args = append(os.Args, "-t", "boltdb", "-s", dbPath)

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(boltdb_storage.BoltdbStorage), actualRepository())
		}
	})

	t.Run("when fail to load boltdb, returns error", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "err_boltdb")
		os.RemoveAll(dbPath)
		os.MkdirAll(dbPath, 0600)

		os.Args = append(os.Args, "-t", "boltdb", "-s", dbPath)

		_, err := Configure()
		assert.Error(t, err)
	})

	t.Run("with unknown type, returns error", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-t", "unknown")

		_, err := Configure()
		assert.Error(t, err)
	})
}

func resetFlag() {
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	os.Args = []string{os.Args[0]}
}

func actualRepository() interface{} {
	var photoController controller.PhotoController
	container.Get(&photoController)

	service := reflect.Indirect(reflect.ValueOf(photoController.Service))
	return service.FieldByName("Repository").Interface()
}
<commit_msg>Fix test error in docker<commit_after>package application

import (
	"flag"
	"github.com/photoshelf/photoshelf-storage/infrastructure/container"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/boltdb_storage"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/file_storage"
	"github.com/photoshelf/photoshelf-storage/infrastructure/datastore/leveldb_storage"
	"github.com/photoshelf/photoshelf-storage/presentation/controller"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"testing"
)

func TestLoad(t *testing.T) {
	t.Run("with no args, can load default", func(t *testing.T) {
		resetFlag()

		configuration := load()
		assert.EqualValues(t, 1213, configuration.Server.Port)
		assert.EqualValues(t, "leveldb", configuration.Storage.Type)
		assert.EqualValues(t, "./photos", configuration.Storage.Path)
	})

	t.Run("with specify c flag, can load from file", func(t *testing.T) {
		resetFlag()

		configurationPath := path.Join(os.Getenv("GOPATH"), "src/github.com/photoshelf/photoshelf-storage", "testdata", "test.yml")
		os.Args = append(os.Args, "-c", configurationPath)

		configuration := load()
		assert.EqualValues(t, configuration.Server.Port, 12345)
		assert.EqualValues(t, configuration.Storage.Type, "hoge")
		assert.EqualValues(t, configuration.Storage.Path, "fuga")
	})

	t.Run("with flags, can parse from flags", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-p", "54321", "-t", "foo", "-s", "bar")

		configuration := load()
		assert.EqualValues(t, configuration.Server.Port, 54321)
		assert.EqualValues(t, configuration.Storage.Type, "foo")
		assert.EqualValues(t, configuration.Storage.Path, "bar")
	})
}

func TestConfiguration_Set(t *testing.T) {
	t.Run("with no file, returns error", func(t *testing.T) {
		wrongPath := path.Join(os.TempDir(), "wrong_path")
		if err := os.RemoveAll(wrongPath); err != nil {
			t.Fatal(err)
		}

		conf := &Configuration{}
		err := conf.Set(wrongPath)
		assert.Error(t, err)
	})

	t.Run("with wrong data, returns error", func(t *testing.T) {
		wrongDataPath := path.Join(os.TempDir(), "wrong_data")
		if err := ioutil.WriteFile(wrongDataPath, []byte("This is not yml format"), 0700); err != nil {
			t.Fatal(err)
		}

		conf := &Configuration{}
		err := conf.Set(wrongDataPath)
		assert.Error(t, err)
	})
}

func TestConfiguration_String(t *testing.T) {
	t.Run("when not empty, returns value", func(t *testing.T) {
		resetFlag()

		conf := load()
		assert.NotEmpty(t, conf.String())
	})
}

func TestConfigure(t *testing.T) {
	t.Run("with leveldb type, returns instance specify", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "leveldb")
		os.RemoveAll(dbPath)

		os.Args = append(os.Args, "-t", "leveldb", "-s", dbPath)

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(leveldb_storage.LeveldbStorage), actualRepository())
		}
	})

	t.Run("when fail to load leveldb, returns error", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "readonly")
		os.RemoveAll(dbPath)
		if err := ioutil.WriteFile(dbPath, nil, 0200); err != nil {
			t.Fatal(err)
		}

		os.Args = append(os.Args, "-t", "leveldb", "-s", path.Join(os.TempDir(), "readonly"))

		_, err := Configure()
		assert.Error(t, err)
	})

	t.Run("with file type, returns instance specify", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-t", "file")

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(file_storage.FileStorage), actualRepository())
		}
	})

	t.Run("with boltdb type, returns instance specify", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "boltdb")
		os.RemoveAll(dbPath)

		os.Args = append(os.Args, "-t", "boltdb", "-s", dbPath)

		_, err := Configure()
		if assert.NoError(t, err) {
			assert.IsType(t, new(boltdb_storage.BoltdbStorage), actualRepository())
		}
	})

	t.Run("when fail to load boltdb, returns error", func(t *testing.T) {
		resetFlag()
		dbPath := path.Join(os.TempDir(), "err_boltdb")
		os.RemoveAll(dbPath)
		os.MkdirAll(dbPath, 0600)

		os.Args = append(os.Args, "-t", "boltdb", "-s", dbPath)

		_, err := Configure()
		assert.Error(t, err)
	})

	t.Run("with unknown type, returns error", func(t *testing.T) {
		resetFlag()

		os.Args = append(os.Args, "-t", "unknown")

		_, err := Configure()
		assert.Error(t, err)
	})
}

func resetFlag() {
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	os.Args = []string{os.Args[0]}
}

func actualRepository() interface{} {
	var photoController controller.PhotoController
	container.Get(&photoController)

	service := reflect.Indirect(reflect.ValueOf(photoController.Service))
	return service.FieldByName("Repository").Interface()
}
<|endoftext|>


<commit_before>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
    return []string{
        "donators",
        "donations",
        "donate",
        "supporters",
        "support",
        "patreon",
        "patreons",
        "credits",
    }
}

func (d *Donators) Response(trigger string, content string) string {
    return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 💖\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<commit_msg>special requests<commit_after>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
    return []string{
        "donators",
        "donations",
        "donate",
        "supporters",
        "support",
        "patreon",
        "patreons",
        "credits",
    }
}

func (d *Donators) Response(trigger string, content string) string {
    return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<|endoftext|>


<commit_before>// Simple RPC client balancer
package balancer2go

import (
	"sync"
)

// The main balancer type
type Balancer struct {
	sync.RWMutex
	clients         map[string]Worker
	balancerChannel chan Worker
}

// Interface for RPC clients
type Worker interface {
	Call(serviceMethod string, args interface{}, reply interface{}) error
	Close() error
}

// Constructor for RateList holding one slice for addreses and one slice for connections.
func NewBalancer() *Balancer {
	r := &Balancer{clients: make(map[string]Worker), balancerChannel: make(chan Worker)} // leaving both slices to nil
	go func() {
		for {
			if len(r.clients) > 0 {
				for _, c := range r.clients {
					r.balancerChannel <- c
				}
			} else {
				r.balancerChannel <- nil
			}
		}
	}()
	return r
}

// Adds a client to the two internal map.
func (bl *Balancer) AddClient(address string, client Worker) {
	bl.Lock()
	defer bl.Unlock()
	bl.clients[address] = client
	return
}

// Removes a client from the map locking the readers and reseting the balancer index.
func (bl *Balancer) RemoveClient(address string) {
	bl.Lock()
	defer bl.Unlock()
	delete(bl.clients, address)
	<-bl.balancerChannel
}

// Returns a client for the specifed address.
func (bl *Balancer) GetClient(address string) (c Worker, exists bool) {
	bl.RLock()
	defer bl.RUnlock()
	c, exists = bl.clients[address]
	return
}

// Returns the next available connection at each call looping at the end of connections.
func (bl *Balancer) Balance() (result Worker) {
	bl.RLock()
	defer bl.RUnlock()
	return <-bl.balancerChannel
}

// Sends a shotdown call to the clients
func (bl *Balancer) Shutdown() {
	bl.Lock()
	defer bl.Unlock()
	var reply string
	for _, client := range bl.clients {
		client.Call("Responder.Shutdown", "", &reply)
	}
}

// Returns a string slice with all client addresses
func (bl *Balancer) GetClientAddresses() []string {
	bl.RLock()
	defer bl.RUnlock()
	var addresses []string
	for a, _ := range bl.clients {
		addresses = append(addresses, a)
	}
	return addresses
}
<commit_msg>shutdown method as parameter<commit_after>// Simple RPC client balancer
package balancer2go

import (
	"sync"
)

// The main balancer type
type Balancer struct {
	sync.RWMutex
	clients         map[string]Worker
	balancerChannel chan Worker
}

// Interface for RPC clients
type Worker interface {
	Call(serviceMethod string, args interface{}, reply interface{}) error
	Close() error
}

// Constructor for RateList holding one slice for addreses and one slice for connections.
func NewBalancer() *Balancer {
	r := &Balancer{clients: make(map[string]Worker), balancerChannel: make(chan Worker)} // leaving both slices to nil
	go func() {
		for {
			if len(r.clients) > 0 {
				for _, c := range r.clients {
					r.balancerChannel <- c
				}
			} else {
				r.balancerChannel <- nil
			}
		}
	}()
	return r
}

// Adds a client to the two internal map.
func (bl *Balancer) AddClient(address string, client Worker) {
	bl.Lock()
	defer bl.Unlock()
	bl.clients[address] = client
	return
}

// Removes a client from the map locking the readers and reseting the balancer index.
func (bl *Balancer) RemoveClient(address string) {
	bl.Lock()
	defer bl.Unlock()
	delete(bl.clients, address)
	<-bl.balancerChannel
}

// Returns a client for the specifed address.
func (bl *Balancer) GetClient(address string) (c Worker, exists bool) {
	bl.RLock()
	defer bl.RUnlock()
	c, exists = bl.clients[address]
	return
}

// Returns the next available connection at each call looping at the end of connections.
func (bl *Balancer) Balance() (result Worker) {
	bl.RLock()
	defer bl.RUnlock()
	return <-bl.balancerChannel
}

// Sends a shotdown call to the clients
func (bl *Balancer) Shutdown(shutdownMethod string) {
	bl.Lock()
	defer bl.Unlock()
	var reply string
	for _, client := range bl.clients {
		client.Call(shutdownMethod, "", &reply)
	}
}

// Returns a string slice with all client addresses
func (bl *Balancer) GetClientAddresses() []string {
	bl.RLock()
	defer bl.RUnlock()
	var addresses []string
	for a, _ := range bl.clients {
		addresses = append(addresses, a)
	}
	return addresses
}
<|endoftext|>


<commit_before>package cotacao

import (
	"fmt"

	"github.com/go-chat-bot/bot"
	"github.com/go-chat-bot/plugins/web"
)

var (
	url = "http://api.fixer.io/latest?base=BRL"
)

type retorno struct {
	Real struct {
		USD float32 `json:"USD"`
		EUR float32 `json:"EUR"`
		CAD float32 `json:"CAD"`
		GBP float32 `json:"GBP"`
	} `json:"rates"`
}

func cotacao(command *bot.Cmd) (msg string, err error) {
	data := &retorno{}
	err = web.GetJSON(url, data)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("Dólar: %.2f, Euro: %.2f, CAD: %.2f, Libra: %.2f",
		1 / data.Real.USD,
		1 / data.Real.EUR,
		1 / data.Real.CAD,
		1 / data.Real.GBP), nil
}

func init() {
	bot.RegisterCommand(
		"cotacao",
		"Informa a cotação do Dólar e Euro.",
		"",
		cotacao)
}
<commit_msg>Update cotacao.go (#13)<commit_after>package cotacao

import (
	"fmt"

	"github.com/go-chat-bot/bot"
	"github.com/go-chat-bot/plugins/web"
)

var (
	url = "https://api.exchangeratesapi.io/latest?base=BRL"
)

type retorno struct {
	Real struct {
		USD float32 `json:"USD"`
		EUR float32 `json:"EUR"`
		CAD float32 `json:"CAD"`
		GBP float32 `json:"GBP"`
	} `json:"rates"`
}

func cotacao(command *bot.Cmd) (msg string, err error) {
	data := &retorno{}
	err = web.GetJSON(url, data)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("Dólar: %.2f, Euro: %.2f, CAD: %.2f, Libra: %.2f",
		1 / data.Real.USD,
		1 / data.Real.EUR,
		1 / data.Real.CAD,
		1 / data.Real.GBP), nil
}

func init() {
	bot.RegisterCommand(
		"cotacao",
		"Informa a cotação do Dólar e Euro.",
		"",
		cotacao)
}
<|endoftext|>


<commit_before>package amalog // import "github.com/amalog/go"
import (
	"fmt"
	"io"
)

type Amalog struct {
	Out io.Writer
	Err io.Writer
}

func (ama *Amalog) Run(args []string) int {
	fmt.Fprintf(ama.Out, "TODO implement Amalog\n")
	return 1
}
<commit_msg>ama: format given source code<commit_after>package amalog // import "github.com/amalog/go"
import (
	"bufio"
	"fmt"
	"io"
	"os"

	"github.com/amalog/go/term"
)

type Amalog struct {
	Out io.Writer
	Err io.Writer
}

func (ama *Amalog) Run(args []string) int {
	if len(args) == 0 {
		fmt.Fprintf(ama.Err, "Usage: ama foo.ama\n")
		return 1
	}

	return ama.CmdFormat(args[0])
}

func (ama *Amalog) CmdFormat(filename string) int {
	// open Amalog source code file
	file, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(ama.Err, "%s\n", err)
		return 1
	}
	buf := bufio.NewReader(file)

	// read and output terms
	style := term.Style{}
	reader := term.NewReader(buf)
	for {
		t, err := reader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Fprintf(ama.Err, "read: %s", err)
			return 1
		}

		t.Format(ama.Out, style)
	}

	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/aws/clientmanager"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation/templates"
	"github.com/cloudfoundry/bosh-bootloader/aws/ec2"
	"github.com/cloudfoundry/bosh-bootloader/aws/iam"
	"github.com/cloudfoundry/bosh-bootloader/azure"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/certs"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/config"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/proxy"
	"github.com/cloudfoundry/bosh-bootloader/stack"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"

	awsapplication "github.com/cloudfoundry/bosh-bootloader/application/aws"
	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	awsterraform "github.com/cloudfoundry/bosh-bootloader/terraform/aws"
	azureterraform "github.com/cloudfoundry/bosh-bootloader/terraform/azure"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
)

var (
	Version     string
	gcpBasePath string
)

func main() {
	newConfig := config.NewConfig(storage.GetState)
	parsedFlags, err := newConfig.Bootstrap(os.Args)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	loadedState := parsedFlags.State

	// Utilities
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	envGetter := helpers.NewEnvGetter()
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)

	// Usage Command
	usage := commands.NewUsage(logger)

	storage.GetStateLogger = stderrLogger

	stateStore := storage.NewStore(parsedFlags.StateDir)
	stateValidator := application.NewStateValidator(parsedFlags.StateDir)

	// Amazon
	awsConfiguration := aws.Config{
		AccessKeyID:     loadedState.AWS.AccessKeyID,
		SecretAccessKey: loadedState.AWS.SecretAccessKey,
		Region:          loadedState.AWS.Region,
	}

	awsClientProvider := &clientmanager.ClientProvider{}
	awsClientProvider.SetConfig(awsConfiguration)

	vpcStatusChecker := ec2.NewVPCStatusChecker(awsClientProvider)
	awsAvailabilityZoneRetriever := ec2.NewAvailabilityZoneRetriever(awsClientProvider)
	templateBuilder := templates.NewTemplateBuilder(logger)
	stackManager := cloudformation.NewStackManager(awsClientProvider, logger)
	infrastructureManager := cloudformation.NewInfrastructureManager(templateBuilder, stackManager)
	certificateDescriber := iam.NewCertificateDescriber(awsClientProvider)
	certificateDeleter := iam.NewCertificateDeleter(awsClientProvider)
	certificateValidator := certs.NewValidator()
	userPolicyDeleter := iam.NewUserPolicyDeleter(awsClientProvider)
	awsKeyPairDeleter := ec2.NewKeyPair(awsClientProvider, logger)

	// GCP
	gcpClientProvider := gcp.NewClientProvider(gcpBasePath)
	if loadedState.IAAS == "gcp" {
		err = gcpClientProvider.SetConfig(loadedState.GCP.ServiceAccountKey, loadedState.GCP.ProjectID, loadedState.GCP.Region, loadedState.GCP.Zone)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
	}
	gcpNetworkInstancesChecker := gcp.NewNetworkInstancesChecker(gcpClientProvider.Client())
	// EnvID

	envIDManager := helpers.NewEnvIDManager(envIDGenerator, gcpClientProvider.Client(), infrastructureManager)

	// Terraform
	terraformOutputBuffer := bytes.NewBuffer([]byte{})

	terraformCmd := terraform.NewCmd(os.Stderr, terraformOutputBuffer)
	terraformExecutor := terraform.NewExecutor(terraformCmd, parsedFlags.Debug)

	gcpTemplateGenerator := gcpterraform.NewTemplateGenerator()
	gcpInputGenerator := gcpterraform.NewInputGenerator()
	gcpOutputGenerator := gcpterraform.NewOutputGenerator(terraformExecutor)

	awsTemplateGenerator := awsterraform.NewTemplateGenerator()
	awsInputGenerator := awsterraform.NewInputGenerator(awsAvailabilityZoneRetriever)
	awsOutputGenerator := awsterraform.NewOutputGenerator(terraformExecutor)

	azureTemplateGenerator := azureterraform.NewTemplateGenerator()
	azureInputGenerator := azureterraform.NewInputGenerator()

	templateGenerator := terraform.NewTemplateGenerator(gcpTemplateGenerator, awsTemplateGenerator, azureTemplateGenerator)
	inputGenerator := terraform.NewInputGenerator(gcpInputGenerator, awsInputGenerator, azureInputGenerator)
	stackMigrator := stack.NewMigrator(terraformExecutor, infrastructureManager, certificateDescriber, userPolicyDeleter, awsAvailabilityZoneRetriever, awsKeyPairDeleter)

	terraformManager := terraform.NewManager(terraform.NewManagerArgs{
		Executor:              terraformExecutor,
		TemplateGenerator:     templateGenerator,
		InputGenerator:        inputGenerator,
		AWSOutputGenerator:    awsOutputGenerator,
		GCPOutputGenerator:    gcpOutputGenerator,
		TerraformOutputBuffer: terraformOutputBuffer,
		Logger:                logger,
		StackMigrator:         stackMigrator,
	})

	// BOSH
	hostKeyGetter := proxy.NewHostKeyGetter()
	socks5Proxy := proxy.NewSocks5Proxy(logger, hostKeyGetter, 0)
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.TempDir, ioutil.ReadFile, json.Unmarshal,
		json.Marshal, ioutil.WriteFile)
	boshManager := bosh.NewManager(boshExecutor, logger, socks5Proxy)
	boshClientProvider := bosh.NewClientProvider()

	// Environment Validators
	awsBrokenEnvironmentValidator := awsapplication.NewBrokenEnvironmentValidator(infrastructureManager)
	awsEnvironmentValidator := awsapplication.NewEnvironmentValidator(infrastructureManager, boshClientProvider)

	// Cloud Config
	sshKeyGetter := bosh.NewSSHKeyGetter()
	awsCloudFormationOpsGenerator := awscloudconfig.NewCloudFormationOpsGenerator(awsAvailabilityZoneRetriever, infrastructureManager)
	awsTerraformOpsGenerator := awscloudconfig.NewTerraformOpsGenerator(terraformManager)
	gcpOpsGenerator := gcpcloudconfig.NewOpsGenerator(terraformManager)
	cloudConfigOpsGenerator := cloudconfig.NewOpsGenerator(awsCloudFormationOpsGenerator, awsTerraformOpsGenerator, gcpOpsGenerator)
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, cloudConfigOpsGenerator, boshClientProvider, socks5Proxy, terraformManager, sshKeyGetter)

	// Subcommands
	awsUp := commands.NewAWSUp(boshManager, cloudConfigManager, stateStore, awsClientProvider, envIDManager, terraformManager, awsBrokenEnvironmentValidator)
	awsCreateLBs := commands.NewAWSCreateLBs(logger, cloudConfigManager, stateStore, terraformManager, awsEnvironmentValidator)
	awsLBs := commands.NewAWSLBs(terraformManager, logger)
	awsUpdateLBs := commands.NewAWSUpdateLBs(awsCreateLBs, awsEnvironmentValidator)
	awsDeleteLBs := commands.NewAWSDeleteLBs(logger, cloudConfigManager, stateStore, awsEnvironmentValidator, terraformManager)

	azureClient := azure.NewClient()
	azureUp := commands.NewAzureUp(azureClient, logger, envIDManager, stateStore, terraformManager)

	gcpDeleteLBs := commands.NewGCPDeleteLBs(stateStore, terraformManager, cloudConfigManager)

	gcpUp := commands.NewGCPUp(commands.NewGCPUpArgs{
		StateStore:                   stateStore,
		TerraformManager:             terraformManager,
		BoshManager:                  boshManager,
		Logger:                       logger,
		EnvIDManager:                 envIDManager,
		CloudConfigManager:           cloudConfigManager,
		GCPAvailabilityZoneRetriever: gcpClientProvider.Client(),
	})

	gcpCreateLBs := commands.NewGCPCreateLBs(terraformManager, cloudConfigManager, stateStore, logger, gcpClientProvider.Client())

	gcpLBs := commands.NewGCPLBs(terraformManager, logger)

	gcpUpdateLBs := commands.NewGCPUpdateLBs(gcpCreateLBs)

	// Commands
	commandSet := application.CommandSet{}
	commandSet["help"] = usage
	commandSet["version"] = commands.NewVersion(Version, logger)
	commandSet["up"] = commands.NewUp(awsUp, gcpUp, azureUp, envGetter, boshManager)
	commandSet["destroy"] = commands.NewDestroy(
		logger, os.Stdin, boshManager, vpcStatusChecker, stackManager,
		infrastructureManager, certificateDeleter,
		stateStore, stateValidator, terraformManager, gcpNetworkInstancesChecker,
	)
	commandSet["down"] = commandSet["destroy"]
	commandSet["create-lbs"] = commands.NewCreateLBs(awsCreateLBs, gcpCreateLBs, stateValidator, certificateValidator, boshManager)
	commandSet["update-lbs"] = commands.NewUpdateLBs(awsUpdateLBs, gcpUpdateLBs, certificateValidator, stateValidator, logger, boshManager)
	commandSet["delete-lbs"] = commands.NewDeleteLBs(gcpDeleteLBs, awsDeleteLBs, logger, stateValidator, boshManager)
	commandSet["lbs"] = commands.NewLBs(gcpLBs, awsLBs, stateValidator, logger)
	commandSet["jumpbox-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.JumpboxAddressPropertyName)
	commandSet["director-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorAddressPropertyName)
	commandSet["director-username"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorUsernamePropertyName)
	commandSet["director-password"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorPasswordPropertyName)
	commandSet["director-ca-cert"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorCACertPropertyName)
	commandSet["ssh-key"] = commands.NewSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["env-id"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.EnvIDPropertyName)
	commandSet["latest-error"] = commands.NewLatestError(logger, stateValidator)
	commandSet["print-env"] = commands.NewPrintEnv(logger, stateValidator, terraformManager)
	commandSet["cloud-config"] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)
	commandSet["bosh-deployment-vars"] = commands.NewBOSHDeploymentVars(logger, boshManager, stateValidator, terraformManager)

	commandConfiguration := &application.Configuration{
		Global: application.GlobalConfiguration{
			StateDir: parsedFlags.StateDir,
			Debug:    parsedFlags.Debug,
		},
		State:           loadedState,
		ShowCommandHelp: parsedFlags.Help,
	}

	if len(parsedFlags.RemainingArgs) > 0 {
		commandConfiguration.Command = parsedFlags.RemainingArgs[0]
		commandConfiguration.SubcommandFlags = parsedFlags.RemainingArgs[1:]
	} else {
		commandConfiguration.ShowCommandHelp = false
		if parsedFlags.Help {
			commandConfiguration.Command = "help"
		}
		if parsedFlags.Version {
			commandConfiguration.Command = "version"
		}
	}

	if len(os.Args) == 1 {
		commandConfiguration.Command = "help"
	}

	app := application.New(commandSet, *commandConfiguration, usage)

	err = app.Run()
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}
}
<commit_msg>Remove timestamp in error output.<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/aws/clientmanager"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation/templates"
	"github.com/cloudfoundry/bosh-bootloader/aws/ec2"
	"github.com/cloudfoundry/bosh-bootloader/aws/iam"
	"github.com/cloudfoundry/bosh-bootloader/azure"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/certs"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/config"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/proxy"
	"github.com/cloudfoundry/bosh-bootloader/stack"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"

	awsapplication "github.com/cloudfoundry/bosh-bootloader/application/aws"
	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	awsterraform "github.com/cloudfoundry/bosh-bootloader/terraform/aws"
	azureterraform "github.com/cloudfoundry/bosh-bootloader/terraform/azure"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
)

var (
	Version     string
	gcpBasePath string
)

func main() {
	newConfig := config.NewConfig(storage.GetState)
	parsedFlags, err := newConfig.Bootstrap(os.Args)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	loadedState := parsedFlags.State

	// Utilities
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	envGetter := helpers.NewEnvGetter()
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)

	// Usage Command
	usage := commands.NewUsage(logger)

	storage.GetStateLogger = stderrLogger

	stateStore := storage.NewStore(parsedFlags.StateDir)
	stateValidator := application.NewStateValidator(parsedFlags.StateDir)

	// Amazon
	awsConfiguration := aws.Config{
		AccessKeyID:     loadedState.AWS.AccessKeyID,
		SecretAccessKey: loadedState.AWS.SecretAccessKey,
		Region:          loadedState.AWS.Region,
	}

	awsClientProvider := &clientmanager.ClientProvider{}
	awsClientProvider.SetConfig(awsConfiguration)

	vpcStatusChecker := ec2.NewVPCStatusChecker(awsClientProvider)
	awsAvailabilityZoneRetriever := ec2.NewAvailabilityZoneRetriever(awsClientProvider)
	templateBuilder := templates.NewTemplateBuilder(logger)
	stackManager := cloudformation.NewStackManager(awsClientProvider, logger)
	infrastructureManager := cloudformation.NewInfrastructureManager(templateBuilder, stackManager)
	certificateDescriber := iam.NewCertificateDescriber(awsClientProvider)
	certificateDeleter := iam.NewCertificateDeleter(awsClientProvider)
	certificateValidator := certs.NewValidator()
	userPolicyDeleter := iam.NewUserPolicyDeleter(awsClientProvider)
	awsKeyPairDeleter := ec2.NewKeyPair(awsClientProvider, logger)

	// GCP
	gcpClientProvider := gcp.NewClientProvider(gcpBasePath)
	if loadedState.IAAS == "gcp" {
		err = gcpClientProvider.SetConfig(loadedState.GCP.ServiceAccountKey, loadedState.GCP.ProjectID, loadedState.GCP.Region, loadedState.GCP.Zone)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
	}
	gcpNetworkInstancesChecker := gcp.NewNetworkInstancesChecker(gcpClientProvider.Client())
	// EnvID

	envIDManager := helpers.NewEnvIDManager(envIDGenerator, gcpClientProvider.Client(), infrastructureManager)

	// Terraform
	terraformOutputBuffer := bytes.NewBuffer([]byte{})

	terraformCmd := terraform.NewCmd(os.Stderr, terraformOutputBuffer)
	terraformExecutor := terraform.NewExecutor(terraformCmd, parsedFlags.Debug)

	gcpTemplateGenerator := gcpterraform.NewTemplateGenerator()
	gcpInputGenerator := gcpterraform.NewInputGenerator()
	gcpOutputGenerator := gcpterraform.NewOutputGenerator(terraformExecutor)

	awsTemplateGenerator := awsterraform.NewTemplateGenerator()
	awsInputGenerator := awsterraform.NewInputGenerator(awsAvailabilityZoneRetriever)
	awsOutputGenerator := awsterraform.NewOutputGenerator(terraformExecutor)

	azureTemplateGenerator := azureterraform.NewTemplateGenerator()
	azureInputGenerator := azureterraform.NewInputGenerator()

	templateGenerator := terraform.NewTemplateGenerator(gcpTemplateGenerator, awsTemplateGenerator, azureTemplateGenerator)
	inputGenerator := terraform.NewInputGenerator(gcpInputGenerator, awsInputGenerator, azureInputGenerator)
	stackMigrator := stack.NewMigrator(terraformExecutor, infrastructureManager, certificateDescriber, userPolicyDeleter, awsAvailabilityZoneRetriever, awsKeyPairDeleter)

	terraformManager := terraform.NewManager(terraform.NewManagerArgs{
		Executor:              terraformExecutor,
		TemplateGenerator:     templateGenerator,
		InputGenerator:        inputGenerator,
		AWSOutputGenerator:    awsOutputGenerator,
		GCPOutputGenerator:    gcpOutputGenerator,
		TerraformOutputBuffer: terraformOutputBuffer,
		Logger:                logger,
		StackMigrator:         stackMigrator,
	})

	// BOSH
	hostKeyGetter := proxy.NewHostKeyGetter()
	socks5Proxy := proxy.NewSocks5Proxy(logger, hostKeyGetter, 0)
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.TempDir, ioutil.ReadFile, json.Unmarshal,
		json.Marshal, ioutil.WriteFile)
	boshManager := bosh.NewManager(boshExecutor, logger, socks5Proxy)
	boshClientProvider := bosh.NewClientProvider()

	// Environment Validators
	awsBrokenEnvironmentValidator := awsapplication.NewBrokenEnvironmentValidator(infrastructureManager)
	awsEnvironmentValidator := awsapplication.NewEnvironmentValidator(infrastructureManager, boshClientProvider)

	// Cloud Config
	sshKeyGetter := bosh.NewSSHKeyGetter()
	awsCloudFormationOpsGenerator := awscloudconfig.NewCloudFormationOpsGenerator(awsAvailabilityZoneRetriever, infrastructureManager)
	awsTerraformOpsGenerator := awscloudconfig.NewTerraformOpsGenerator(terraformManager)
	gcpOpsGenerator := gcpcloudconfig.NewOpsGenerator(terraformManager)
	cloudConfigOpsGenerator := cloudconfig.NewOpsGenerator(awsCloudFormationOpsGenerator, awsTerraformOpsGenerator, gcpOpsGenerator)
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, cloudConfigOpsGenerator, boshClientProvider, socks5Proxy, terraformManager, sshKeyGetter)

	// Subcommands
	awsUp := commands.NewAWSUp(boshManager, cloudConfigManager, stateStore, awsClientProvider, envIDManager, terraformManager, awsBrokenEnvironmentValidator)
	awsCreateLBs := commands.NewAWSCreateLBs(logger, cloudConfigManager, stateStore, terraformManager, awsEnvironmentValidator)
	awsLBs := commands.NewAWSLBs(terraformManager, logger)
	awsUpdateLBs := commands.NewAWSUpdateLBs(awsCreateLBs, awsEnvironmentValidator)
	awsDeleteLBs := commands.NewAWSDeleteLBs(logger, cloudConfigManager, stateStore, awsEnvironmentValidator, terraformManager)

	azureClient := azure.NewClient()
	azureUp := commands.NewAzureUp(azureClient, logger, envIDManager, stateStore, terraformManager)

	gcpDeleteLBs := commands.NewGCPDeleteLBs(stateStore, terraformManager, cloudConfigManager)

	gcpUp := commands.NewGCPUp(commands.NewGCPUpArgs{
		StateStore:                   stateStore,
		TerraformManager:             terraformManager,
		BoshManager:                  boshManager,
		Logger:                       logger,
		EnvIDManager:                 envIDManager,
		CloudConfigManager:           cloudConfigManager,
		GCPAvailabilityZoneRetriever: gcpClientProvider.Client(),
	})

	gcpCreateLBs := commands.NewGCPCreateLBs(terraformManager, cloudConfigManager, stateStore, logger, gcpClientProvider.Client())

	gcpLBs := commands.NewGCPLBs(terraformManager, logger)

	gcpUpdateLBs := commands.NewGCPUpdateLBs(gcpCreateLBs)

	// Commands
	commandSet := application.CommandSet{}
	commandSet["help"] = usage
	commandSet["version"] = commands.NewVersion(Version, logger)
	commandSet["up"] = commands.NewUp(awsUp, gcpUp, azureUp, envGetter, boshManager)
	commandSet["destroy"] = commands.NewDestroy(
		logger, os.Stdin, boshManager, vpcStatusChecker, stackManager,
		infrastructureManager, certificateDeleter,
		stateStore, stateValidator, terraformManager, gcpNetworkInstancesChecker,
	)
	commandSet["down"] = commandSet["destroy"]
	commandSet["create-lbs"] = commands.NewCreateLBs(awsCreateLBs, gcpCreateLBs, stateValidator, certificateValidator, boshManager)
	commandSet["update-lbs"] = commands.NewUpdateLBs(awsUpdateLBs, gcpUpdateLBs, certificateValidator, stateValidator, logger, boshManager)
	commandSet["delete-lbs"] = commands.NewDeleteLBs(gcpDeleteLBs, awsDeleteLBs, logger, stateValidator, boshManager)
	commandSet["lbs"] = commands.NewLBs(gcpLBs, awsLBs, stateValidator, logger)
	commandSet["jumpbox-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.JumpboxAddressPropertyName)
	commandSet["director-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorAddressPropertyName)
	commandSet["director-username"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorUsernamePropertyName)
	commandSet["director-password"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorPasswordPropertyName)
	commandSet["director-ca-cert"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorCACertPropertyName)
	commandSet["ssh-key"] = commands.NewSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["env-id"] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.EnvIDPropertyName)
	commandSet["latest-error"] = commands.NewLatestError(logger, stateValidator)
	commandSet["print-env"] = commands.NewPrintEnv(logger, stateValidator, terraformManager)
	commandSet["cloud-config"] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)
	commandSet["bosh-deployment-vars"] = commands.NewBOSHDeploymentVars(logger, boshManager, stateValidator, terraformManager)

	commandConfiguration := &application.Configuration{
		Global: application.GlobalConfiguration{
			StateDir: parsedFlags.StateDir,
			Debug:    parsedFlags.Debug,
		},
		State:           loadedState,
		ShowCommandHelp: parsedFlags.Help,
	}

	if len(parsedFlags.RemainingArgs) > 0 {
		commandConfiguration.Command = parsedFlags.RemainingArgs[0]
		commandConfiguration.SubcommandFlags = parsedFlags.RemainingArgs[1:]
	} else {
		commandConfiguration.ShowCommandHelp = false
		if parsedFlags.Help {
			commandConfiguration.Command = "help"
		}
		if parsedFlags.Version {
			commandConfiguration.Command = "version"
		}
	}

	if len(os.Args) == 1 {
		commandConfiguration.Command = "help"
	}

	app := application.New(commandSet, *commandConfiguration, usage)

	err = app.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "\n\n%s\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Author(s): Michael Koeppl

package dinero

import (
	"fmt"
)

// Amount represents a money value in a given currency.
type Amount struct {
	Value    float64
	Currency Currency
}

// ConvertToMv converts mv to another target currency. It returns the converted
// money value or an error.
func (a Amount) ConvertToMv(target Currency) (*Amount, error) {
	resval := &Amount{}
	rate, err := rate(currencyCodes[a.Currency], currencyCodes[target])
	if err != nil {
		return nil, err
	}
	resval.Value = a.Value * rate
	resval.Currency = target
	return resval, nil
}

// ConvertTo converts mv to another target currency. It returns the converted
// money value as a float64 or an error.
func (a Amount) ConvertTo(target Currency) (float64, error) {
	res, err := a.ConvertToMv(target)
	if err != nil {
		return -1, err
	}
	return res.Value, nil
}

// String returns the amount with its currency symbol in the
// predefined format "<value> <symbol>".
func (a Amount) String() string {
	return fmt.Sprintf("%f %s", a.Value, currencySymbols[a.Currency])
}
<commit_msg>Fix comments<commit_after>// Author(s): Michael Koeppl

package dinero

import (
	"fmt"
)

// Amount represents a money value in a given currency.
type Amount struct {
	Value    float64
	Currency Currency
}

// ConvertToMv converts Amount a to another target currency. It returns the converted
// Amount or an error.
func (a Amount) ConvertToMv(target Currency) (*Amount, error) {
	resval := &Amount{}
	rate, err := rate(currencyCodes[a.Currency], currencyCodes[target])
	if err != nil {
		return nil, err
	}
	resval.Value = a.Value * rate
	resval.Currency = target
	return resval, nil
}

// ConvertTo converts Amount a to another target currency. It returns the converted
// value as a float64 or an error.
func (a Amount) ConvertTo(target Currency) (float64, error) {
	res, err := a.ConvertToMv(target)
	if err != nil {
		return -1, err
	}
	return res.Value, nil
}

// String returns the amount with its currency symbol in the
// predefined format "<value> <symbol>".
func (a Amount) String() string {
	return fmt.Sprintf("%f %s", a.Value, currencySymbols[a.Currency])
}
<|endoftext|>


<commit_before>package machine

import (
    "novmm/platform"
    "sync"
)

const (
    VirtioConsoleFSize      = 1
    VirtioConsoleFMultiPort = 2
)

const (
    VirtioConsoleDeviceReady = 0
    VirtioConsolePortAdd     = 1
    VirtioConsolePortRemove  = 2
    VirtioConsolePortReady   = 3
    VirtioConsolePortConsole = 4
    VirtioConsolePortResize  = 5
    VirtioConsolePortOpen    = 6
    VirtioConsolePortName    = 7
)

type VirtioConsoleDevice struct {
    *VirtioDevice

    read_buf    *VirtioBuffer
    read_offset int
    read_lock   sync.Mutex

    write_lock sync.Mutex

    Opened bool `json:"opened"`
}

func (device *VirtioConsoleDevice) sendCtrl(
    port int,
    event int,
    value int) error {

    buf := <-device.Channels[2].incoming

    header := buf.Map(0, 8)

    if header.Size() < 8 {
        buf.length = 0
        device.Channels[2].outgoing <- buf
        return nil
    }

    header.Set32(0, uint32(port))
    header.Set16(4, uint16(event))
    header.Set16(6, uint16(value))
    buf.length = 8

    device.Channels[2].outgoing <- buf
    return nil
}

func (device *VirtioConsoleDevice) ctrlConsole(
    vchannel *VirtioChannel) error {

    for buf := range vchannel.incoming {

        header := buf.Map(0, 8)

        // Legit?
        if header.Size() < 8 {
            device.Debug("invalid ctrl packet?")
            vchannel.outgoing <- buf
            continue
        }

        id := header.Get32(0)
        event := header.Get16(4)
        value := header.Get16(6)

        // Return the buffer.
        vchannel.outgoing <- buf

        switch int(event) {
        case VirtioConsoleDeviceReady:
            vchannel.Debug("device-ready")
            device.sendCtrl(0, VirtioConsolePortAdd, 1)
            break

        case VirtioConsolePortAdd:
            vchannel.Debug("port-add?")
            break

        case VirtioConsolePortRemove:
            vchannel.Debug("port-remove?")
            break

        case VirtioConsolePortReady:
            vchannel.Debug("port-ready")

            if id == 0 && value == 1 {
                // No, this is not a console.
                device.sendCtrl(0, VirtioConsolePortConsole, 0)
                device.sendCtrl(0, VirtioConsolePortOpen, 1)
                if !device.Opened {
                    device.Opened = true
                    device.read_lock.Unlock()
                    device.write_lock.Unlock()
                }
            }
            break

        case VirtioConsolePortConsole:
            vchannel.Debug("port-console?")
            break

        case VirtioConsolePortResize:
            vchannel.Debug("port-resize")
            break

        case VirtioConsolePortOpen:
            vchannel.Debug("port-open")
            break

        case VirtioConsolePortName:
            vchannel.Debug("port-name")
            break

        default:
            vchannel.Debug("unknown?")
            break
        }
    }

    return nil
}

func setupConsole(device *VirtioDevice) (Device, error) {

    // Set our features.
    device.SetFeatures(VirtioConsoleFMultiPort)

    // We only support a single port.
    // (The worst multi-port device in history).
    device.Config.GrowTo(8)
    device.Config.Set32(4, 1)

    device.Channels[0] = device.NewVirtioChannel(128)
    device.Channels[1] = device.NewVirtioChannel(128)
    device.Channels[2] = device.NewVirtioChannel(32)
    device.Channels[3] = device.NewVirtioChannel(32)

    return &VirtioConsoleDevice{
        VirtioDevice: device}, nil
}

func NewVirtioMmioConsole(info *DeviceInfo) (Device, error) {
    device, err := NewMmioVirtioDevice(info, VirtioTypeConsole)
    if err != nil {
        return nil, err
    }

    return setupConsole(device)
}

func NewVirtioPciConsole(info *DeviceInfo) (Device, error) {
    device, err := NewPciVirtioDevice(info, PciClassMisc, VirtioTypeConsole, 8)
    if err != nil {
        return nil, err
    }

    return setupConsole(device)
}

func (console *VirtioConsoleDevice) Attach(vm *platform.Vm, model *Model) error {
    err := console.VirtioDevice.Attach(vm, model)
    if err != nil {
        return err
    }

    // Ensure no reads/writes go through.
    console.read_lock.Lock()
    console.write_lock.Lock()

    // Start our console process.
    go console.ctrlConsole(console.Channels[3])

    return nil
}

func (console *VirtioConsoleDevice) Read(p []byte) (int, error) {

    console.read_lock.Lock()
    defer console.read_lock.Unlock()

    // Need a new buffer?
    if console.read_buf == nil {
        console.read_buf = <-console.Channels[1].incoming
    }

    // Copy out as much as possible.
    n := console.read_buf.CopyOut(console.read_offset, p)
    console.read_offset += n
    if console.read_offset == console.read_buf.Length() {
        // Done with this buffer.
        console.Channels[1].outgoing <- console.read_buf
        console.read_buf = nil
        console.read_offset = 0
    }

    return n, nil
}

func (console *VirtioConsoleDevice) Write(p []byte) (int, error) {

    console.write_lock.Lock()
    defer console.write_lock.Unlock()

    // Always grab a new buffer.
    buf := <-console.Channels[0].incoming

    // Map as much as possible.
    var n int
    data := buf.Map(0, len(p))
    if len(data) < len(p) {
        n = len(data)
        copy(data, p[:len(data)])
    } else {
        n = len(p)
        copy(data, p)
    }
    buf.length = n

    // Put the buffer back.
    console.Channels[0].outgoing <- buf

    // We're done.
    return n, nil
}

func (console *VirtioConsoleDevice) Close() error {
    // Ignore.
    return nil
}
<commit_msg>Correctly implement Write() contract.<commit_after>package machine

import (
    "novmm/platform"
    "sync"
)

const (
    VirtioConsoleFSize      = 1
    VirtioConsoleFMultiPort = 2
)

const (
    VirtioConsoleDeviceReady = 0
    VirtioConsolePortAdd     = 1
    VirtioConsolePortRemove  = 2
    VirtioConsolePortReady   = 3
    VirtioConsolePortConsole = 4
    VirtioConsolePortResize  = 5
    VirtioConsolePortOpen    = 6
    VirtioConsolePortName    = 7
)

type VirtioConsoleDevice struct {
    *VirtioDevice

    read_buf    *VirtioBuffer
    read_offset int
    read_lock   sync.Mutex

    write_lock sync.Mutex

    Opened bool `json:"opened"`
}

func (device *VirtioConsoleDevice) sendCtrl(
    port int,
    event int,
    value int) error {

    buf := <-device.Channels[2].incoming

    header := buf.Map(0, 8)

    if header.Size() < 8 {
        buf.length = 0
        device.Channels[2].outgoing <- buf
        return nil
    }

    header.Set32(0, uint32(port))
    header.Set16(4, uint16(event))
    header.Set16(6, uint16(value))
    buf.length = 8

    device.Channels[2].outgoing <- buf
    return nil
}

func (device *VirtioConsoleDevice) ctrlConsole(
    vchannel *VirtioChannel) error {

    for buf := range vchannel.incoming {

        header := buf.Map(0, 8)

        // Legit?
        if header.Size() < 8 {
            device.Debug("invalid ctrl packet?")
            vchannel.outgoing <- buf
            continue
        }

        id := header.Get32(0)
        event := header.Get16(4)
        value := header.Get16(6)

        // Return the buffer.
        vchannel.outgoing <- buf

        switch int(event) {
        case VirtioConsoleDeviceReady:
            vchannel.Debug("device-ready")
            device.sendCtrl(0, VirtioConsolePortAdd, 1)
            break

        case VirtioConsolePortAdd:
            vchannel.Debug("port-add?")
            break

        case VirtioConsolePortRemove:
            vchannel.Debug("port-remove?")
            break

        case VirtioConsolePortReady:
            vchannel.Debug("port-ready")

            if id == 0 && value == 1 {
                // No, this is not a console.
                device.sendCtrl(0, VirtioConsolePortConsole, 0)
                device.sendCtrl(0, VirtioConsolePortOpen, 1)
                if !device.Opened {
                    device.Opened = true
                    device.read_lock.Unlock()
                    device.write_lock.Unlock()
                }
            }
            break

        case VirtioConsolePortConsole:
            vchannel.Debug("port-console?")
            break

        case VirtioConsolePortResize:
            vchannel.Debug("port-resize")
            break

        case VirtioConsolePortOpen:
            vchannel.Debug("port-open")
            break

        case VirtioConsolePortName:
            vchannel.Debug("port-name")
            break

        default:
            vchannel.Debug("unknown?")
            break
        }
    }

    return nil
}

func setupConsole(device *VirtioDevice) (Device, error) {

    // Set our features.
    device.SetFeatures(VirtioConsoleFMultiPort)

    // We only support a single port.
    // (The worst multi-port device in history).
    device.Config.GrowTo(8)
    device.Config.Set32(4, 1)

    device.Channels[0] = device.NewVirtioChannel(128)
    device.Channels[1] = device.NewVirtioChannel(128)
    device.Channels[2] = device.NewVirtioChannel(32)
    device.Channels[3] = device.NewVirtioChannel(32)

    return &VirtioConsoleDevice{
        VirtioDevice: device}, nil
}

func NewVirtioMmioConsole(info *DeviceInfo) (Device, error) {
    device, err := NewMmioVirtioDevice(info, VirtioTypeConsole)
    if err != nil {
        return nil, err
    }

    return setupConsole(device)
}

func NewVirtioPciConsole(info *DeviceInfo) (Device, error) {
    device, err := NewPciVirtioDevice(info, PciClassMisc, VirtioTypeConsole, 8)
    if err != nil {
        return nil, err
    }

    return setupConsole(device)
}

func (console *VirtioConsoleDevice) Attach(vm *platform.Vm, model *Model) error {
    err := console.VirtioDevice.Attach(vm, model)
    if err != nil {
        return err
    }

    // Ensure no reads/writes go through.
    console.read_lock.Lock()
    console.write_lock.Lock()

    // Start our console process.
    go console.ctrlConsole(console.Channels[3])

    return nil
}

func (console *VirtioConsoleDevice) Read(p []byte) (int, error) {

    console.read_lock.Lock()
    defer console.read_lock.Unlock()

    // Need a new buffer?
    if console.read_buf == nil {
        console.read_buf = <-console.Channels[1].incoming
    }

    // Copy out as much as possible.
    n := console.read_buf.CopyOut(console.read_offset, p)
    console.read_offset += n
    if console.read_offset == console.read_buf.Length() {
        // Done with this buffer.
        console.Channels[1].outgoing <- console.read_buf
        console.read_buf = nil
        console.read_offset = 0
    }

    return n, nil
}

func (console *VirtioConsoleDevice) Write(p []byte) (int, error) {

    console.write_lock.Lock()
    defer console.write_lock.Unlock()

    var n int

    for n < len(p) {

        // Always grab a new buffer.
        buf := <-console.Channels[0].incoming

        // Map as much as needed.
        left := len(p) - n
        data := buf.Map(0, left)
        if len(data) < left {
            copy(data, p[n:n+len(data)])
            n += len(data)
            buf.length = len(data)
        } else {
            copy(data, p[n:])
            n += left
            buf.length = left
        }

        // Put the buffer back.
        console.Channels[0].outgoing <- buf
    }

    // We're done.
    return n, nil
}

func (console *VirtioConsoleDevice) Close() error {
    // Ignore.
    return nil
}
<|endoftext|>


<commit_before>package runtime

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/hpcloud/tail"
	"github.com/micro/go-micro/v2/logger"
)

type runtime struct {
	sync.RWMutex
	// options configure runtime
	options Options
	// used to stop the runtime
	closed chan bool
	// used to start new services
	start chan *service
	// indicates if we're running
	running bool
	// the service map
	// TODO: track different versions of the same service
	services map[string]*service
}

// NewRuntime creates new local runtime and returns it
func NewRuntime(opts ...Option) Runtime {
	// get default options
	options := Options{}

	// apply requested options
	for _, o := range opts {
		o(&options)
	}

	return &runtime{
		options:  options,
		closed:   make(chan bool),
		start:    make(chan *service, 128),
		services: make(map[string]*service),
	}
}

// Init initializes runtime options
func (r *runtime) Init(opts ...Option) error {
	r.Lock()
	defer r.Unlock()

	for _, o := range opts {
		o(&r.options)
	}

	return nil
}

// run runs the runtime management loop
func (r *runtime) run(events <-chan Event) {
	t := time.NewTicker(time.Second * 5)
	defer t.Stop()

	// process event processes an incoming event
	processEvent := func(event Event, service *service) error {
		// get current vals
		r.RLock()
		name := service.Name
		updated := service.updated
		r.RUnlock()

		// only process if the timestamp is newer
		if !event.Timestamp.After(updated) {
			return nil
		}

		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
			logger.Debugf("Runtime updating service %s", name)
		}

		// this will cause a delete followed by created
		if err := r.Update(service.Service); err != nil {
			return err
		}

		// update the local timestamp
		r.Lock()
		service.updated = updated
		r.Unlock()

		return nil
	}

	for {
		select {
		case <-t.C:
			// check running services
			r.RLock()
			for _, service := range r.services {
				if !service.ShouldStart() {
					continue
				}

				// TODO: check service error
				if logger.V(logger.DebugLevel, logger.DefaultLogger) {
					logger.Debugf("Runtime starting %s", service.Name)
				}
				if err := service.Start(); err != nil {
					if logger.V(logger.DebugLevel, logger.DefaultLogger) {
						logger.Debugf("Runtime error starting %s: %v", service.Name, err)
					}
				}
			}
			r.RUnlock()
		case service := <-r.start:
			if !service.ShouldStart() {
				continue
			}
			// TODO: check service error
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime starting service %s", service.Name)
			}
			if err := service.Start(); err != nil {
				if logger.V(logger.DebugLevel, logger.DefaultLogger) {
					logger.Debugf("Runtime error starting service %s: %v", service.Name, err)
				}
			}
		case event := <-events:
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime received notification event: %v", event)
			}
			// NOTE: we only handle Update events for now
			switch event.Type {
			case Update:
				if len(event.Service) > 0 {
					r.RLock()
					service, ok := r.services[event.Service]
					r.RUnlock()
					if !ok {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime unknown service: %s", event.Service)
						}
						continue
					}
					if err := processEvent(event, service); err != nil {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime error updating service %s: %v", event.Service, err)
						}
					}
					continue
				}

				r.RLock()
				services := r.services
				r.RUnlock()

				// if blank service was received we update all services
				for _, service := range services {
					if err := processEvent(event, service); err != nil {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime error updating service %s: %v", service.Name, err)
						}
					}
				}
			}
		case <-r.closed:
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime stopped")
			}
			return
		}
	}
}

func logFile(serviceName string) string {
	name := strings.Replace(serviceName, "/", "-", -1)
	return filepath.Join(os.TempDir(), "micro", "logs", fmt.Sprintf("%v.log", name))
}

// Create creates a new service which is then started by runtime
func (r *runtime) Create(s *Service, opts ...CreateOption) error {
	r.Lock()
	defer r.Unlock()

	if _, ok := r.services[s.Name]; ok {
		return errors.New("service already running")
	}

	var options CreateOptions
	for _, o := range opts {
		o(&options)
	}

	if len(options.Command) == 0 {
		options.Command = []string{"go"}
		options.Args = []string{"run", "."}
	}

	// create new service
	service := newService(s, options)

	f, err := os.OpenFile(logFile(service.Name), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatal(err)
	}

	if service.output != nil {
		service.output = io.MultiWriter(service.output, f)
	} else {
		service.output = f
	}
	// start the service
	if err := service.Start(); err != nil {
		return err
	}

	// save service
	r.services[s.Name] = service

	return nil
}

// @todo: Getting existing lines is not supported yet.
// The reason for this is because it's hard to calculate line offset
// as opposed to character offset.
// This logger streams by default and only supports the `StreamCount` option.
func (r *runtime) Logs(s *Service, options ...LogsOption) (LogStream, error) {
	lopts := LogsOptions{}
	for _, o := range options {
		o(&lopts)
	}
	ret := &logStream{
		service: s.Name,
		stream:  make(chan LogRecord),
		stop:    make(chan bool),
	}
	t, err := tail.TailFile(logFile(s.Name), tail.Config{Follow: true, Location: &tail.SeekInfo{
		Whence: 2,
		Offset: 0,
	}, Logger: tail.DiscardingLogger})
	if err != nil {
		return nil, err
	}
	ret.tail = t
	go func() {
		for line := range t.Lines {
			ret.stream <- LogRecord{Message: line.Text}
		}
	}()
	return ret, nil
}

type logStream struct {
	tail    *tail.Tail
	service string
	stream  chan LogRecord
	sync.Mutex
	stop chan bool
	err  error
}

func (l *logStream) Chan() chan LogRecord {
	return l.stream
}

func (l *logStream) Error() error {
	return l.err
}

func (l *logStream) Stop() error {
	l.Lock()
	defer l.Unlock()
	// @todo seems like this is causing a hangup
	//err := l.tail.Stop()
	//if err != nil {
	//	return err
	//}
	select {
	case <-l.stop:
		return nil
	default:
		close(l.stop)
	}
	return nil
}

// Read returns all instances of requested service
// If no service name is provided we return all the track services.
func (r *runtime) Read(opts ...ReadOption) ([]*Service, error) {
	r.Lock()
	defer r.Unlock()

	gopts := ReadOptions{}
	for _, o := range opts {
		o(&gopts)
	}

	save := func(k, v string) bool {
		if len(k) == 0 {
			return true
		}
		return k == v
	}

	//nolint:prealloc
	var services []*Service

	for _, service := range r.services {
		if !save(gopts.Service, service.Name) {
			continue
		}
		if !save(gopts.Version, service.Version) {
			continue
		}
		// TODO deal with service type
		// no version has sbeen requested, just append the service
		services = append(services, service.Service)
	}

	return services, nil
}

// Update attemps to update the service
func (r *runtime) Update(s *Service) error {
	var opts []CreateOption

	// check if the service already exists
	r.RLock()
	if service, ok := r.services[s.Name]; ok {
		opts = append(opts, WithOutput(service.output))
	}
	r.RUnlock()

	// delete the service
	if err := r.Delete(s); err != nil {
		return err
	}

	// create new service
	return r.Create(s, opts...)
}

// Delete removes the service from the runtime and stops it
func (r *runtime) Delete(s *Service) error {
	r.Lock()
	defer r.Unlock()

	if logger.V(logger.DebugLevel, logger.DefaultLogger) {
		logger.Debugf("Runtime deleting service %s", s.Name)
	}
	if s, ok := r.services[s.Name]; ok {
		// check if running
		if s.Running() {
			delete(r.services, s.Name)
			return nil
		}
		// otherwise stop it
		if err := s.Stop(); err != nil {
			return err
		}
		// delete it
		delete(r.services, s.Name)
		return nil
	}

	return nil
}

// List returns a slice of all services tracked by the runtime
func (r *runtime) List() ([]*Service, error) {
	r.RLock()
	defer r.RUnlock()

	services := make([]*Service, 0, len(r.services))

	for _, service := range r.services {
		services = append(services, service.Service)
	}

	return services, nil
}

// Start starts the runtime
func (r *runtime) Start() error {
	r.Lock()
	defer r.Unlock()

	// already running
	if r.running {
		return nil
	}

	// set running
	r.running = true
	r.closed = make(chan bool)

	var events <-chan Event
	if r.options.Scheduler != nil {
		var err error
		events, err = r.options.Scheduler.Notify()
		if err != nil {
			// TODO: should we bail here?
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime failed to start update notifier")
			}
		}
	}

	go r.run(events)

	return nil
}

// Stop stops the runtime
func (r *runtime) Stop() error {
	r.Lock()
	defer r.Unlock()

	if !r.running {
		return nil
	}

	select {
	case <-r.closed:
		return nil
	default:
		close(r.closed)

		// set not running
		r.running = false

		// stop all the services
		for _, service := range r.services {
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime stopping %s", service.Name)
			}
			service.Stop()
		}
		// stop the scheduler
		if r.options.Scheduler != nil {
			return r.options.Scheduler.Close()
		}
	}

	return nil
}

// String implements stringer interface
func (r *runtime) String() string {
	return "local"
}
<commit_msg>fix log file creation<commit_after>package runtime

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/hpcloud/tail"
	"github.com/micro/go-micro/v2/logger"
)

type runtime struct {
	sync.RWMutex
	// options configure runtime
	options Options
	// used to stop the runtime
	closed chan bool
	// used to start new services
	start chan *service
	// indicates if we're running
	running bool
	// the service map
	// TODO: track different versions of the same service
	services map[string]*service
}

// NewRuntime creates new local runtime and returns it
func NewRuntime(opts ...Option) Runtime {
	// get default options
	options := Options{}

	// apply requested options
	for _, o := range opts {
		o(&options)
	}

	// make the logs directory
	path := filepath.Join(os.TempDir(), "micro", "logs")
	_ = os.MkdirAll(path, 0755)

	return &runtime{
		options:  options,
		closed:   make(chan bool),
		start:    make(chan *service, 128),
		services: make(map[string]*service),
	}
}

// Init initializes runtime options
func (r *runtime) Init(opts ...Option) error {
	r.Lock()
	defer r.Unlock()

	for _, o := range opts {
		o(&r.options)
	}

	return nil
}

// run runs the runtime management loop
func (r *runtime) run(events <-chan Event) {
	t := time.NewTicker(time.Second * 5)
	defer t.Stop()

	// process event processes an incoming event
	processEvent := func(event Event, service *service) error {
		// get current vals
		r.RLock()
		name := service.Name
		updated := service.updated
		r.RUnlock()

		// only process if the timestamp is newer
		if !event.Timestamp.After(updated) {
			return nil
		}

		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
			logger.Debugf("Runtime updating service %s", name)
		}

		// this will cause a delete followed by created
		if err := r.Update(service.Service); err != nil {
			return err
		}

		// update the local timestamp
		r.Lock()
		service.updated = updated
		r.Unlock()

		return nil
	}

	for {
		select {
		case <-t.C:
			// check running services
			r.RLock()
			for _, service := range r.services {
				if !service.ShouldStart() {
					continue
				}

				// TODO: check service error
				if logger.V(logger.DebugLevel, logger.DefaultLogger) {
					logger.Debugf("Runtime starting %s", service.Name)
				}
				if err := service.Start(); err != nil {
					if logger.V(logger.DebugLevel, logger.DefaultLogger) {
						logger.Debugf("Runtime error starting %s: %v", service.Name, err)
					}
				}
			}
			r.RUnlock()
		case service := <-r.start:
			if !service.ShouldStart() {
				continue
			}
			// TODO: check service error
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime starting service %s", service.Name)
			}
			if err := service.Start(); err != nil {
				if logger.V(logger.DebugLevel, logger.DefaultLogger) {
					logger.Debugf("Runtime error starting service %s: %v", service.Name, err)
				}
			}
		case event := <-events:
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime received notification event: %v", event)
			}
			// NOTE: we only handle Update events for now
			switch event.Type {
			case Update:
				if len(event.Service) > 0 {
					r.RLock()
					service, ok := r.services[event.Service]
					r.RUnlock()
					if !ok {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime unknown service: %s", event.Service)
						}
						continue
					}
					if err := processEvent(event, service); err != nil {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime error updating service %s: %v", event.Service, err)
						}
					}
					continue
				}

				r.RLock()
				services := r.services
				r.RUnlock()

				// if blank service was received we update all services
				for _, service := range services {
					if err := processEvent(event, service); err != nil {
						if logger.V(logger.DebugLevel, logger.DefaultLogger) {
							logger.Debugf("Runtime error updating service %s: %v", service.Name, err)
						}
					}
				}
			}
		case <-r.closed:
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime stopped")
			}
			return
		}
	}
}

func logFile(serviceName string) string {
	// make the directory
	name := strings.Replace(serviceName, "/", "-", -1)
	path := filepath.Join(os.TempDir(), "micro", "logs")
	return filepath.Join(path, fmt.Sprintf("%v.log", name))
}

// Create creates a new service which is then started by runtime
func (r *runtime) Create(s *Service, opts ...CreateOption) error {
	r.Lock()
	defer r.Unlock()

	if _, ok := r.services[s.Name]; ok {
		return errors.New("service already running")
	}

	var options CreateOptions
	for _, o := range opts {
		o(&options)
	}

	if len(options.Command) == 0 {
		options.Command = []string{"go"}
		options.Args = []string{"run", "."}
	}

	// create new service
	service := newService(s, options)

	f, err := os.OpenFile(logFile(service.Name), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatal(err)
	}

	if service.output != nil {
		service.output = io.MultiWriter(service.output, f)
	} else {
		service.output = f
	}
	// start the service
	if err := service.Start(); err != nil {
		return err
	}

	// save service
	r.services[s.Name] = service

	return nil
}

// @todo: Getting existing lines is not supported yet.
// The reason for this is because it's hard to calculate line offset
// as opposed to character offset.
// This logger streams by default and only supports the `StreamCount` option.
func (r *runtime) Logs(s *Service, options ...LogsOption) (LogStream, error) {
	lopts := LogsOptions{}
	for _, o := range options {
		o(&lopts)
	}
	ret := &logStream{
		service: s.Name,
		stream:  make(chan LogRecord),
		stop:    make(chan bool),
	}
	t, err := tail.TailFile(logFile(s.Name), tail.Config{Follow: true, Location: &tail.SeekInfo{
		Whence: 2,
		Offset: 0,
	}, Logger: tail.DiscardingLogger})
	if err != nil {
		return nil, err
	}
	ret.tail = t
	go func() {
		for line := range t.Lines {
			ret.stream <- LogRecord{Message: line.Text}
		}
	}()
	return ret, nil
}

type logStream struct {
	tail    *tail.Tail
	service string
	stream  chan LogRecord
	sync.Mutex
	stop chan bool
	err  error
}

func (l *logStream) Chan() chan LogRecord {
	return l.stream
}

func (l *logStream) Error() error {
	return l.err
}

func (l *logStream) Stop() error {
	l.Lock()
	defer l.Unlock()
	// @todo seems like this is causing a hangup
	//err := l.tail.Stop()
	//if err != nil {
	//	return err
	//}
	select {
	case <-l.stop:
		return nil
	default:
		close(l.stop)
	}
	return nil
}

// Read returns all instances of requested service
// If no service name is provided we return all the track services.
func (r *runtime) Read(opts ...ReadOption) ([]*Service, error) {
	r.Lock()
	defer r.Unlock()

	gopts := ReadOptions{}
	for _, o := range opts {
		o(&gopts)
	}

	save := func(k, v string) bool {
		if len(k) == 0 {
			return true
		}
		return k == v
	}

	//nolint:prealloc
	var services []*Service

	for _, service := range r.services {
		if !save(gopts.Service, service.Name) {
			continue
		}
		if !save(gopts.Version, service.Version) {
			continue
		}
		// TODO deal with service type
		// no version has sbeen requested, just append the service
		services = append(services, service.Service)
	}

	return services, nil
}

// Update attemps to update the service
func (r *runtime) Update(s *Service) error {
	var opts []CreateOption

	// check if the service already exists
	r.RLock()
	if service, ok := r.services[s.Name]; ok {
		opts = append(opts, WithOutput(service.output))
	}
	r.RUnlock()

	// delete the service
	if err := r.Delete(s); err != nil {
		return err
	}

	// create new service
	return r.Create(s, opts...)
}

// Delete removes the service from the runtime and stops it
func (r *runtime) Delete(s *Service) error {
	r.Lock()
	defer r.Unlock()

	if logger.V(logger.DebugLevel, logger.DefaultLogger) {
		logger.Debugf("Runtime deleting service %s", s.Name)
	}
	if s, ok := r.services[s.Name]; ok {
		// check if running
		if s.Running() {
			delete(r.services, s.Name)
			return nil
		}
		// otherwise stop it
		if err := s.Stop(); err != nil {
			return err
		}
		// delete it
		delete(r.services, s.Name)
		return nil
	}

	return nil
}

// List returns a slice of all services tracked by the runtime
func (r *runtime) List() ([]*Service, error) {
	r.RLock()
	defer r.RUnlock()

	services := make([]*Service, 0, len(r.services))

	for _, service := range r.services {
		services = append(services, service.Service)
	}

	return services, nil
}

// Start starts the runtime
func (r *runtime) Start() error {
	r.Lock()
	defer r.Unlock()

	// already running
	if r.running {
		return nil
	}

	// set running
	r.running = true
	r.closed = make(chan bool)

	var events <-chan Event
	if r.options.Scheduler != nil {
		var err error
		events, err = r.options.Scheduler.Notify()
		if err != nil {
			// TODO: should we bail here?
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime failed to start update notifier")
			}
		}
	}

	go r.run(events)

	return nil
}

// Stop stops the runtime
func (r *runtime) Stop() error {
	r.Lock()
	defer r.Unlock()

	if !r.running {
		return nil
	}

	select {
	case <-r.closed:
		return nil
	default:
		close(r.closed)

		// set not running
		r.running = false

		// stop all the services
		for _, service := range r.services {
			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
				logger.Debugf("Runtime stopping %s", service.Name)
			}
			service.Stop()
		}
		// stop the scheduler
		if r.options.Scheduler != nil {
			return r.options.Scheduler.Close()
		}
	}

	return nil
}

// String implements stringer interface
func (r *runtime) String() string {
	return "local"
}
<|endoftext|>


<commit_before>package aralog

import (
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

// These flags define which text to prefix to each log entry generated by the Logger.
const (
// Bits or'ed together to control what's printed. There is no control over the
// order they appear (the order listed here) or the format they present (as
// described in the comments).  A colon appears after these items:
//	2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate = 1 << iota     // the date: 2009/01/23
	Ltime                         // the time: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LstdFlags = Ldate | Ltime // initial values for the standard logger
)

// A Logger represents an active logging object that generates lines of
// output to an io.Writer.  Each logging operation makes a single call to
// the Writer's Write method.  A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
	mu      sync.Mutex // ensures atomic writes; protects the following fields
	prefix  string     // prefix to write at beginning of each line
	flag    int        // properties
	out     io.Writer  // destination for output
	buf     []byte     // for accumulating text to write
	size    uint // current size of log file
	path    string // file path if output to a file
	maxsize uint // minimal maxsize should >= 1MB
}

var currentOutFile *os.File

// New creates a new Logger.   The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
	return &Logger{out: out, prefix: prefix, flag: flag}
}

// NewFileLogger create a new Logger which output to a file specified
func NewFileLogger(path string, flag int) (*Logger, error) {
	return NewRollFileLogger(path, 1024*1024*10, flag)
}

// NewRollFileLogger create a new Logger which output to a file specified path,
// and roll at specified size
func NewRollFileLogger(path string, maxsize uint, flag int) (*Logger, error) {
	out, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return nil, err
	}

	currentOutFile = out

	// minimal maxsize should >= 1MB
	if maxsize < 1024 * 1024 {
		maxsize = 1024 * 1024 * 10
	}

	return &Logger{out: out, prefix: "", flag: flag, path: path, maxsize: maxsize}, nil
}

//var std = New(os.Stderr, "", LstdFlags)

// Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
// Knows the buffer has capacity.
func itoa(buf *[]byte, i int, wid int) {
	var u uint = uint(i)
	if u == 0 && wid <= 1 {
		*buf = append(*buf, '0')
		return
	}

	// Assemble decimal in reverse order.
	var b [32]byte
	bp := len(b)
	for ; u > 0 || wid > 0; u /= 10 {
		bp--
		wid--
		b[bp] = byte(u%10) + '0'
	}
	*buf = append(*buf, b[bp:]...)
}

func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
	*buf = append(*buf, l.prefix...)
	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
		if l.flag&Ldate != 0 {
			year, month, day := t.Date()
			itoa(buf, year, 4)
			*buf = append(*buf, '/')
			itoa(buf, int(month), 2)
			*buf = append(*buf, '/')
			itoa(buf, day, 2)
			*buf = append(*buf, ' ')
		}
		if l.flag&(Ltime|Lmicroseconds) != 0 {
			hour, min, sec := t.Clock()
			itoa(buf, hour, 2)
			*buf = append(*buf, ':')
			itoa(buf, min, 2)
			*buf = append(*buf, ':')
			itoa(buf, sec, 2)
			if l.flag&Lmicroseconds != 0 {
				*buf = append(*buf, '.')
				itoa(buf, t.Nanosecond()/1e3, 6)
			}
			*buf = append(*buf, ' ')
		}
	}
	if l.flag&(Lshortfile|Llongfile) != 0 {
		if l.flag&Lshortfile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		*buf = append(*buf, file...)
		*buf = append(*buf, ':')
		itoa(buf, line, -1)
		*buf = append(*buf, ": "...)
	}
}

// Output writes the output for a logging event.  The string s contains
// the text to print after the prefix specified by the flags of the
// Logger.  A newline is appended if the last character of s is not
// already a newline.  Calldepth is used to recover the PC and is
// provided for generality, although at the moment on all pre-defined
// paths it will be 2.
func (l *Logger) output(calldepth int, s string) error {
	now := time.Now() // get this early.
	var file string
	var line int
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&(Lshortfile|Llongfile) != 0 {
		// release lock while getting caller info - it's expensive.
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	l.formatHeader(&l.buf, now, file, line)
	l.buf = append(l.buf, s...)
	if len(s) > 0 && s[len(s)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}

	if len(l.path) > 0 {
		err := l.rollFile(now)
		if err != nil {
			return err
		}
	}
	_, err := l.out.Write(l.buf)
	return err
}

func (l *Logger) rollFile(now time.Time) error {
	l.size += uint(len(l.buf))
	// file rotation if size > maxsize
	if l.size > l.maxsize {

		// close file before rename it
		if currentOutFile != nil {
			// ignore if Close() failed
			err := currentOutFile.Close()
			if err != nil {
				l.buf = append(l.buf, ("[XXX] ARALOGGER ERROR: Close current output file failed, " + err.Error())...)
				l.buf = append(l.buf, '\n')
			}
		}

		newPath := l.path
		err := os.Rename(l.path,
			l.path + string(now.Year()) + string(now.Month()) + string(now.Day()) +
			string(now.Hour()) + string(now.Minute()) + string(now.Second()))
		if err != nil {
			l.buf = append(l.buf, ("[XXX] ARALOGGER ERROR: Rolling file failed, " + err.Error())...)
			l.buf = append(l.buf, '\n')
			newPath = l.path + string(now.Unix())
		}

		newOut, err := os.OpenFile(newPath, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}

		currentOutFile = newOut
		l.out = newOut
		l.size = uint(len(l.buf))
	}

	return nil
}

func (l *Logger) Debug(s string) error {
	err := l.output(2, s)
	return err
}
<commit_msg>fix file rolling<commit_after>package aralog

import (
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

// These flags define which text to prefix to each log entry generated by the Logger.
const (
// Bits or'ed together to control what's printed. There is no control over the
// order they appear (the order listed here) or the format they present (as
// described in the comments).  A colon appears after these items:
//	2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate = 1 << iota     // the date: 2009/01/23
	Ltime                         // the time: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LstdFlags = Ldate | Ltime // initial values for the standard logger
)

// A Logger represents an active logging object that generates lines of
// output to an io.Writer.  Each logging operation makes a single call to
// the Writer's Write method.  A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
	mu      sync.Mutex // ensures atomic writes; protects the following fields
	prefix  string     // prefix to write at beginning of each line
	flag    int        // properties
	out     io.Writer  // destination for output
	buf     []byte     // for accumulating text to write
	size    uint // current size of log file
	path    string // file path if output to a file
	maxsize uint // minimal maxsize should >= 1MB
}

var currentOutFile *os.File

// New creates a new Logger.   The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
	return &Logger{out: out, prefix: prefix, flag: flag}
}

// NewFileLogger create a new Logger which output to a file specified
func NewFileLogger(path string, flag int) (*Logger, error) {
	return NewRollFileLogger(path, 1024*1024*10, flag)
}

// NewRollFileLogger create a new Logger which output to a file specified path,
// and roll at specified size
func NewRollFileLogger(path string, maxsize uint, flag int) (*Logger, error) {
	out, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return nil, err
	}

	currentOutFile = out

	// minimal maxsize should >= 1MB
	if maxsize < 1024 * 1024 {
		maxsize = 1024 * 1024 * 10
	}

	return &Logger{out: out, prefix: "", flag: flag, path: path, maxsize: maxsize}, nil
}

//var std = New(os.Stderr, "", LstdFlags)

// Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
// Knows the buffer has capacity.
func itoa(buf *[]byte, i int, wid int) {
	var u uint = uint(i)
	if u == 0 && wid <= 1 {
		*buf = append(*buf, '0')
		return
	}

	// Assemble decimal in reverse order.
	var b [32]byte
	bp := len(b)
	for ; u > 0 || wid > 0; u /= 10 {
		bp--
		wid--
		b[bp] = byte(u%10) + '0'
	}
	*buf = append(*buf, b[bp:]...)
}

func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
	*buf = append(*buf, l.prefix...)
	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
		if l.flag&Ldate != 0 {
			year, month, day := t.Date()
			itoa(buf, year, 4)
			*buf = append(*buf, '/')
			itoa(buf, int(month), 2)
			*buf = append(*buf, '/')
			itoa(buf, day, 2)
			*buf = append(*buf, ' ')
		}
		if l.flag&(Ltime|Lmicroseconds) != 0 {
			hour, min, sec := t.Clock()
			itoa(buf, hour, 2)
			*buf = append(*buf, ':')
			itoa(buf, min, 2)
			*buf = append(*buf, ':')
			itoa(buf, sec, 2)
			if l.flag&Lmicroseconds != 0 {
				*buf = append(*buf, '.')
				itoa(buf, t.Nanosecond()/1e3, 6)
			}
			*buf = append(*buf, ' ')
		}
	}
	if l.flag&(Lshortfile|Llongfile) != 0 {
		if l.flag&Lshortfile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		*buf = append(*buf, file...)
		*buf = append(*buf, ':')
		itoa(buf, line, -1)
		*buf = append(*buf, ": "...)
	}
}

// Output writes the output for a logging event.  The string s contains
// the text to print after the prefix specified by the flags of the
// Logger.  A newline is appended if the last character of s is not
// already a newline.  Calldepth is used to recover the PC and is
// provided for generality, although at the moment on all pre-defined
// paths it will be 2.
func (l *Logger) output(calldepth int, s string) error {
	now := time.Now() // get this early.
	var file string
	var line int
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&(Lshortfile|Llongfile) != 0 {
		// release lock while getting caller info - it's expensive.
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	l.formatHeader(&l.buf, now, file, line)
	l.buf = append(l.buf, s...)
	if len(s) > 0 && s[len(s)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}

	if len(l.path) > 0 {
		err := l.rollFile(now)
		if err != nil {
			return err
		}
	}
	_, err := l.out.Write(l.buf)
	return err
}

func (l *Logger) rollFile(now time.Time) error {
	l.size += uint(len(l.buf))
	// file rotation if size > maxsize
	if l.size > l.maxsize {

		// close file before rename it
		if currentOutFile != nil {
			// ignore if Close() failed
			err := currentOutFile.Close()
			if err == nil{
				// TODO zip it
			} else {
				l.buf = append(l.buf, ("[XXX] ARALOGGER ERROR: Close current output file failed, " + err.Error())...)
				l.buf = append(l.buf, '\n')
			}
		}

		newPath := l.path

		// rename l.path to nameYYYYMMDDhhmmss
		err := os.Rename(l.path,
			l.path + string(now.Year()) + string(now.Month()) + string(now.Day()) +
			string(now.Hour()) + string(now.Minute()) + string(now.Second()))
		if err != nil {
			l.buf = append(l.buf, ("[XXX] ARALOGGER ERROR: Rolling file failed, " + err.Error())...)
			l.buf = append(l.buf, '\n')

			// if rename failed, start a new log file with different name
			newPath = l.path + string(now.Unix())
		}

		newOut, err := os.OpenFile(newPath, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}

		currentOutFile = newOut
		l.out = newOut
		l.size = uint(len(l.buf))
	}

	return nil
}

func (l *Logger) Debug(s string) error {
	err := l.output(2, s)
	return err
}
<|endoftext|>