<commit_before>// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

var (
	cmd         *exec.Cmd
	state       sync.Mutex
	eventTime   = make(map[string]int64)
	buildPeriod time.Time
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
		os.Exit(2)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true

				// Skip TMP files for Sublime Text.
				if checkTMPFile(e.Name) {
					continue
				}
				if !chekcIfWatchExt(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if buildPeriod.Add(1 * time.Second).After(time.Now()) {
					continue
				}
				buildPeriod = time.Now()

				mt := getFileModTime(e.Name)
				if t := eventTime[e.Name]; mt == t {
					ColorLog("[SKIP] # %s #\n", e.String())
					isbuild = false
				}

				eventTime[e.Name] = mt

				if isbuild {
					ColorLog("[EVEN] %s\n", e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
			}
		}
	}()

	ColorLog("[INFO] Initializing watcher...\n")
	for _, path := range paths {
		ColorLog("[TRAC] Directory( %s )\n", path)
		err = watcher.Watch(path)
		if err != nil {
			ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
			os.Exit(2)
		}
	}

}

// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
func getFileModTime(path string) int64 {
	path = strings.Replace(path, "\\", "/", -1)
	f, err := os.Open(path)
	if err != nil {
		ColorLog("[ERRO] Fail to open file[ %s ]\n", err)
		return time.Now().Unix()
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		ColorLog("[ERRO] Fail to get file information[ %s ]\n", err)
		return time.Now().Unix()
	}

	return fi.ModTime().Unix()
}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	ColorLog("[INFO] Start building...\n")
	path, _ := os.Getwd()
	os.Chdir(path)

	cmdName := "go"
	if conf.Gopm.Enable {
		cmdName = "gopm"
	}

	var err error
	// For applications use full import path like "github.com/.../.."
	// are able to use "go install" to reduce build time.
	if conf.GoInstall || conf.Gopm.Install {
		icmd := exec.Command(cmdName, "install")
		icmd.Stdout = os.Stdout
		icmd.Stderr = os.Stderr
		err = icmd.Run()
	}

	if err == nil {
		appName := appname
		if runtime.GOOS == "windows" {
			appName += ".exe"
		}
		binPath := GetGOPATHs()[0] + "/bin/" + appName

		if conf.GoInstall && isExist(binPath) {
			os.Rename(binPath, appName)
			ColorLog("[INFO] Build command reduced\n")
		} else {
			bcmd := exec.Command(cmdName, "build")
			bcmd.Stdout = os.Stdout
			bcmd.Stderr = os.Stderr
			err = bcmd.Run()
		}
	}

	if err != nil {
		ColorLog("[ERRO] ============== Build failed ===================\n")
		return
	}
	ColorLog("[SUCC] Build was successful\n")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill -> ", e)
		}
	}()
	if cmd != nil && cmd.Process != nil {
		cmd.Process.Kill()
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	ColorLog("[INFO] Restarting %s ...\n", appname)
	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Args = append([]string{appname}, conf.CmdArgs...)
	cmd.Env = append(os.Environ(), conf.Envs...)

	go cmd.Run()
	ColorLog("[INFO] %s is running...\n", appname)
	started <- true
}

// checkTMPFile returns true if the event was for TMP files.
func checkTMPFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

var watchExts = []string{".go"}

// chekcIfWatchExt returns true if the name HasSuffix <watch_ext>.
func chekcIfWatchExt(name string) bool {
	for _, s := range watchExts {
		if strings.HasSuffix(name, s) {
			return true
		}
	}
	return false
}
<commit_msg>Bug fixed<commit_after>// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

var (
	cmd         *exec.Cmd
	state       sync.Mutex
	eventTime   = make(map[string]int64)
	buildPeriod time.Time
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
		os.Exit(2)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true

				// Skip TMP files for Sublime Text.
				if checkTMPFile(e.Name) {
					continue
				}
				if !chekcIfWatchExt(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if buildPeriod.Add(1 * time.Second).After(time.Now()) {
					continue
				}
				buildPeriod = time.Now()

				mt := getFileModTime(e.Name)
				if t := eventTime[e.Name]; mt == t {
					ColorLog("[SKIP] # %s #\n", e.String())
					isbuild = false
				}

				eventTime[e.Name] = mt

				if isbuild {
					ColorLog("[EVEN] %s\n", e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
			}
		}
	}()

	ColorLog("[INFO] Initializing watcher...\n")
	for _, path := range paths {
		ColorLog("[TRAC] Directory( %s )\n", path)
		err = watcher.Watch(path)
		if err != nil {
			ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
			os.Exit(2)
		}
	}

}

// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
func getFileModTime(path string) int64 {
	path = strings.Replace(path, "\\", "/", -1)
	f, err := os.Open(path)
	if err != nil {
		ColorLog("[ERRO] Fail to open file[ %s ]\n", err)
		return time.Now().Unix()
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		ColorLog("[ERRO] Fail to get file information[ %s ]\n", err)
		return time.Now().Unix()
	}

	return fi.ModTime().Unix()
}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	ColorLog("[INFO] Start building...\n")
	path, _ := os.Getwd()
	os.Chdir(path)

	cmdName := "go"
	if conf.Gopm.Enable {
		cmdName = "gopm"
	}

	var err error
	// For applications use full import path like "github.com/.../.."
	// are able to use "go install" to reduce build time.
	if conf.GoInstall || conf.Gopm.Install {
		icmd := exec.Command(cmdName, "install")
		icmd.Stdout = os.Stdout
		icmd.Stderr = os.Stderr
		err = icmd.Run()
	}

	if err == nil {
		appName := appname
		if runtime.GOOS == "windows" {
			appName += ".exe"
		}
		if isExist(appName) {
			os.Remove(appName)
		}
		binPath := GetGOPATHs()[0] + "/bin/" + appName

		if conf.GoInstall && isExist(binPath) {
			os.Rename(binPath, appName)
			ColorLog("[INFO] Build command reduced\n")
		} else {
			bcmd := exec.Command(cmdName, "build")
			bcmd.Stdout = os.Stdout
			bcmd.Stderr = os.Stderr
			err = bcmd.Run()
		}
	}

	if err != nil {
		ColorLog("[ERRO] ============== Build failed ===================\n")
		return
	}
	ColorLog("[SUCC] Build was successful\n")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill -> ", e)
		}
	}()
	if cmd != nil && cmd.Process != nil {
		cmd.Process.Kill()
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	ColorLog("[INFO] Restarting %s ...\n", appname)
	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Args = append([]string{appname}, conf.CmdArgs...)
	cmd.Env = append(os.Environ(), conf.Envs...)

	go cmd.Run()
	ColorLog("[INFO] %s is running...\n", appname)
	started <- true
}

// checkTMPFile returns true if the event was for TMP files.
func checkTMPFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

var watchExts = []string{".go"}

// chekcIfWatchExt returns true if the name HasSuffix <watch_ext>.
func chekcIfWatchExt(name string) bool {
	for _, s := range watchExts {
		if strings.HasSuffix(name, s) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
)

// watch watchers the input paths. The returned Watcher should only be used in
// tests.
func watch(inputPaths, ignoredPaths []string, cmdCh chan<- event) (*fsnotify.Watcher, error) {
	// Creates an Ignorer that just ignores file paths the user
	// specifically asked to be ignored.
	ui, err := createUserIgnorer(ignoredPaths)
	if err != nil {
		return nil, err
	}

	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("unable to create watcher: %s", err)
	}

	// Watch user-specified paths and create a set of them for walking
	// later. Paths that are both asked to be watched and ignored by
	// the user are ignored.
	userPaths := make(map[string]bool)
	includedHiddenFiles := make(map[string]bool)
	for _, path := range inputPaths {
		fullPath, err := filepath.Abs(path)
		if err != nil {
			w.Close()
			return nil, errors.New("unable to get current working directory while working with user-watched paths")
		}
		if userPaths[fullPath] || ui.IsIgnored(path) {
			continue
		}
		err = w.Add(fullPath)
		if err != nil {
			w.Close()
			return nil, fmt.Errorf("unable to watch '%s': %s", path, err)
		}
		userPaths[fullPath] = true
	}

	// Create some useful sets from the user-specified paths to be
	// used in smartIgnorer (and, therefore, listenForEvents). One is
	// the set of hidden paths that the user does not want ignored to
	// be used in smartIgnorer. We create this smaller map because the
	// amount of paths the user asked to watch may be large.
	//
	// We also create the sets renameDirs and renameChildren to better
	// handle files that are renamed away and back from the paths the
	// user wanted watched. To be more concrete, folks might want to
	// watch only the normal file "foobar" but their tooling moves
	// foobar away and then back (like vim does on save). This will
	// cause the watch to fire on the first move but then never again,
	// even when its returned. So, we have to track the parent
	// directory of foobar in order to capture when foobar shows up in
	// its parent directory again but we don't want to send all events
	// in that parent directory.
	renameDirs := make(map[string]bool)
	renameChildren := make(map[string]bool)
	for fullPath, _ := range userPaths {
		baseName := filepath.Base(fullPath)
		if strings.HasPrefix(baseName, ".") {
			includedHiddenFiles[fullPath] = true
		}

		dirPath := filepath.Dir(fullPath)
		if !userPaths[dirPath] && dirPath != "" {
			if !renameDirs[dirPath] {
				err = w.Add(dirPath)
				if err != nil {
					w.Close()
					return nil, fmt.Errorf("unable to watch rename-watched-only dir '%s': %s", fullPath, err)
				}
			}
			renameDirs[dirPath] = true
			renameChildren[fullPath] = true
		}
	}
	ig := &smartIgnorer{
		includedHiddenFiles: includedHiddenFiles,
		ui:                  ui,
		renameDirs:          renameDirs,
		renameChildren:      renameChildren,
	}

	go listenForEvents(w, cmdCh, ig)
	return w, nil
}

type event struct {
	time.Time
	Event fsnotify.Event
}

func listenForEvents(w *fsnotify.Watcher, cmdCh chan<- event, ignorer Ignorer) {
	for {
		select {
		case ev, ok := <-w.Events:
			if !ok {
				return
			}
			if ignorer.IsIgnored(ev.Name) {
				continue
			}
			if *verbose {
				log.Printf("filtered file change: %s", ev)
			}
			cmdCh <- event{
				Time:  time.Now(),
				Event: ev,
			}
		case err := <-w.Errors:
			// w.Close causes this.
			if err == nil {
				return
			}
			log.Println("watch error:", err)
		}
	}
}

func createUserIgnorer(ignoredPaths []string) (*userIgnorer, error) {
	ignored := make(map[string]bool)
	ignoredDirs := make([]string, 0)
	for _, in := range ignoredPaths {
		in = strings.TrimSpace(in)
		if len(in) == 0 {
			continue
		}
		path, err := filepath.Abs(in)
		if err != nil {
			return nil, errors.New("unable to get current working dir while working with ignored paths")
		}
		ignored[path] = true
		dirPath := path
		if !strings.HasSuffix(path, "/") {
			dirPath += "/"
		}
		ignoredDirs = append(ignoredDirs, dirPath)
	}
	return &userIgnorer{ignored, ignoredDirs}, nil
}
<commit_msg>clear up the verbose log meaning<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
)

// watch watchers the input paths. The returned Watcher should only be used in
// tests.
func watch(inputPaths, ignoredPaths []string, cmdCh chan<- event) (*fsnotify.Watcher, error) {
	// Creates an Ignorer that just ignores file paths the user
	// specifically asked to be ignored.
	ui, err := createUserIgnorer(ignoredPaths)
	if err != nil {
		return nil, err
	}

	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("unable to create watcher: %s", err)
	}

	// Watch user-specified paths and create a set of them for walking
	// later. Paths that are both asked to be watched and ignored by
	// the user are ignored.
	userPaths := make(map[string]bool)
	includedHiddenFiles := make(map[string]bool)
	for _, path := range inputPaths {
		fullPath, err := filepath.Abs(path)
		if err != nil {
			w.Close()
			return nil, errors.New("unable to get current working directory while working with user-watched paths")
		}
		if userPaths[fullPath] || ui.IsIgnored(path) {
			continue
		}
		err = w.Add(fullPath)
		if err != nil {
			w.Close()
			return nil, fmt.Errorf("unable to watch '%s': %s", path, err)
		}
		userPaths[fullPath] = true
	}

	// Create some useful sets from the user-specified paths to be
	// used in smartIgnorer (and, therefore, listenForEvents). One is
	// the set of hidden paths that the user does not want ignored to
	// be used in smartIgnorer. We create this smaller map because the
	// amount of paths the user asked to watch may be large.
	//
	// We also create the sets renameDirs and renameChildren to better
	// handle files that are renamed away and back from the paths the
	// user wanted watched. To be more concrete, folks might want to
	// watch only the normal file "foobar" but their tooling moves
	// foobar away and then back (like vim does on save). This will
	// cause the watch to fire on the first move but then never again,
	// even when its returned. So, we have to track the parent
	// directory of foobar in order to capture when foobar shows up in
	// its parent directory again but we don't want to send all events
	// in that parent directory.
	renameDirs := make(map[string]bool)
	renameChildren := make(map[string]bool)
	for fullPath, _ := range userPaths {
		baseName := filepath.Base(fullPath)
		if strings.HasPrefix(baseName, ".") {
			includedHiddenFiles[fullPath] = true
		}

		dirPath := filepath.Dir(fullPath)
		if !userPaths[dirPath] && dirPath != "" {
			if !renameDirs[dirPath] {
				err = w.Add(dirPath)
				if err != nil {
					w.Close()
					return nil, fmt.Errorf("unable to watch rename-watched-only dir '%s': %s", fullPath, err)
				}
			}
			renameDirs[dirPath] = true
			renameChildren[fullPath] = true
		}
	}
	ig := &smartIgnorer{
		includedHiddenFiles: includedHiddenFiles,
		ui:                  ui,
		renameDirs:          renameDirs,
		renameChildren:      renameChildren,
	}

	go listenForEvents(w, cmdCh, ig)
	return w, nil
}

type event struct {
	time.Time
	Event fsnotify.Event
}

func listenForEvents(w *fsnotify.Watcher, cmdCh chan<- event, ignorer Ignorer) {
	for {
		select {
		case ev, ok := <-w.Events:
			if !ok {
				return
			}
			if ignorer.IsIgnored(ev.Name) {
				continue
			}
			if *verbose {
				log.Printf("unignored file change: %s", ev)
			}
			cmdCh <- event{
				Time:  time.Now(),
				Event: ev,
			}
		case err := <-w.Errors:
			// w.Close causes this.
			if err == nil {
				return
			}
			log.Println("watch error:", err)
		}
	}
}

func createUserIgnorer(ignoredPaths []string) (*userIgnorer, error) {
	ignored := make(map[string]bool)
	ignoredDirs := make([]string, 0)
	for _, in := range ignoredPaths {
		in = strings.TrimSpace(in)
		if len(in) == 0 {
			continue
		}
		path, err := filepath.Abs(in)
		if err != nil {
			return nil, errors.New("unable to get current working dir while working with ignored paths")
		}
		ignored[path] = true
		dirPath := path
		if !strings.HasSuffix(path, "/") {
			dirPath += "/"
		}
		ignoredDirs = append(ignoredDirs, dirPath)
	}
	return &userIgnorer{ignored, ignoredDirs}, nil
}
<|endoftext|>


<commit_before>package hypervisor

import (
	"net"
	"os"
	"sync"
	"syscall"

	"github.com/hyperhq/runv/hypervisor/pod"
)

type VmEvent interface {
	Event() int
}

type VmExit struct{}

type VmStartFailEvent struct {
	Message string
}

type VmKilledEvent struct {
	Success bool
}

type PodFinished struct {
	result []uint32
}

type VmTimeout struct{}

type InitFailedEvent struct {
	Reason string
}

type InitConnectedEvent struct {
	conn *net.UnixConn
}

type GetPodIPCommand struct {
	Id string
}

type GetPodStatsCommand struct {
	Id string
}

type RunPodCommand struct {
	Spec       *pod.UserPod
	Containers []*ContainerInfo
	Volumes    []*VolumeInfo
	Wg         *sync.WaitGroup
}

type ReplacePodCommand RunPodCommand

type PauseCommand struct {
	Pause bool
}

type PauseResult struct {
	Cause string
	Reply *PauseCommand
}

type NewContainerCommand struct {
	container *pod.UserContainer
	info      *ContainerInfo
}

type ExecCommand struct {
	*TtyIO    `json:"-"`
	Sequence  uint64   `json:"seq"`
	Container string   `json:"container,omitempty"`
	Command   []string `json:"cmd"`
}

type KillCommand struct {
	Container string         `json:"container"`
	Signal    syscall.Signal `json:"signal"`
}

type OnlineCpuMemCommand struct{}

type WriteFileCommand struct {
	Container string `json:"container"`
	File      string `json:"file"`
	Data      []byte `json:"-"`
}

type ReadFileCommand struct {
	Container string `json:"container"`
	File      string `json:"file"`
}

type StopPodCommand struct{}
type ShutdownCommand struct {
	Wait bool
}
type ReleaseVMCommand struct{}

type AttachCommand struct {
	Streams   *TtyIO
	Stderr    *TtyIO
	Size      *WindowSize
	Container string
}

type CommandAck struct {
	reply *DecodedMessage
	msg   []byte
}

type CommandError CommandAck

type WindowSizeCommand struct {
	ClientTag string
	Size      *WindowSize
}

type ContainerCreatedEvent struct {
	Index  int
	Id     string
	Rootfs string
	Image  string // if fstype is `dir`, this should be a path relative to share_dir
	// which described the mounted aufs or overlayfs dir.
	Fstype     string
	Workdir    string
	Entrypoint []string
	Cmd        []string
	Envs       map[string]string
}

type ContainerInfo struct {
	Id     string
	Rootfs string
	Image  string // if fstype is `dir`, this should be a path relative to share_dir
	// which described the mounted aufs or overlayfs dir.
	Fstype     string
	Workdir    string
	Entrypoint []string
	Cmd        []string
	Envs       map[string]string
}

type ContainerUnmounted struct {
	Index   int
	Success bool
}

type VolumeReadyEvent struct {
	Name     string //volumen name in spec
	Filepath string //block dev absolute path, or dir path relative to share dir
	Fstype   string //"xfs", "ext4" etc. for block dev, or "dir" for dir path
	Format   string //"raw" (or "qcow2") for volume, no meaning for dir path
}

type VolumeInfo struct {
	Name     string //volumen name in spec
	Filepath string //block dev absolute path, or dir path relative to share dir
	Fstype   string //"xfs", "ext4" etc. for block dev, or "dir" for dir path
	Format   string //"raw" (or "qcow2") for volume, no meaning for dir path
}

type VolumeUnmounted struct {
	Name    string
	Success bool
}

type BlockdevInsertedEvent struct {
	Name       string
	SourceType string //image or volume
	DeviceName string
	ScsiId     int
	ScsiAddr   string // pass scsi addr to hyperstart
}

type DevSkipEvent struct{}

type BlockdevRemovedEvent struct {
	Name    string
	Success bool
}

type InterfaceCreated struct {
	Index      int
	PCIAddr    int
	Fd         *os.File
	Bridge     string
	HostDevice string
	DeviceName string
	MacAddr    string
	IpAddr     string
	NetMask    string
	RouteTable []*RouteRule
}

type InterfaceReleased struct {
	Index   int
	Success bool
}

type RouteRule struct {
	Destination string
	Gateway     string
	ViaThis     bool
}

type NetDevInsertedEvent struct {
	Index      int
	DeviceName string
	Address    int
}

type NetDevRemovedEvent struct {
	Index int
}

type DeviceFailed struct {
	Session VmEvent
}

type Interrupted struct {
	Reason string
}

type GenericOperation struct {
	OpName string
	State  []string
	OpFunc func(ctx *VmContext, result chan<- error)
	Result chan<- error
}

func (qe *VmStartFailEvent) Event() int      { return EVENT_VM_START_FAILED }
func (qe *VmExit) Event() int                { return EVENT_VM_EXIT }
func (qe *VmKilledEvent) Event() int         { return EVENT_VM_KILL }
func (qe *PauseCommand) Event() int          { return COMMAND_PAUSEVM }
func (qe *PauseResult) Event() int           { return EVENT_PAUSE_RESULT }
func (qe *VmTimeout) Event() int             { return EVENT_VM_TIMEOUT }
func (qe *PodFinished) Event() int           { return EVENT_POD_FINISH }
func (qe *InitConnectedEvent) Event() int    { return EVENT_INIT_CONNECTED }
func (qe *ContainerCreatedEvent) Event() int { return EVENT_CONTAINER_ADD }
func (qe *ContainerUnmounted) Event() int    { return EVENT_CONTAINER_DELETE }
func (qe *VolumeUnmounted) Event() int       { return EVENT_BLOCK_EJECTED }
func (qe *VolumeReadyEvent) Event() int      { return EVENT_VOLUME_ADD }
func (qe *BlockdevInsertedEvent) Event() int { return EVENT_BLOCK_INSERTED }
func (qe *DevSkipEvent) Event() int          { return EVENT_DEV_SKIP }
func (qe *BlockdevRemovedEvent) Event() int  { return EVENT_VOLUME_DELETE }
func (qe *InterfaceCreated) Event() int      { return EVENT_INTERFACE_ADD }
func (qe *InterfaceReleased) Event() int     { return EVENT_INTERFACE_DELETE }
func (qe *NetDevInsertedEvent) Event() int   { return EVENT_INTERFACE_INSERTED }
func (qe *NetDevRemovedEvent) Event() int    { return EVENT_INTERFACE_EJECTED }
func (qe *RunPodCommand) Event() int         { return COMMAND_RUN_POD }
func (qe *GetPodIPCommand) Event() int       { return COMMAND_GET_POD_IP }
func (qe *GetPodStatsCommand) Event() int    { return COMMAND_GET_POD_STATS }
func (qe *StopPodCommand) Event() int        { return COMMAND_STOP_POD }
func (qe *ReplacePodCommand) Event() int     { return COMMAND_REPLACE_POD }
func (qe *NewContainerCommand) Event() int   { return COMMAND_NEWCONTAINER }
func (qe *ExecCommand) Event() int           { return COMMAND_EXEC }
func (qe *KillCommand) Event() int           { return COMMAND_KILL }
func (qe *OnlineCpuMemCommand) Event() int   { return COMMAND_ONLINECPUMEM }
func (qe *WriteFileCommand) Event() int      { return COMMAND_WRITEFILE }
func (qe *ReadFileCommand) Event() int       { return COMMAND_READFILE }
func (qe *AttachCommand) Event() int         { return COMMAND_ATTACH }
func (qe *WindowSizeCommand) Event() int     { return COMMAND_WINDOWSIZE }
func (qe *ShutdownCommand) Event() int       { return COMMAND_SHUTDOWN }
func (qe *ReleaseVMCommand) Event() int      { return COMMAND_RELEASE }
func (qe *CommandAck) Event() int            { return COMMAND_ACK }
func (qe *GenericOperation) Event() int      { return GENERIC_OPERATION }
func (qe *InitFailedEvent) Event() int       { return ERROR_INIT_FAIL }
func (qe *DeviceFailed) Event() int          { return ERROR_QMP_FAIL }
func (qe *Interrupted) Event() int           { return ERROR_INTERRUPTED }
func (qe *CommandError) Event() int          { return ERROR_CMD_FAIL }
<commit_msg>container: store mountid to container info<commit_after>package hypervisor

import (
	"net"
	"os"
	"sync"
	"syscall"

	"github.com/hyperhq/runv/hypervisor/pod"
)

type VmEvent interface {
	Event() int
}

type VmExit struct{}

type VmStartFailEvent struct {
	Message string
}

type VmKilledEvent struct {
	Success bool
}

type PodFinished struct {
	result []uint32
}

type VmTimeout struct{}

type InitFailedEvent struct {
	Reason string
}

type InitConnectedEvent struct {
	conn *net.UnixConn
}

type GetPodIPCommand struct {
	Id string
}

type GetPodStatsCommand struct {
	Id string
}

type RunPodCommand struct {
	Spec       *pod.UserPod
	Containers []*ContainerInfo
	Volumes    []*VolumeInfo
	Wg         *sync.WaitGroup
}

type ReplacePodCommand RunPodCommand

type PauseCommand struct {
	Pause bool
}

type PauseResult struct {
	Cause string
	Reply *PauseCommand
}

type NewContainerCommand struct {
	container *pod.UserContainer
	info      *ContainerInfo
}

type ExecCommand struct {
	*TtyIO    `json:"-"`
	Sequence  uint64   `json:"seq"`
	Container string   `json:"container,omitempty"`
	Command   []string `json:"cmd"`
}

type KillCommand struct {
	Container string         `json:"container"`
	Signal    syscall.Signal `json:"signal"`
}

type OnlineCpuMemCommand struct{}

type WriteFileCommand struct {
	Container string `json:"container"`
	File      string `json:"file"`
	Data      []byte `json:"-"`
}

type ReadFileCommand struct {
	Container string `json:"container"`
	File      string `json:"file"`
}

type StopPodCommand struct{}
type ShutdownCommand struct {
	Wait bool
}
type ReleaseVMCommand struct{}

type AttachCommand struct {
	Streams   *TtyIO
	Stderr    *TtyIO
	Size      *WindowSize
	Container string
}

type CommandAck struct {
	reply *DecodedMessage
	msg   []byte
}

type CommandError CommandAck

type WindowSizeCommand struct {
	ClientTag string
	Size      *WindowSize
}

type ContainerCreatedEvent struct {
	Index  int
	Id     string
	Rootfs string
	Image  string // if fstype is `dir`, this should be a path relative to share_dir
	// which described the mounted aufs or overlayfs dir.
	Fstype     string
	Workdir    string
	Entrypoint []string
	Cmd        []string
	Envs       map[string]string
}

type ContainerInfo struct {
	Id      string
	MountId string
	Rootfs  string
	Image   string // if fstype is `dir`, this should be a path relative to share_dir
	// which described the mounted aufs or overlayfs dir.
	Fstype     string
	Workdir    string
	Entrypoint []string
	Cmd        []string
	Envs       map[string]string
}

type ContainerUnmounted struct {
	Index   int
	Success bool
}

type VolumeReadyEvent struct {
	Name     string //volumen name in spec
	Filepath string //block dev absolute path, or dir path relative to share dir
	Fstype   string //"xfs", "ext4" etc. for block dev, or "dir" for dir path
	Format   string //"raw" (or "qcow2") for volume, no meaning for dir path
}

type VolumeInfo struct {
	Name     string //volumen name in spec
	Filepath string //block dev absolute path, or dir path relative to share dir
	Fstype   string //"xfs", "ext4" etc. for block dev, or "dir" for dir path
	Format   string //"raw" (or "qcow2") for volume, no meaning for dir path
}

type VolumeUnmounted struct {
	Name    string
	Success bool
}

type BlockdevInsertedEvent struct {
	Name       string
	SourceType string //image or volume
	DeviceName string
	ScsiId     int
	ScsiAddr   string // pass scsi addr to hyperstart
}

type DevSkipEvent struct{}

type BlockdevRemovedEvent struct {
	Name    string
	Success bool
}

type InterfaceCreated struct {
	Index      int
	PCIAddr    int
	Fd         *os.File
	Bridge     string
	HostDevice string
	DeviceName string
	MacAddr    string
	IpAddr     string
	NetMask    string
	RouteTable []*RouteRule
}

type InterfaceReleased struct {
	Index   int
	Success bool
}

type RouteRule struct {
	Destination string
	Gateway     string
	ViaThis     bool
}

type NetDevInsertedEvent struct {
	Index      int
	DeviceName string
	Address    int
}

type NetDevRemovedEvent struct {
	Index int
}

type DeviceFailed struct {
	Session VmEvent
}

type Interrupted struct {
	Reason string
}

type GenericOperation struct {
	OpName string
	State  []string
	OpFunc func(ctx *VmContext, result chan<- error)
	Result chan<- error
}

func (qe *VmStartFailEvent) Event() int      { return EVENT_VM_START_FAILED }
func (qe *VmExit) Event() int                { return EVENT_VM_EXIT }
func (qe *VmKilledEvent) Event() int         { return EVENT_VM_KILL }
func (qe *PauseCommand) Event() int          { return COMMAND_PAUSEVM }
func (qe *PauseResult) Event() int           { return EVENT_PAUSE_RESULT }
func (qe *VmTimeout) Event() int             { return EVENT_VM_TIMEOUT }
func (qe *PodFinished) Event() int           { return EVENT_POD_FINISH }
func (qe *InitConnectedEvent) Event() int    { return EVENT_INIT_CONNECTED }
func (qe *ContainerCreatedEvent) Event() int { return EVENT_CONTAINER_ADD }
func (qe *ContainerUnmounted) Event() int    { return EVENT_CONTAINER_DELETE }
func (qe *VolumeUnmounted) Event() int       { return EVENT_BLOCK_EJECTED }
func (qe *VolumeReadyEvent) Event() int      { return EVENT_VOLUME_ADD }
func (qe *BlockdevInsertedEvent) Event() int { return EVENT_BLOCK_INSERTED }
func (qe *DevSkipEvent) Event() int          { return EVENT_DEV_SKIP }
func (qe *BlockdevRemovedEvent) Event() int  { return EVENT_VOLUME_DELETE }
func (qe *InterfaceCreated) Event() int      { return EVENT_INTERFACE_ADD }
func (qe *InterfaceReleased) Event() int     { return EVENT_INTERFACE_DELETE }
func (qe *NetDevInsertedEvent) Event() int   { return EVENT_INTERFACE_INSERTED }
func (qe *NetDevRemovedEvent) Event() int    { return EVENT_INTERFACE_EJECTED }
func (qe *RunPodCommand) Event() int         { return COMMAND_RUN_POD }
func (qe *GetPodIPCommand) Event() int       { return COMMAND_GET_POD_IP }
func (qe *GetPodStatsCommand) Event() int    { return COMMAND_GET_POD_STATS }
func (qe *StopPodCommand) Event() int        { return COMMAND_STOP_POD }
func (qe *ReplacePodCommand) Event() int     { return COMMAND_REPLACE_POD }
func (qe *NewContainerCommand) Event() int   { return COMMAND_NEWCONTAINER }
func (qe *ExecCommand) Event() int           { return COMMAND_EXEC }
func (qe *KillCommand) Event() int           { return COMMAND_KILL }
func (qe *OnlineCpuMemCommand) Event() int   { return COMMAND_ONLINECPUMEM }
func (qe *WriteFileCommand) Event() int      { return COMMAND_WRITEFILE }
func (qe *ReadFileCommand) Event() int       { return COMMAND_READFILE }
func (qe *AttachCommand) Event() int         { return COMMAND_ATTACH }
func (qe *WindowSizeCommand) Event() int     { return COMMAND_WINDOWSIZE }
func (qe *ShutdownCommand) Event() int       { return COMMAND_SHUTDOWN }
func (qe *ReleaseVMCommand) Event() int      { return COMMAND_RELEASE }
func (qe *CommandAck) Event() int            { return COMMAND_ACK }
func (qe *GenericOperation) Event() int      { return GENERIC_OPERATION }
func (qe *InitFailedEvent) Event() int       { return ERROR_INIT_FAIL }
func (qe *DeviceFailed) Event() int          { return ERROR_QMP_FAIL }
func (qe *Interrupted) Event() int           { return ERROR_INTERRUPTED }
func (qe *CommandError) Event() int          { return ERROR_CMD_FAIL }
<|endoftext|>


<commit_before>// +build OMIT

package main

import "fmt"

func f(left, right chan int) {
	left <- 1 + <-right
}

func main() {
	const n = 100000
	leftmost := make(chan int)
	right := leftmost
	left := leftmost
	for i := 0; i < n; i++ {
		right = make(chan int)
		go f(left, right)
		left = right
	}
	go func(c chan int) { c <- 1 }(right)
	fmt.Println(<-leftmost)
}
<commit_msg>go.talks/2013/concurrency: decrease length of the daisy-chain<commit_after>// +build OMIT

package main

import "fmt"

func f(left, right chan int) {
	left <- 1 + <-right
}

func main() {
	const n = 10000
	leftmost := make(chan int)
	right := leftmost
	left := leftmost
	for i := 0; i < n; i++ {
		right = make(chan int)
		go f(left, right)
		left = right
	}
	go func(c chan int) { c <- 1 }(right)
	fmt.Println(<-leftmost)
}
<|endoftext|>


<commit_before>package tsz

import (
	"math/rand"
	"testing"
)

var T uint32
var V float64

func BenchmarkPushSeries4h(b *testing.B) {
	s := NewSeries4h(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		if i%10 == 0 {
			s.Push(i, 0)
		} else if i%10 == 1 {
			s.Push(i, 1)
		} else {
			s.Push(i, float64(i)+123.45)
		}
	}
	s.Finish()
	b.Logf("Series4h size: %dB", len(s.Bytes()))
}

func benchmarkSeriesLong(b *testing.B, generator func(uint32) float64) {
	s := NewSeriesLong(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, generator(i))
	}
	s.Finish()
	b.Logf("SeriesLong size: %d points in %dB, avg %.2f bytes/point", N, len(s.Bytes()), float64(len(s.Bytes()))/float64(N))
}

func BenchmarkPushSeriesLongMonotonicIncreaseWithResets(b *testing.B) {
	benchmarkSeriesLong(b, func(i uint32) float64 {
		if i%10 == 0 {
			return 0
		} else if i%10 == 1 {
			return 1
		}
		return float64(i) + 123.45
	})
}

func BenchmarkPushSeriesLongMonotonicIncrease(b *testing.B) {
	benchmarkSeriesLong(b, func(i uint32) float64 {
		return float64(i) + 123.45
	})
}

func BenchmarkPushSeriesLongSawtooth(b *testing.B) {
	benchmarkSeriesLong(b, func(i uint32) float64 {
		multiplier := 1.0
		if i%2 == 0 {
			multiplier = -1.0
		}
		return multiplier*123.45 + float64(i)/1000
	})
}

func BenchmarkPushSeriesLongSawtoothWithFlats(b *testing.B) {
	benchmarkSeriesLong(b, func(i uint32) float64 {
		multiplier := 1.0
		if i%2 == 0 && i%100 != 0 {
			multiplier = -1.0
		}
		return multiplier*123.45 + float64(i)/1000
	})
}

func BenchmarkPushSeriesLongSteps(b *testing.B) {
	benchmarkSeriesLong(b, func(i uint32) float64 {
		multiplier := 1.0
		if (i/100)%2 == 0 {
			multiplier = -1.0
		}
		return multiplier*123.45 + float64(i)/1000
	})
}

func BenchmarkPushSeriesLongRealWorldCPU(b *testing.B) {
	values := []float64{95.3, 95.7, 86.2, 95.0, 94.7, 95.4, 94.5, 94.0, 94.7, 95.0, 95.0, 93.8, 95.3, 95.4, 94.6, 83.8, 94.5, 94.5, 94.6, 92.0, 95.0, 89.6, 72.8, 72.1, 86.5, 94.9, 94.9, 93.9, 94.4, 95.4, 95.1, 93.7, 95.5, 95.4, 94.4, 93.2, 94.6, 95.5, 94.9, 94.1, 95.0, 95.5, 94.7, 93.7, 95.1, 96.6, 95.3, 94.0, 95.0, 95.2, 93.3, 94.2, 95.2, 94.9, 94.5, 95.3, 93.2, 95.4, 95.0, 95.2, 93.7}
	benchmarkSeriesLong(b, func(i uint32) float64 {
		return values[int(i)%len(values)]
	})
}

func BenchmarkIterSeries4h(b *testing.B) {
	s := NewSeries4h(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	iter := s.Iter(1)
	var t uint32
	var v float64
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}

func BenchmarkIterSeriesLong(b *testing.B) {
	s := NewSeriesLong(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	iter := s.Iter()
	var t uint32
	var v float64
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}

func BenchmarkIterSeriesLongInterface(b *testing.B) {
	s := NewSeriesLong(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	var t uint32
	var v float64
	var iter Iter
	// avoid compiler optimization where it can statically assign the right type
	// and skip the overhead of the interface
	if rand.Intn(1) == 0 {
		iter = s.Iter()
	}
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}
<commit_msg>divorce b.N from number of points in chunk<commit_after>package tsz

import (
	"math/rand"
	"strconv"
	"testing"
)

var T uint32
var V float64

func BenchmarkPushSeries4h(b *testing.B) {
	s := NewSeries4h(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		if i%10 == 0 {
			s.Push(i, 0)
		} else if i%10 == 1 {
			s.Push(i, 1)
		} else {
			s.Push(i, float64(i)+123.45)
		}
	}
	s.Finish()
	b.Logf("Series4h size: %dB", len(s.Bytes()))
}

var benchmarkSeriesLongChunkSizes = []int{1, 10, 30, 60, 120, 180, 240}

func benchmarkSeriesLong(num int, generator func(uint32) float64) func(b *testing.B) {
	return func(b *testing.B) {
		var s *SeriesLong
		for j := 0; j < b.N; j++ {
			s = NewSeriesLong(0)
			N := uint32(num)
			for i := uint32(1); i <= N; i++ {
				s.Push(i, generator(i))
			}
			s.Finish()
		}
		b.Logf("SeriesLong size: %d points in %dB, avg %.2f bytes/point, b.n %d", num, len(s.Bytes()), float64(len(s.Bytes()))/float64(num), b.N)
	}
}

func BenchmarkPushSeriesLongMonotonicIncreaseWithResets(b *testing.B) {
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			if i%10 == 0 {
				return 0
			} else if i%10 == 1 {
				return 1
			}
			return float64(i) + 123.45
		}))
	}
}

func BenchmarkPushSeriesLongMonotonicIncrease(b *testing.B) {
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			return float64(i) + 123.45
		}))
	}
}

func BenchmarkPushSeriesLongSawtooth(b *testing.B) {
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			multiplier := 1.0
			if i%2 == 0 {
				multiplier = -1.0
			}
			return multiplier*123.45 + float64(i)/1000
		}))
	}
}

func BenchmarkPushSeriesLongSawtoothWithFlats(b *testing.B) {
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			multiplier := 1.0
			if i%2 == 0 && i%100 != 0 {
				multiplier = -1.0
			}
			return multiplier*123.45 + float64(i)/1000
		}))
	}
}

func BenchmarkPushSeriesLongSteps(b *testing.B) {
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			multiplier := 1.0
			if (i/100)%2 == 0 {
				multiplier = -1.0
			}
			return multiplier*123.45 + float64(i)/1000
		}))
	}
}

func BenchmarkPushSeriesLongRealWorldCPU(b *testing.B) {
	values := []float64{95.3, 95.7, 86.2, 95.0, 94.7, 95.4, 94.5, 94.0, 94.7, 95.0, 95.0, 93.8, 95.3, 95.4, 94.6, 83.8, 94.5, 94.5, 94.6, 92.0, 95.0, 89.6, 72.8, 72.1, 86.5, 94.9, 94.9, 93.9, 94.4, 95.4, 95.1, 93.7, 95.5, 95.4, 94.4, 93.2, 94.6, 95.5, 94.9, 94.1, 95.0, 95.5, 94.7, 93.7, 95.1, 96.6, 95.3, 94.0, 95.0, 95.2, 93.3, 94.2, 95.2, 94.9, 94.5, 95.3, 93.2, 95.4, 95.0, 95.2, 93.7}
	for _, num := range benchmarkSeriesLongChunkSizes {
		b.Run(strconv.Itoa(num), benchmarkSeriesLong(num, func(i uint32) float64 {
			return values[int(i)%len(values)]
		}))
	}
}

func BenchmarkIterSeries4h(b *testing.B) {
	s := NewSeries4h(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	iter := s.Iter(1)
	var t uint32
	var v float64
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}

func BenchmarkIterSeriesLong(b *testing.B) {
	s := NewSeriesLong(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	iter := s.Iter()
	var t uint32
	var v float64
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}

func BenchmarkIterSeriesLongInterface(b *testing.B) {
	s := NewSeriesLong(0)
	N := uint32(b.N)
	for i := uint32(1); i <= N; i++ {
		s.Push(i, 123.45)
	}
	b.ResetTimer()
	var t uint32
	var v float64
	var iter Iter
	// avoid compiler optimization where it can statically assign the right type
	// and skip the overhead of the interface
	if rand.Intn(1) == 0 {
		iter = s.Iter()
	}
	for iter.Next() {
		t, v = iter.Values()
	}
	err := iter.Err()
	if err != nil {
		panic(err)
	}
	T = t
	V = v
}
<|endoftext|>


<commit_before>package cfg

import (
  "encoding/json"
  "io/ioutil"
  "log"
)

// Configuration is a Configuration object.
type Configuration struct {
  LogLevel string
  UploadPath string
}

// Monitoring monitoring
type Monitoring struct {
  Enable string `json:"enabled"`
}

// EC2Config EC2 configuration
type EC2Config struct {
  DryRun string `json:"dry_run"`
  ImageID string `json:"image_id"`
  KeyName string `json:"key_name"`
  MinCount int `json:"min_count"`
  MaxCount int `json:"max_count"`
  InstanceType string `json:"instance_type"`
  Monitoring Monitoring `json:"monitoring"`
}

// IPRange ip range
type IPRange struct {
  // CidrIP cidr_ip
  CidrIP string `json:"cidr_ip"`
}

// IPPermission ip permission
type IPPermission struct {
  IPProtocol string `json:"ip_protocol"`
  FromPort string `json:"from_port"`
  ToPort string `json:"to_port"`
  IPRanges []IPRange `json:"ip_ranges"`
}

// SecurityGroup Security Group
type SecurityGroup struct {
  IPPermissions []IPPermission `json:"ip_permission"`
}

// LoadEC2Configuration Loads the configuration file.
func LoadEC2Configuration() {
  dat, err := ioutil.ReadFile("ec2_conf.json")
  if err != nil {
    panic(err)
  }
  ec2Config := EC2Config{}
  err = json.Unmarshal(dat, &ec2Config)
  if err != nil {
    panic(err)
  }
  log.Println(ec2Config)
}
<commit_msg>Parsing json file.<commit_after>package cfg

import (
  "encoding/json"
  "io/ioutil"
  "log"
)

// Configuration is a Configuration object.
type Configuration struct {
  LogLevel string
  UploadPath string
}

// Monitoring monitoring
type Monitoring struct {
  Enable bool `json:"enabled"`
}

// EC2Config EC2 configuration
type EC2Config struct {
  DryRun bool `json:"dry_run"`
  ImageID string `json:"image_id"`
  KeyName string `json:"key_name"`
  MinCount int `json:"min_count"`
  MaxCount int `json:"max_count"`
  InstanceType string `json:"instance_type"`
  Monitoring Monitoring `json:"monitoring"`
}

// IPRange ip range
type IPRange struct {
  // CidrIP cidr_ip
  CidrIP string `json:"cidr_ip"`
}

// IPPermission ip permission
type IPPermission struct {
  IPProtocol string `json:"ip_protocol"`
  FromPort string `json:"from_port"`
  ToPort string `json:"to_port"`
  IPRanges []IPRange `json:"ip_ranges"`
}

// SecurityGroup Security Group
type SecurityGroup struct {
  IPPermissions []IPPermission `json:"ip_permission"`
}

// LoadEC2Configuration Loads the configuration file.
func LoadEC2Configuration() {
  dat, err := ioutil.ReadFile("cfg/ec2_conf.json")
  if err != nil {
    panic(err)
  }
  ec2Config := EC2Config{}
  err = json.Unmarshal(dat, &ec2Config)
  if err != nil {
    panic(err)
  }
  log.Println(ec2Config)
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/openzipkin/zipkin-go-opentracing"
	"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore"
	"github.com/prometheus/client_golang/prometheus"
	"strings"
	"time"
)

// PrometheusCollector is a custom Collector
// which sends ZipKin traces to Prometheus
type PrometheusCollector struct {

	// Each span name is published as a separate Histogram metric
	// Using metric names of the form fn_span_<span-name>_duration_seconds
	histogramVecMap map[string]*prometheus.HistogramVec
}

// NewPrometheusCollector returns a new PrometheusCollector
func NewPrometheusCollector() (zipkintracer.Collector, error) {
	pc := &PrometheusCollector{make(map[string]*prometheus.HistogramVec)}
	return pc, nil
}

// Return the HistogramVec corresponding to the specified spanName.
// If a HistogramVec does not already exist for specified spanName then one is created and configured with the specified labels
// otherwise the labels parameter is ignored.
func (pc PrometheusCollector) getHistogramVecForSpanName(spanName string, labels []string) *prometheus.HistogramVec {
	thisHistogramVec, found := pc.histogramVecMap[spanName]
	if !found {
		thisHistogramVec = prometheus.NewHistogramVec(
			prometheus.HistogramOpts{
				Name: "fn_span_" + spanName + "_duration_seconds",
				Help: "Span " + spanName + " duration, by span name",
			},
			labels,
		)
		pc.histogramVecMap[spanName] = thisHistogramVec
		prometheus.MustRegister(thisHistogramVec)
	}
	return thisHistogramVec
}

// PrometheusCollector implements Collector.
func (pc PrometheusCollector) Collect(span *zipkincore.Span) error {

	// extract any label values from the span
	labelKeys, labelValueMap := getLabels(span)

	pc.getHistogramVecForSpanName(span.GetName(), labelKeys).With(labelValueMap).Observe((time.Duration(span.GetDuration()) * time.Microsecond).Seconds())
	return nil
}

// extract from the specified span the key/value pairs that we want to add as labels to the Prometheus metric for this span
// returns an array of keys, and a map of key-value pairs
func getLabels(span *zipkincore.Span) ([]string, map[string]string) {

	var keys []string
	labelMap := make(map[string]string)

	// extract any tags whose key starts with "fn" from the span
	binaryAnnotations := span.GetBinaryAnnotations()
	for _, thisBinaryAnnotation := range binaryAnnotations {
		key := thisBinaryAnnotation.GetKey()
		if thisBinaryAnnotation.GetAnnotationType() == zipkincore.AnnotationType_STRING && strings.HasPrefix(key, "fn") {
			keys = append(keys, key)
			value := string(thisBinaryAnnotation.GetValue()[:])
			labelMap[key] = value
		}
	}

	return keys, labelMap
}

// PrometheusCollector implements Collector.
func (PrometheusCollector) Close() error { return nil }
<commit_msg>Add protection against inconsistent cardinaliity errors<commit_after>package server

import (
	"github.com/openzipkin/zipkin-go-opentracing"
	"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore"
	"github.com/prometheus/client_golang/prometheus"
	"strings"
	"time"
)

// PrometheusCollector is a custom Collector
// which sends ZipKin traces to Prometheus
type PrometheusCollector struct {

	// Each span name is published as a separate Histogram metric
	// Using metric names of the form fn_span_<span-name>_duration_seconds

	// In this map, the key is the name of a tracing span,
	// and the corresponding value is a HistogramVec metric used to report the duration of spans with this name to Prometheus
	histogramVecMap map[string]*prometheus.HistogramVec

	// In this map, the key is the name of a tracing span,
	// and the corresponding value is an array containing the label keys that were specified when the HistogramVec metric was created
	registeredLabelKeysMap map[string][]string
}

// NewPrometheusCollector returns a new PrometheusCollector
func NewPrometheusCollector() (zipkintracer.Collector, error) {
	pc := &PrometheusCollector{make(map[string]*prometheus.HistogramVec), make(map[string][]string)}
	return pc, nil
}

// PrometheusCollector implements Collector.
func (pc PrometheusCollector) Collect(span *zipkincore.Span) error {
	var labelValuesToUse map[string]string

	// extract any label values from the span
	labelKeysFromSpan, labelValuesFromSpan := getLabels(span)

	// get the HistogramVec for this span name
	histogramVec, found := pc.histogramVecMap[span.GetName()]
	if !found {
		// create a new HistogramVec
		histogramVec = prometheus.NewHistogramVec(
			prometheus.HistogramOpts{
				Name: "fn_span_" + span.GetName() + "_duration_seconds",
				Help: "Span " + span.GetName() + " duration, by span name",
			},
			labelKeysFromSpan,
		)
		pc.histogramVecMap[span.GetName()] = histogramVec
		pc.registeredLabelKeysMap[span.GetName()] = labelKeysFromSpan
		prometheus.MustRegister(histogramVec)
		labelValuesToUse = labelValuesFromSpan
	} else {
		// found an existing HistogramVec
		// need to be careful here, since we must supply the same label keys as when we first created the metric
		// otherwise we will get a "inconsistent label cardinality" panic
		// that's why we saved the original label keys in the registeredLabelKeysMap map
		// so we can use that to construct a map of label key/value pairs to set on the metric
		labelValuesToUse = make(map[string]string)
		for _, thisRegisteredLabelKey := range pc.registeredLabelKeysMap[span.GetName()] {
			if value, found := labelValuesFromSpan[thisRegisteredLabelKey]; found {
				labelValuesToUse[thisRegisteredLabelKey] = value
			} else {
				labelValuesToUse[thisRegisteredLabelKey] = ""
			}
		}
	}

	// now report the metric value
	histogramVec.With(labelValuesToUse).Observe((time.Duration(span.GetDuration()) * time.Microsecond).Seconds())

	return nil
}

// extract from the specified span the key/value pairs that we want to add as labels to the Prometheus metric for this span
// returns an array of keys, and a map of key-value pairs
func getLabels(span *zipkincore.Span) ([]string, map[string]string) {

	var keys []string
	labelMap := make(map[string]string)

	// extract any tags whose key starts with "fn" from the span
	binaryAnnotations := span.GetBinaryAnnotations()
	for _, thisBinaryAnnotation := range binaryAnnotations {
		key := thisBinaryAnnotation.GetKey()
		if thisBinaryAnnotation.GetAnnotationType() == zipkincore.AnnotationType_STRING && strings.HasPrefix(key, "fn") {
			keys = append(keys, key)
			value := string(thisBinaryAnnotation.GetValue()[:])
			labelMap[key] = value
		}
	}

	return keys, labelMap
}

// PrometheusCollector implements Collector.
func (PrometheusCollector) Close() error { return nil }
<|endoftext|>


<commit_before>// Copyright 2011 Julian Phillips.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// #include "utils.h"
// static inline void decref(PyObject *obj) { Py_DECREF(obj); }
import "C"

import (
	"unsafe"
)

type Op int

const (
	LT = Op(C.Py_LT)
	LE = Op(C.Py_LE)
	EQ = Op(C.Py_EQ)
	NE = Op(C.Py_NE)
	GT = Op(C.Py_GT)
	GE = Op(C.Py_GE)
)

type Object interface {
	Base() *BaseObject
	Type() *Type
	Decref()
	Incref()
	IsTrue() bool
	Not() bool
	Free()
}

var None = (*NoneObject)(unsafe.Pointer(&C._Py_NoneStruct))

type NoneObject struct {
	AbstractObject
}

func (n *NoneObject) String() string {
	return "None"
}

func c(obj Object) *C.PyObject {
	if obj == nil {
		return nil
	}
	return (*C.PyObject)(unsafe.Pointer(obj.Base()))
}

var types = make(map[*C.PyTypeObject]*Class)

func registerType(pyType *C.PyTypeObject, class *Class) {
	types[pyType] = class
}

func newObject(obj *C.PyObject) Object {
	if obj == nil {
		return nil
	}
	o := unsafe.Pointer(obj)
	if o == unsafe.Pointer(None) {
		return None
	}
	pyType := (*C.PyTypeObject)(obj.ob_type)
	class, ok := types[pyType]
	if ok {
		t := unsafe.Typeof(class.Pointer)
		ret, ok := unsafe.Unreflect(t, unsafe.Pointer(&obj)).(Object)
		if ok {
			return ret
		}
	}
	switch C.getBasePyType(obj) {
	case &C.PyList_Type:
		return (*List)(o)
	case &C.PyTuple_Type:
		return (*Tuple)(o)
	case &C.PyDict_Type:
		return (*Dict)(o)
	case &C.PyString_Type:
		return (*String)(o)
	case &C.PyBool_Type:
		return newBool(obj)
	case &C.PyInt_Type:
		return (*Int)(o)
	case &C.PyLong_Type:
		return (*Long)(o)
	case &C.PyFloat_Type:
		return (*Float)(o)
	case &C.PyModule_Type:
		return (*Module)(o)
	case &C.PyType_Type:
		return (*Type)(o)
	case &C.PyCode_Type:
		return (*Code)(o)
	case &C.PyCFunction_Type:
		return (*CFunction)(o)
	}
	for pyType.tp_base != nil {
		pyType = (*C.PyTypeObject)(unsafe.Pointer(pyType.tp_base))
		class, ok := types[pyType]
		if ok {
			t := unsafe.Typeof(class.Pointer)
			ret, ok := unsafe.Unreflect(t, unsafe.Pointer(&obj)).(Object)
			if ok {
				return ret
			}
		}
	}
	return newBaseObject(obj)
}
<commit_msg>Check base object before standard types<commit_after>// Copyright 2011 Julian Phillips.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// #include "utils.h"
// static inline void decref(PyObject *obj) { Py_DECREF(obj); }
import "C"

import (
	"unsafe"
)

type Op int

const (
	LT = Op(C.Py_LT)
	LE = Op(C.Py_LE)
	EQ = Op(C.Py_EQ)
	NE = Op(C.Py_NE)
	GT = Op(C.Py_GT)
	GE = Op(C.Py_GE)
)

type Object interface {
	Base() *BaseObject
	Type() *Type
	Decref()
	Incref()
	IsTrue() bool
	Not() bool
	Free()
}

var None = (*NoneObject)(unsafe.Pointer(&C._Py_NoneStruct))

type NoneObject struct {
	AbstractObject
}

func (n *NoneObject) String() string {
	return "None"
}

func c(obj Object) *C.PyObject {
	if obj == nil {
		return nil
	}
	return (*C.PyObject)(unsafe.Pointer(obj.Base()))
}

var types = make(map[*C.PyTypeObject]*Class)

func registerType(pyType *C.PyTypeObject, class *Class) {
	types[pyType] = class
}

func newObject(obj *C.PyObject) Object {
	if obj == nil {
		return nil
	}
	o := unsafe.Pointer(obj)
	if o == unsafe.Pointer(None) {
		return None
	}
	pyType := (*C.PyTypeObject)(obj.ob_type)
	class, ok := types[pyType]
	if ok {
		t := unsafe.Typeof(class.Pointer)
		ret, ok := unsafe.Unreflect(t, unsafe.Pointer(&obj)).(Object)
		if ok {
			return ret
		}
	}
	for pyType.tp_base != nil {
		pyType = (*C.PyTypeObject)(unsafe.Pointer(pyType.tp_base))
		class, ok := types[pyType]
		if ok {
			t := unsafe.Typeof(class.Pointer)
			ret, ok := unsafe.Unreflect(t, unsafe.Pointer(&obj)).(Object)
			if ok {
				return ret
			}
		}
	}
	switch C.getBasePyType(obj) {
	case &C.PyList_Type:
		return (*List)(o)
	case &C.PyTuple_Type:
		return (*Tuple)(o)
	case &C.PyDict_Type:
		return (*Dict)(o)
	case &C.PyString_Type:
		return (*String)(o)
	case &C.PyBool_Type:
		return newBool(obj)
	case &C.PyInt_Type:
		return (*Int)(o)
	case &C.PyLong_Type:
		return (*Long)(o)
	case &C.PyFloat_Type:
		return (*Float)(o)
	case &C.PyModule_Type:
		return (*Module)(o)
	case &C.PyType_Type:
		return (*Type)(o)
	case &C.PyCode_Type:
		return (*Code)(o)
	case &C.PyCFunction_Type:
		return (*CFunction)(o)
	}
	return newBaseObject(obj)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sync"
	"syscall"
	"time"

	"golang.org/x/sys/windows/svc"
	"golang.org/x/sys/windows/svc/eventlog"
)

const name = "mackerel-agent"

const defaultEid = 1
const startEid = 2
const stopEid = 3
const loggerEid = 4

var (
	kernel32                     = syscall.NewLazyDLL("kernel32")
	procAllocConsole             = kernel32.NewProc("AllocConsole")
	procGenerateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent")
)

func main() {
	if len(os.Args) == 2 {
		var err error
		switch os.Args[1] {
		case "install":
			err = installService("mackerel-agent", "mackerel agent")
		case "remove":
			err = removeService("mackerel-agent")
		}
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	elog, err := eventlog.Open(name)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer elog.Close()

	// `svc.Run` blocks until windows service will stopped.
	// ref. https://msdn.microsoft.com/library/cc429362.aspx
	err = svc.Run(name, &handler{elog: elog})
	if err != nil {
		log.Fatal(err.Error())
	}
}

type logger interface {
	Info(eid uint32, msg string) error
	Warning(eid uint32, msg string) error
	Error(eid uint32, msg string) error
}

type handler struct {
	elog logger
	cmd  *exec.Cmd
	r    io.Reader
	w    io.WriteCloser
	wg   sync.WaitGroup
}

// ex.
// verbose log: 2017/01/21 22:21:08 command.go:434: DEBUG <command> received 'immediate' chan
// normal log:  2017/01/24 14:14:27 INFO <main> Starting mackerel-agent version:0.36.0
var logRe = regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} (?:\S+\.go:\d+: )?([A-Z]+) `)

func (h *handler) retire() error {
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"), "retire", "--force")
	cmd.Dir = dir
	return cmd.Run()
}

func (h *handler) start() error {
	procAllocConsole.Call()
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"))
	cmd.SysProcAttr = &syscall.SysProcAttr{
		CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
	}
	cmd.Dir = dir

	h.cmd = cmd
	h.r, h.w = io.Pipe()
	cmd.Stderr = h.w

	err := h.cmd.Start()
	if err != nil {
		return err
	}

	return h.aggregate()
}

func (h *handler) aggregate() error {
	br := bufio.NewReader(h.r)
	lc := make(chan string, 10)
	done := make(chan struct{})

	// It need to read data from pipe continuously. And it need to close handle
	// when process finished.
	// read data from pipe. When data arrived at EOL, send line-string to the
	// channel. Also remaining line to EOF.
	h.wg.Add(1)
	go func() {
		defer h.wg.Done()
		defer h.w.Close()

		// pipe stderr to windows event log
		var body bytes.Buffer
		for {
			b, err := br.ReadByte()
			if err != nil {
				if err != io.EOF {
					h.elog.Error(loggerEid, err.Error())
				}
				break
			}
			if b == '\n' {
				if body.Len() > 0 {
					lc <- body.String()
					body.Reset()
				}
				continue
			}
			body.WriteByte(b)
		}
		if body.Len() > 0 {
			lc <- body.String()
		}
		done <- struct{}{}
	}()

	h.wg.Add(1)
	go func() {
		defer h.wg.Done()

		linebuf := []string{}
	loop:
		for {
			select {
			case line := <-lc:
				if len(linebuf) == 0 || logRe.MatchString(line) {
					linebuf = append(linebuf, line)
				} else {
					linebuf[len(linebuf)-1] += "\n" + line
				}
			case <-time.After(10 * time.Millisecond):
				// When it take 10ms, it is located at end of paragraph. Then
				// slice appended at above should be the paragraph.
				for _, line := range linebuf {
					if match := logRe.FindStringSubmatch(line); match != nil {
						level := match[1]
						switch level {
						case "TRACE", "DEBUG", "INFO":
							h.elog.Info(defaultEid, line)
						case "WARNING", "ERROR":
							h.elog.Warning(defaultEid, line)
						case "CRITICAL":
							h.elog.Error(defaultEid, line)
						default:
							h.elog.Error(defaultEid, line)
						}
					} else {
						h.elog.Error(defaultEid, line)
					}
				}
				select {
				case <-done:
					break loop
				default:
				}
				linebuf = nil
			}
		}
		close(lc)
		close(done)
	}()

	return nil
}

func interrupt(p *os.Process) error {
	r1, _, err := procGenerateConsoleCtrlEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(p.Pid))
	if r1 == 0 {
		return err
	}
	return nil
}

func (h *handler) stop() error {
	if h.cmd != nil && h.cmd.Process != nil {
		err := interrupt(h.cmd.Process)
		if err == nil {
			end := time.Now().Add(10 * time.Second)
			for time.Now().Before(end) {
				if h.cmd.ProcessState != nil && h.cmd.ProcessState.Exited() {
					return nil
				}
				time.Sleep(1 * time.Second)
			}
		}
		return h.cmd.Process.Kill()
	}

	h.wg.Wait()
	return nil
}

func autoRetire() bool {
	env := os.Getenv("MACKEREL_AUTO_RETIREMENT")
	return env != "" && env != "0"
}

// implement https://godoc.org/golang.org/x/sys/windows/svc#Handler
func (h *handler) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
	s <- svc.Status{State: svc.StartPending}
	defer func() {
		s <- svc.Status{State: svc.Stopped}
	}()

	if err := h.start(); err != nil {
		h.elog.Error(startEid, err.Error())
		// https://msdn.microsoft.com/library/windows/desktop/ms681383(v=vs.85).aspx
		// use ERROR_SERVICE_SPECIFIC_ERROR
		return true, 1
	}

	exit := make(chan struct{})
	go func() {
		err := h.cmd.Wait()
		// enter when the child process exited
		if err != nil {
			h.elog.Error(stopEid, err.Error())
		}
		exit <- struct{}{}
	}()

	s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
L:
	for {
		select {
		case req := <-r:
			switch req.Cmd {
			case svc.Interrogate:
				s <- req.CurrentStatus
			case svc.Stop, svc.Shutdown:
				s <- svc.Status{State: svc.StopPending, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				if err := h.stop(); err != nil {
					h.elog.Error(stopEid, err.Error())
					s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				} else {
					if req.Cmd == svc.Shutdown && autoRetire() {
						if err := h.retire(); err != nil {
							h.elog.Error(stopEid, err.Error())
							s <- svc.Status{State: svc.Running, Accepts: svc.AcceptShutdown | svc.AcceptShutdown}
						}
					}
				}
			}
		case <-exit:
			break L
		}
	}

	return
}

func execdir() string {
	p, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	return filepath.Dir(p)
}
<commit_msg>Remove duplicate<commit_after>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sync"
	"syscall"
	"time"

	"golang.org/x/sys/windows/svc"
	"golang.org/x/sys/windows/svc/eventlog"
)

const name = "mackerel-agent"

const defaultEid = 1
const startEid = 2
const stopEid = 3
const loggerEid = 4

var (
	kernel32                     = syscall.NewLazyDLL("kernel32")
	procAllocConsole             = kernel32.NewProc("AllocConsole")
	procGenerateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent")
)

func main() {
	if len(os.Args) == 2 {
		var err error
		switch os.Args[1] {
		case "install":
			err = installService("mackerel-agent", "mackerel agent")
		case "remove":
			err = removeService("mackerel-agent")
		}
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	elog, err := eventlog.Open(name)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer elog.Close()

	// `svc.Run` blocks until windows service will stopped.
	// ref. https://msdn.microsoft.com/library/cc429362.aspx
	err = svc.Run(name, &handler{elog: elog})
	if err != nil {
		log.Fatal(err.Error())
	}
}

type logger interface {
	Info(eid uint32, msg string) error
	Warning(eid uint32, msg string) error
	Error(eid uint32, msg string) error
}

type handler struct {
	elog logger
	cmd  *exec.Cmd
	r    io.Reader
	w    io.WriteCloser
	wg   sync.WaitGroup
}

// ex.
// verbose log: 2017/01/21 22:21:08 command.go:434: DEBUG <command> received 'immediate' chan
// normal log:  2017/01/24 14:14:27 INFO <main> Starting mackerel-agent version:0.36.0
var logRe = regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} (?:\S+\.go:\d+: )?([A-Z]+) `)

func (h *handler) retire() error {
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"), "retire", "--force")
	cmd.Dir = dir
	return cmd.Run()
}

func (h *handler) start() error {
	procAllocConsole.Call()
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"))
	cmd.SysProcAttr = &syscall.SysProcAttr{
		CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
	}
	cmd.Dir = dir

	h.cmd = cmd
	h.r, h.w = io.Pipe()
	cmd.Stderr = h.w

	err := h.cmd.Start()
	if err != nil {
		return err
	}

	return h.aggregate()
}

func (h *handler) aggregate() error {
	br := bufio.NewReader(h.r)
	lc := make(chan string, 10)
	done := make(chan struct{})

	// It need to read data from pipe continuously. And it need to close handle
	// when process finished.
	// read data from pipe. When data arrived at EOL, send line-string to the
	// channel. Also remaining line to EOF.
	h.wg.Add(1)
	go func() {
		defer h.wg.Done()
		defer h.w.Close()

		// pipe stderr to windows event log
		var body bytes.Buffer
		for {
			b, err := br.ReadByte()
			if err != nil {
				if err != io.EOF {
					h.elog.Error(loggerEid, err.Error())
				}
				break
			}
			if b == '\n' {
				if body.Len() > 0 {
					lc <- body.String()
					body.Reset()
				}
				continue
			}
			body.WriteByte(b)
		}
		if body.Len() > 0 {
			lc <- body.String()
		}
		done <- struct{}{}
	}()

	h.wg.Add(1)
	go func() {
		defer h.wg.Done()

		linebuf := []string{}
	loop:
		for {
			select {
			case line := <-lc:
				if len(linebuf) == 0 || logRe.MatchString(line) {
					linebuf = append(linebuf, line)
				} else {
					linebuf[len(linebuf)-1] += "\n" + line
				}
			case <-time.After(10 * time.Millisecond):
				// When it take 10ms, it is located at end of paragraph. Then
				// slice appended at above should be the paragraph.
				for _, line := range linebuf {
					if match := logRe.FindStringSubmatch(line); match != nil {
						level := match[1]
						switch level {
						case "TRACE", "DEBUG", "INFO":
							h.elog.Info(defaultEid, line)
						case "WARNING", "ERROR":
							h.elog.Warning(defaultEid, line)
						case "CRITICAL":
							h.elog.Error(defaultEid, line)
						default:
							h.elog.Error(defaultEid, line)
						}
					} else {
						h.elog.Error(defaultEid, line)
					}
				}
				select {
				case <-done:
					break loop
				default:
				}
				linebuf = nil
			}
		}
		close(lc)
		close(done)
	}()

	return nil
}

func interrupt(p *os.Process) error {
	r1, _, err := procGenerateConsoleCtrlEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(p.Pid))
	if r1 == 0 {
		return err
	}
	return nil
}

func (h *handler) stop() error {
	if h.cmd != nil && h.cmd.Process != nil {
		err := interrupt(h.cmd.Process)
		if err == nil {
			end := time.Now().Add(10 * time.Second)
			for time.Now().Before(end) {
				if h.cmd.ProcessState != nil && h.cmd.ProcessState.Exited() {
					return nil
				}
				time.Sleep(1 * time.Second)
			}
		}
		return h.cmd.Process.Kill()
	}

	h.wg.Wait()
	return nil
}

func autoRetire() bool {
	env := os.Getenv("MACKEREL_AUTO_RETIREMENT")
	return env != "" && env != "0"
}

// implement https://godoc.org/golang.org/x/sys/windows/svc#Handler
func (h *handler) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
	s <- svc.Status{State: svc.StartPending}
	defer func() {
		s <- svc.Status{State: svc.Stopped}
	}()

	if err := h.start(); err != nil {
		h.elog.Error(startEid, err.Error())
		// https://msdn.microsoft.com/library/windows/desktop/ms681383(v=vs.85).aspx
		// use ERROR_SERVICE_SPECIFIC_ERROR
		return true, 1
	}

	exit := make(chan struct{})
	go func() {
		err := h.cmd.Wait()
		// enter when the child process exited
		if err != nil {
			h.elog.Error(stopEid, err.Error())
		}
		exit <- struct{}{}
	}()

	s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
L:
	for {
		select {
		case req := <-r:
			switch req.Cmd {
			case svc.Interrogate:
				s <- req.CurrentStatus
			case svc.Stop, svc.Shutdown:
				s <- svc.Status{State: svc.StopPending, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				if err := h.stop(); err != nil {
					h.elog.Error(stopEid, err.Error())
					s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				} else {
					if req.Cmd == svc.Shutdown && autoRetire() {
						if err := h.retire(); err != nil {
							h.elog.Error(stopEid, err.Error())
							s <- svc.Status{State: svc.Running, Accepts: svc.AcceptShutdown}
						}
					}
				}
			}
		case <-exit:
			break L
		}
	}

	return
}

func execdir() string {
	p, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	return filepath.Dir(p)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"

	"github.com/mrap/stringutil"
	wordpatterns "github.com/mrap/wordpatterns"
)

type CombosRes struct {
	Combos []string `json:"combos"`
}

// TODO: store results in a db
var (
	_sharedLookup  = make(wordpatterns.Wordmap)
	_sharedWordmap = make(wordpatterns.Wordmap)
)

const (
	MinCombosCount = 0
	MinMaxComboLen = 2
	MinMinComboLen = 2
)

func combosHandler(w http.ResponseWriter, req *http.Request) {
	v := req.URL.Query()

	// Validate query params
	// chars
	chars := v.Get("chars")
	if len(chars) == 0 {
		w.WriteHeader(400)
		return
	}

	// count
	count, err := strconv.Atoi(v.Get("count"))
	if err != nil {
		log.Println(err)
	}
	if count < MinCombosCount {
		count = MinCombosCount
	}

	// max_len
	maxLen, err := strconv.Atoi(v.Get("max_len"))
	if err != nil {
		log.Println(err)
	}
	if maxLen < MinMaxComboLen {
		maxLen = MinMaxComboLen
	}

	// min_len
	minLen, err := strconv.Atoi(v.Get("min_len"))
	if err != nil {
		log.Println(err)
	}
	if minLen < MinMinComboLen {
		minLen = MinMinComboLen
	}

	combos := RankedCombos(_sharedLookup, chars, minLen, maxLen)
	comboList := GenerateComboList(_sharedWordmap, combos, count)

	data, err := json.Marshal(CombosRes{comboList})
	if err != nil {
		log.Fatal(err)
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(data)
}

// StoreWords stores words from a word list file into
func StoreWords(filename string) {
	wm := wordpatterns.CreateWordmap(filename)
	_sharedWordmap = wm
	_sharedLookup = CreateCharWordLookup(_sharedWordmap)
}

// CreateCharWordLookup creates a lookup structure that makes it efficient
// to query for sub-words that only contain specific characters
func CreateCharWordLookup(wm wordpatterns.Wordmap) wordpatterns.Wordmap {
	var (
		lookup = make(wordpatterns.Wordmap)
		sorted string
	)

	for subword, _ := range wm {
		sorted = stringutil.SortString(subword)
		lookup[sorted] = append(lookup[sorted], subword)
	}
	return lookup
}

func CombosWithChars(lookup wordpatterns.Wordmap, chars string, minLen int, maxLen int) []string {
	if maxLen < 0 {
		maxLen = 0
	}
	if minLen < 0 {
		minLen = 0
	}

	var combos []string
	sortedChars := stringutil.SortString(chars)

	for _, subsorted := range stringutil.Substrs(sortedChars, 2) {
		if (minLen != 0 && len(subsorted) < minLen) || (maxLen != 0 && len(subsorted) > maxLen) {
			continue
		} else {
			combos = append(combos, lookup[subsorted]...)
		}
	}
	return combos
}

func RankedCombos(lookup wordpatterns.Wordmap, chars string, minLen, maxLen int) []string {
	combos := CombosWithChars(lookup, chars, minLen, maxLen)
	sort.Sort(ByWordCount(combos))
	return combos
}

// GenerateComboList returns a slice of combos that are distributed based on rank
// More occurring combos will appear more frequently
func GenerateComboList(wm wordpatterns.Wordmap, combos []string, count int) []string {
	total := 0
	for _, c := range combos {
		total += len(wm[c])
	}

	list := make([]string, count)
	var r, cur int
	for i := 0; i < count; i++ {
		cur = 0
		r = rand.Intn(total) + 1
		for _, c := range combos {
			cur += len(wm[c])
			if r <= cur {
				list[i] = c
				break
			}
		}
	}
	return list
}

type ByWordCount []string

func (a ByWordCount) Len() int      { return len(a) }
func (a ByWordCount) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByWordCount) Less(i, j int) bool {
	return _sharedWordmap.Compare(a[i], a[j]) >= 0
}

func printComboListCounts(combos []string) {
	counts := make(map[string]int)
	for _, c := range combos {
		counts[c]++
	}
	for key, count := range counts {
		fmt.Printf("%s -> %d\n", key, count)
	}
	fmt.Println()
}
<commit_msg>/combo max_len is never less than min_len<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"

	"github.com/mrap/stringutil"
	wordpatterns "github.com/mrap/wordpatterns"
)

type CombosRes struct {
	Combos []string `json:"combos"`
}

// TODO: store results in a db
var (
	_sharedLookup  = make(wordpatterns.Wordmap)
	_sharedWordmap = make(wordpatterns.Wordmap)
)

const (
	MinCombosCount = 0
	MinMaxComboLen = 2
	MinMinComboLen = 2
)

func combosHandler(w http.ResponseWriter, req *http.Request) {
	v := req.URL.Query()

	// Validate query params
	// chars
	chars := v.Get("chars")
	if len(chars) == 0 {
		w.WriteHeader(400)
		return
	}

	// count
	count, err := strconv.Atoi(v.Get("count"))
	if err != nil {
		log.Println(err)
	}
	if count < MinCombosCount {
		count = MinCombosCount
	}

	// min_len
	minLen, err := strconv.Atoi(v.Get("min_len"))
	if err != nil {
		log.Println(err)
	}
	if minLen < MinMinComboLen {
		minLen = MinMinComboLen
	}

	// max_len
	maxLen, err := strconv.Atoi(v.Get("max_len"))
	if err != nil {
		log.Println(err)
	}
	if maxLen < MinMaxComboLen {
		maxLen = MinMaxComboLen
	}
	if maxLen < minLen {
		maxLen = minLen
	}

	combos := RankedCombos(_sharedLookup, chars, minLen, maxLen)
	comboList := GenerateComboList(_sharedWordmap, combos, count)

	data, err := json.Marshal(CombosRes{comboList})
	if err != nil {
		log.Fatal(err)
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(data)
}

// StoreWords stores words from a word list file into
func StoreWords(filename string) {
	wm := wordpatterns.CreateWordmap(filename)
	_sharedWordmap = wm
	_sharedLookup = CreateCharWordLookup(_sharedWordmap)
}

// CreateCharWordLookup creates a lookup structure that makes it efficient
// to query for sub-words that only contain specific characters
func CreateCharWordLookup(wm wordpatterns.Wordmap) wordpatterns.Wordmap {
	var (
		lookup = make(wordpatterns.Wordmap)
		sorted string
	)

	for subword, _ := range wm {
		sorted = stringutil.SortString(subword)
		lookup[sorted] = append(lookup[sorted], subword)
	}
	return lookup
}

func CombosWithChars(lookup wordpatterns.Wordmap, chars string, minLen int, maxLen int) []string {
	if maxLen < 0 {
		maxLen = 0
	}
	if minLen < 0 {
		minLen = 0
	}

	var combos []string
	sortedChars := stringutil.SortString(chars)

	for _, subsorted := range stringutil.Substrs(sortedChars, 2) {
		if (minLen != 0 && len(subsorted) < minLen) || (maxLen != 0 && len(subsorted) > maxLen) {
			continue
		} else {
			combos = append(combos, lookup[subsorted]...)
		}
	}
	return combos
}

func RankedCombos(lookup wordpatterns.Wordmap, chars string, minLen, maxLen int) []string {
	combos := CombosWithChars(lookup, chars, minLen, maxLen)
	sort.Sort(ByWordCount(combos))
	return combos
}

// GenerateComboList returns a slice of combos that are distributed based on rank
// More occurring combos will appear more frequently
func GenerateComboList(wm wordpatterns.Wordmap, combos []string, count int) []string {
	total := 0
	for _, c := range combos {
		total += len(wm[c])
	}

	list := make([]string, count)
	var r, cur int
	for i := 0; i < count; i++ {
		cur = 0
		r = rand.Intn(total) + 1
		for _, c := range combos {
			cur += len(wm[c])
			if r <= cur {
				list[i] = c
				break
			}
		}
	}
	return list
}

type ByWordCount []string

func (a ByWordCount) Len() int      { return len(a) }
func (a ByWordCount) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByWordCount) Less(i, j int) bool {
	return _sharedWordmap.Compare(a[i], a[j]) >= 0
}

func printComboListCounts(combos []string) {
	counts := make(map[string]int)
	for _, c := range combos {
		counts[c]++
	}
	for key, count := range counts {
		fmt.Printf("%s -> %d\n", key, count)
	}
	fmt.Println()
}
<|endoftext|>


<commit_before>package epub

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

const (
	containerFilename     = "container.xml"
	containerFileTemplate = `<?xml version="1.0" encoding="UTF-8"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
  <rootfiles>
    <rootfile full-path="%s/%s" media-type="application/oebps-package+xml" />
  </rootfiles>
</container>
`
	contentFolderName = "EPUB"
	// Permissions for any new directories we create
	dirPermissions = 0755
	// Permissions for any new files we create
	filePermissions   = 0644
	imageFolderName   = "img"
	mediaTypeNcx      = "application/x-dtbncx+xml"
	mediaTypeEpub     = "application/epub+zip"
	mediaTypeXhtml    = "application/xhtml+xml"
	metaInfFolderName = "META-INF"
	mimetypeFilename  = "mimetype"
	pkgFilename       = "package.opf"
	sectionFileFormat = "section%04d.xhtml"
	tempDirPrefix     = "go-epub"
	xhtmlFolderName   = "xhtml"
)

func (e *Epub) Write(destFilePath string) error {
	tempDir, err := ioutil.TempDir("", tempDirPrefix)
	defer os.Remove(tempDir)
	if err != nil {
		log.Fatalf("os.Remove error: %s", err)
	}

	err = createEpubFolders(tempDir)
	if err != nil {
		return err
	}

	err = writeMimetype(tempDir)
	if err != nil {
		return err
	}

	err = e.writeSections(tempDir)
	if err != nil {
		return err
	}

	err = e.writeToc(tempDir)
	if err != nil {
		return err
	}

	err = writeContainerFile(tempDir)
	if err != nil {
		return err
	}

	err = e.writePackageFile(tempDir)
	if err != nil {
		return err
	}

	err = e.writeEpub(tempDir, destFilePath)
	if err != nil {
		return err
	}

	// TODO

	//	output, err := xml.MarshalIndent(e.toc.navDoc.xml, "", "  ")
	//	output = append([]byte(xhtmlDoctype), output...)

	//	output, err := xml.MarshalIndent(e.pkg.xml, "", "  ")

	//  output, err := xml.MarshalIndent(e.toc.ncxXml, "", "  ")
	//	output = append([]byte(xml.Header), output...)
	//	fmt.Println(string(output))

	return nil
}

func createEpubFolders(tempDir string) error {
	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			contentFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			contentFolderName,
			xhtmlFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			metaInfFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	return nil
}

func writeContainerFile(tempDir string) error {
	containerFilePath := filepath.Join(tempDir, metaInfFolderName, containerFilename)
	if err := ioutil.WriteFile(
		containerFilePath,
		[]byte(
			fmt.Sprintf(
				containerFileTemplate,
				contentFolderName,
				pkgFilename,
			),
		),
		filePermissions,
	); err != nil {
		return err
	}

	return nil
}

/*
    type Rootfile struct {
        FullPath string `xml:"full-path,attr"`
        MediaType string `xml:"media-type,attr"`
    }

    type Rootfiles struct {
        Rootfile []Rootfile `xml:"rootfile"`
    }

    type Container struct {
        XMLName xml.Name `xml:"urn:oasis:names:tc:opendocument:xmlns:container container"`
        Version string `xml:"version,attr"`
        Rootfiles Rootfiles `xml:"rootfiles"`
    }

    v := &Container{Version: containerVersion}

    v.Rootfiles.Rootfile = append(
        v.Rootfiles.Rootfile,
        Rootfile{
            FullPath: filepath.Join(oebpsFolderName, contentFilename),
            MediaType: mediaTypeOebpsPackage,
        },
    )

    output, err := xml.MarshalIndent(v, "", `   `)
    if err != nil {
        return err
    }
    // Add the xml header to the output
    containerContent := append([]byte(xml.Header), output...)
    // It's generally nice to have files end with a newline
    containerContent = append(containerContent, "\n"...)

    containerFilePath := filepath.Join(metaInfFolderPath, containerFilename)
    if err := ioutil.WriteFile(containerFilePath, containerContent, filePermissions); err != nil {
        return err
    }

    return nil
}

/*
func writeContentFile(tempDir string) error {
    oebpsFolderPath := filepath.Join(tempDir, oebpsFolderName)
    if err := os.Mkdir(oebpsFolderPath, dirPermissions); err != nil {
        return err
    }

    type Package struct {
        XMLName xml.Name `xml:"http://www.idpf.org/2007/opf package"`
        UniqueIdentifier string `xml:"unique-identifier,attr"`
        Version string `xml:"version,attr"`
    }

    type Rootfile struct {
        FullPath string `xml:"full-path,attr"`
        MediaType string `xml:"media-type,attr"`
    }

    type Rootfiles struct {
        Rootfile []Rootfile `xml:"rootfile"`
    }

    type Container struct {
        XMLName xml.Name `xml:"urn:oasis:names:tc:opendocument:xmlns:container container"`
        Version string `xml:"version,attr"`
        Rootfiles Rootfiles `xml:"rootfiles"`
    }

    v := &Container{Version: containerVersion}

    v.Rootfiles.Rootfile = append(
        v.Rootfiles.Rootfile,
        Rootfile{
            FullPath: filepath.Join(oebpsFolderName, contentFilename),
            MediaType: mediaTypeOebpsPackage,
        },
    )

    output, err := xml.MarshalIndent(v, "", `   `)
    if err != nil {
        return err
    }
    // Add the xml header to the output
    containerContent := append([]byte(xml.Header), output...)
    // It's generally nice to have files end with a newline
    containerContent = append(containerContent, "\n"...)

    containerFilePath := filepath.Join(metaInfFolderPath, containerFilename)
    if err := ioutil.WriteFile(containerFilePath, containerContent, filePermissions); err != nil {
        return err
    }

    return nil
}
*/
func writeMimetype(tempDir string) error {
	mimetypeFilePath := filepath.Join(tempDir, mimetypeFilename)

	if err := ioutil.WriteFile(mimetypeFilePath, []byte(mediaTypeEpub), filePermissions); err != nil {
		return err
	}

	return nil
}

func (e *Epub) writeEpub(tempDir string, destFilePath string) error {
	f, err := os.Create(destFilePath)
	if err != nil {
		log.Fatalf("os.Create error: %s", err)
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.Fatalf("os.File.Close error: %s", err)
		}
	}()

	z := zip.NewWriter(f)
	defer func() {
		if err := z.Close(); err != nil {
			log.Fatalf("zip.Writer.Close error: %s", err)
		}
	}()

	skipMimetypeFile := false

	var addFileToZip = func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Get the path of the file relative to the folder we're zipping
		relativePath, err := filepath.Rel(tempDir, path)
		if err != nil {
			log.Fatalf("filepath.Rel error: %s", err)
		}

		// Only include regular files, not directories
		if !info.Mode().IsRegular() {
			return nil
		}

		var w io.Writer
		if path == filepath.Join(tempDir, mimetypeFilename) {
			// Skip the mimetype file
			if skipMimetypeFile == true {
				return nil
			}
			// The mimetype file must be uncompressed according to the EPUB spec
			w, err = z.CreateHeader(&zip.FileHeader{
				Name:   relativePath,
				Method: zip.Store,
			})
		} else {
			w, err = z.Create(relativePath)
		}
		if err != nil {
			log.Fatalf("zip.Writer.Create error: %s", err)
		}

		r, err := os.Open(path)
		if err != nil {
			log.Fatalf("os.Open error: %s", err)
		}
		defer func() {
			if err := r.Close(); err != nil {
				log.Fatalf("os.File.Close error: %s", err)
			}
		}()

		_, err = io.Copy(w, r)
		if err != nil {
			log.Fatalf("io.Copy error: %s", err)
		}

		return nil
	}

	// Add the mimetype file first
	mimetypeFilePath := filepath.Join(tempDir, mimetypeFilename)
	mimetypeInfo, err := os.Lstat(mimetypeFilePath)
	if err != nil {
		log.Fatalf("os.Lstat error: %s", err)
	}
	addFileToZip(mimetypeFilePath, mimetypeInfo, nil)

	skipMimetypeFile = true

	err = filepath.Walk(tempDir, addFileToZip)
	if err != nil {
		log.Fatalf("os.Lstat error: %s", err)
	}

	return nil
}

func (e *Epub) writePackageFile(tempDir string) error {
	err := e.pkg.write(tempDir)
	if err != nil {
		return err
	}

	return nil
}

func (e *Epub) writeSections(tempDir string) error {
	for i, section := range e.sections {
		sectionIndex := i + 1
		sectionFilename := fmt.Sprintf(sectionFileFormat, sectionIndex)
		sectionFilePath := filepath.Join(tempDir, contentFolderName, xhtmlFolderName, sectionFilename)

		if err := section.write(sectionFilePath); err != nil {
			return err
		}

		relativePath := filepath.Join(xhtmlFolderName, sectionFilename)
		e.toc.addSection(sectionIndex, section.Title(), relativePath)
		e.pkg.addToManifest(sectionFilename, relativePath, mediaTypeXhtml, "")
		e.pkg.addToSpine(sectionFilename)
	}

	return nil
}

func (e *Epub) writeToc(tempDir string) error {
	e.pkg.addToManifest(tocNavItemId, tocNavFilename, mediaTypeXhtml, tocNavItemProperties)
	e.pkg.addToManifest(tocNcxItemId, tocNcxFilename, mediaTypeNcx, "")

	err := e.toc.write(tempDir)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add functionality to write the images to the epub file<commit_after>package epub

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	containerFilename     = "container.xml"
	containerFileTemplate = `<?xml version="1.0" encoding="UTF-8"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
  <rootfiles>
    <rootfile full-path="%s/%s" media-type="application/oebps-package+xml" />
  </rootfiles>
</container>
`
	contentFolderName = "EPUB"
	// Permissions for any new directories we create
	dirPermissions = 0755
	// Permissions for any new files we create
	filePermissions   = 0644
	imageFolderName   = "img"
	mediaTypeNcx      = "application/x-dtbncx+xml"
	mediaTypeEpub     = "application/epub+zip"
	mediaTypeXhtml    = "application/xhtml+xml"
	metaInfFolderName = "META-INF"
	mimetypeFilename  = "mimetype"
	pkgFilename       = "package.opf"
	sectionFileFormat = "section%04d.xhtml"
	tempDirPrefix     = "go-epub"
	xhtmlFolderName   = "xhtml"
)

func (e *Epub) Write(destFilePath string) error {
	tempDir, err := ioutil.TempDir("", tempDirPrefix)
	defer os.Remove(tempDir)
	if err != nil {
		log.Fatalf("os.Remove error: %s", err)
	}

	// Must be run first
	err = createEpubFolders(tempDir)
	if err != nil {
		return err
	}

	err = e.writeImages(tempDir)
	if err != nil {
		return err
	}

	err = writeMimetype(tempDir)
	if err != nil {
		return err
	}

	err = e.writeSections(tempDir)
	if err != nil {
		return err
	}

	err = e.writeToc(tempDir)
	if err != nil {
		return err
	}

	err = writeContainerFile(tempDir)
	if err != nil {
		return err
	}

	err = e.writePackageFile(tempDir)
	if err != nil {
		return err
	}

	err = e.writeEpub(tempDir, destFilePath)
	if err != nil {
		return err
	}

	// TODO

	//	output, err := xml.MarshalIndent(e.toc.navDoc.xml, "", "  ")
	//	output = append([]byte(xhtmlDoctype), output...)

	//	output, err := xml.MarshalIndent(e.pkg.xml, "", "  ")

	//  output, err := xml.MarshalIndent(e.toc.ncxXml, "", "  ")
	//	output = append([]byte(xml.Header), output...)
	//	fmt.Println(string(output))

	return nil
}

func createEpubFolders(tempDir string) error {
	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			contentFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			contentFolderName,
			xhtmlFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	if err := os.Mkdir(
		filepath.Join(
			tempDir,
			metaInfFolderName,
		),
		dirPermissions); err != nil {
		return err
	}

	return nil
}

func writeContainerFile(tempDir string) error {
	containerFilePath := filepath.Join(tempDir, metaInfFolderName, containerFilename)
	if err := ioutil.WriteFile(
		containerFilePath,
		[]byte(
			fmt.Sprintf(
				containerFileTemplate,
				contentFolderName,
				pkgFilename,
			),
		),
		filePermissions,
	); err != nil {
		return err
	}

	return nil
}

func (e *Epub) writeImages(tempDir string) error {
	imageFolderPath := filepath.Join(tempDir, contentFolderName, imageFolderName)
	if err := os.Mkdir(imageFolderPath, dirPermissions); err != nil {
		return err
	}

	for imageFilename, imageSource := range e.images {
		u, err := url.Parse(imageSource)
		if err != nil {
			return err
		}

		var r io.ReadCloser
		var resp *http.Response
		// If it's a URL
		if u.Scheme == "http" || u.Scheme == "https" {
			resp, err = http.Get(imageSource)
			r = resp.Body

			// Otherwise, assume it's a local file
		} else {
			r, err = os.Open(imageSource)
		}
		if err != nil {
			return err
		}
		defer func() {
			err = r.Close()
		}()

		imageFilePath := filepath.Join(
			imageFolderPath,
			imageFilename,
		)

		w, err := os.Create(imageFilePath)
		if err != nil {
			return err
		}
		defer func() {
			err = w.Close()
		}()

		_, err = io.Copy(w, r)
		if err != nil {
			return err
		}
	}

	return nil
}

/*
    type Rootfile struct {
        FullPath string `xml:"full-path,attr"`
        MediaType string `xml:"media-type,attr"`
    }

    type Rootfiles struct {
        Rootfile []Rootfile `xml:"rootfile"`
    }

    type Container struct {
        XMLName xml.Name `xml:"urn:oasis:names:tc:opendocument:xmlns:container container"`
        Version string `xml:"version,attr"`
        Rootfiles Rootfiles `xml:"rootfiles"`
    }

    v := &Container{Version: containerVersion}

    v.Rootfiles.Rootfile = append(
        v.Rootfiles.Rootfile,
        Rootfile{
            FullPath: filepath.Join(oebpsFolderName, contentFilename),
            MediaType: mediaTypeOebpsPackage,
        },
    )

    output, err := xml.MarshalIndent(v, "", `   `)
    if err != nil {
        return err
    }
    // Add the xml header to the output
    containerContent := append([]byte(xml.Header), output...)
    // It's generally nice to have files end with a newline
    containerContent = append(containerContent, "\n"...)

    containerFilePath := filepath.Join(metaInfFolderPath, containerFilename)
    if err := ioutil.WriteFile(containerFilePath, containerContent, filePermissions); err != nil {
        return err
    }

    return nil
}

/*
func writeContentFile(tempDir string) error {
    oebpsFolderPath := filepath.Join(tempDir, oebpsFolderName)
    if err := os.Mkdir(oebpsFolderPath, dirPermissions); err != nil {
        return err
    }

    type Package struct {
        XMLName xml.Name `xml:"http://www.idpf.org/2007/opf package"`
        UniqueIdentifier string `xml:"unique-identifier,attr"`
        Version string `xml:"version,attr"`
    }

    type Rootfile struct {
        FullPath string `xml:"full-path,attr"`
        MediaType string `xml:"media-type,attr"`
    }

    type Rootfiles struct {
        Rootfile []Rootfile `xml:"rootfile"`
    }

    type Container struct {
        XMLName xml.Name `xml:"urn:oasis:names:tc:opendocument:xmlns:container container"`
        Version string `xml:"version,attr"`
        Rootfiles Rootfiles `xml:"rootfiles"`
    }

    v := &Container{Version: containerVersion}

    v.Rootfiles.Rootfile = append(
        v.Rootfiles.Rootfile,
        Rootfile{
            FullPath: filepath.Join(oebpsFolderName, contentFilename),
            MediaType: mediaTypeOebpsPackage,
        },
    )

    output, err := xml.MarshalIndent(v, "", `   `)
    if err != nil {
        return err
    }
    // Add the xml header to the output
    containerContent := append([]byte(xml.Header), output...)
    // It's generally nice to have files end with a newline
    containerContent = append(containerContent, "\n"...)

    containerFilePath := filepath.Join(metaInfFolderPath, containerFilename)
    if err := ioutil.WriteFile(containerFilePath, containerContent, filePermissions); err != nil {
        return err
    }

    return nil
}
*/
func writeMimetype(tempDir string) error {
	mimetypeFilePath := filepath.Join(tempDir, mimetypeFilename)

	if err := ioutil.WriteFile(mimetypeFilePath, []byte(mediaTypeEpub), filePermissions); err != nil {
		return err
	}

	return nil
}

func (e *Epub) writeEpub(tempDir string, destFilePath string) error {
	f, err := os.Create(destFilePath)
	if err != nil {
		log.Fatalf("os.Create error: %s", err)
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.Fatalf("os.File.Close error: %s", err)
		}
	}()

	z := zip.NewWriter(f)
	defer func() {
		if err := z.Close(); err != nil {
			log.Fatalf("zip.Writer.Close error: %s", err)
		}
	}()

	skipMimetypeFile := false

	var addFileToZip = func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Get the path of the file relative to the folder we're zipping
		relativePath, err := filepath.Rel(tempDir, path)
		if err != nil {
			log.Fatalf("filepath.Rel error: %s", err)
		}

		// Only include regular files, not directories
		if !info.Mode().IsRegular() {
			return nil
		}

		var w io.Writer
		if path == filepath.Join(tempDir, mimetypeFilename) {
			// Skip the mimetype file
			if skipMimetypeFile == true {
				return nil
			}
			// The mimetype file must be uncompressed according to the EPUB spec
			w, err = z.CreateHeader(&zip.FileHeader{
				Name:   relativePath,
				Method: zip.Store,
			})
		} else {
			w, err = z.Create(relativePath)
		}
		if err != nil {
			log.Fatalf("zip.Writer.Create error: %s", err)
		}

		r, err := os.Open(path)
		if err != nil {
			log.Fatalf("os.Open error: %s", err)
		}
		defer func() {
			if err := r.Close(); err != nil {
				log.Fatalf("os.File.Close error: %s", err)
			}
		}()

		_, err = io.Copy(w, r)
		if err != nil {
			log.Fatalf("io.Copy error: %s", err)
		}

		return nil
	}

	// Add the mimetype file first
	mimetypeFilePath := filepath.Join(tempDir, mimetypeFilename)
	mimetypeInfo, err := os.Lstat(mimetypeFilePath)
	if err != nil {
		log.Fatalf("os.Lstat error: %s", err)
	}
	addFileToZip(mimetypeFilePath, mimetypeInfo, nil)

	skipMimetypeFile = true

	err = filepath.Walk(tempDir, addFileToZip)
	if err != nil {
		log.Fatalf("os.Lstat error: %s", err)
	}

	return nil
}

func (e *Epub) writePackageFile(tempDir string) error {
	err := e.pkg.write(tempDir)
	if err != nil {
		return err
	}

	return nil
}

func (e *Epub) writeSections(tempDir string) error {
	for i, section := range e.sections {
		sectionIndex := i + 1
		sectionFilename := fmt.Sprintf(sectionFileFormat, sectionIndex)
		sectionFilePath := filepath.Join(tempDir, contentFolderName, xhtmlFolderName, sectionFilename)

		if err := section.write(sectionFilePath); err != nil {
			return err
		}

		relativePath := filepath.Join(xhtmlFolderName, sectionFilename)
		e.toc.addSection(sectionIndex, section.Title(), relativePath)
		e.pkg.addToManifest(sectionFilename, relativePath, mediaTypeXhtml, "")
		e.pkg.addToSpine(sectionFilename)
	}

	return nil
}

func (e *Epub) writeToc(tempDir string) error {
	e.pkg.addToManifest(tocNavItemId, tocNavFilename, mediaTypeXhtml, tocNavItemProperties)
	e.pkg.addToManifest(tocNcxItemId, tocNcxFilename, mediaTypeNcx, "")

	err := e.toc.write(tempDir)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>