<commit_before>// Copyright (c) 2019 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

/*
conf is a package used to read configuration file (~/.lssh.conf).
*/
package conf

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/blacknon/lssh/common"
)

// Config is Struct that stores the entire configuration file
type Config struct {
	Log      LogConfig
	Shell    ShellConfig
	Include  map[string]IncludeConfig
	Includes IncludesConfig
	Common   ServerConfig
	Server   map[string]ServerConfig
	Proxy    map[string]ProxyConfig

	SSHConfig map[string]OpenSshConfig
}

// LogConfig store the contents about the terminal log.
// The log file name is created in "YYYYmmdd_HHMMSS_servername.log" of the specified directory.
type LogConfig struct {
	// Enable terminal logging.
	Enable bool `toml:"enable"`

	// Add a timestamp at the beginning of the terminal log line.
	Timestamp bool `toml:"timestamp"`

	// Specifies the directory for creating terminal logs.
	Dir string `toml:"dirpath"`
}

// ShellConfig Structure for storing lssh-shell settings.
type ShellConfig struct {
	// prompt
	Prompt  string `toml:"PROMPT"`  // lssh shell prompt
	OPrompt string `toml:"OPROMPT"` // lssh shell output prompt

	// message,title etc...
	Title string `toml:"title"`

	// history file
	HistoryFile string `toml:"histfile"`

	// pre | post command setting
	PreCmd  string `toml:"pre_cmd"`
	PostCmd string `toml:"post_cmd"`
}

// IncludeConfig specify the configuration file to include (ServerConfig only).
type IncludeConfig struct {
	Path string `toml:"path"`
}

// IncludesConfig specify the configuration file to include (ServerConfig only).
// Struct that can specify multiple files in array.
type IncludesConfig struct {
	// example:
	// 	path = [
	// 		 "~/.lssh.d/home.conf"
	// 		,"~/.lssh.d/cloud.conf"
	// 	]
	Path []string `toml:"path"`
}

// ServerConfig Structure for holding SSH connection information
type ServerConfig struct {
	// Connect basic Setting
	Addr string `toml:"addr"`
	Port string `toml:"port"`
	User string `toml:"user"`

	// Connect auth Setting
	Pass            string   `toml:"pass"`
	Passes          []string `toml:"passes"`
	Key             string   `toml:"key"`
	KeyCommand      string   `toml:"keycmd"`
	KeyCommandPass  string   `toml:"keycmdpass"`
	KeyPass         string   `toml:"keypass"`
	Keys            []string `toml:"keys"` // "keypath::passphrase"
	Cert            string   `toml:"cert"`
	CertKey         string   `toml:"certkey"`
	CertKeyPass     string   `toml:"certkeypass"`
	CertPKCS11      bool     `toml:"certpkcs11"`
	AgentAuth       bool     `toml:"agentauth"`
	SSHAgentUse     bool     `toml:"ssh_agent"`
	SSHAgentKeyPath []string `toml:"ssh_agent_key"` // "keypath::passphrase"
	PKCS11Use       bool     `toml:"pkcs11"`
	PKCS11Provider  string   `toml:"pkcs11provider"` // PKCS11 Provider PATH
	PKCS11PIN       string   `toml:"pkcs11pin"`      // PKCS11 PIN code

	// pre | post command setting
	PreCmd  string `toml:"pre_cmd"`
	PostCmd string `toml:"post_cmd"`

	// proxy setting
	ProxyType    string `toml:"proxy_type"`
	Proxy        string `toml:"proxy"`
	ProxyCommand string `toml:"proxy_cmd"` // OpenSSH type proxy setting

	// local rcfile setting
	LocalRcUse       string   `toml:"local_rc"` // yes|no (default: yes)
	LocalRcPath      []string `toml:"local_rc_file"`
	LocalRcDecodeCmd string   `toml:"local_rc_decode_cmd"`

	// local/remote port forwarding setting
	PortForwardMode   string `toml:"port_forward"`        // [`L`,`l`,`LOCAL`,`local`]|[`R`,`r`,`REMOTE`,`remote`]
	PortForwardLocal  string `toml:"port_forward_local"`  // port forward (local). "host:port"
	PortForwardRemote string `toml:"port_forward_remote"` // port forward (remote). "host:port"

	// Dynamic Port Forwarding setting
	DynamicPortForward string `toml:"dynamic_port_forward"` // ex.) "11080"

	// x11 forwarding setting
	X11 bool `toml:"x11"`

	// Connection Timeout second
	ConnectTimeout int `toml:"connect_timeout"`

	// Server Alive
	ServerAliveCountMax      int `toml:"alive_max"`
	ServerAliveCountInterval int `toml:"alive_interval"`

	// note
	Note string `toml:"note"`
}

//　ProxyConfig is that stores Proxy server settings connected via http and socks5.
type ProxyConfig struct {
	Addr      string `toml:"addr"`
	Port      string `toml:"port"`
	User      string `toml:"user"`
	Pass      string `toml:"pass"`
	Proxy     string `toml:"proxy"`
	ProxyType string `toml:"proxy_type"`
	Note      string `toml:"note"`
}

// OpenSshConfig is  read OpenSSH configuration file.
//
// WARN: This struct is not use...
type OpenSshConfig struct {
	Path    string `toml:"path"` // This is preferred
	Command string `toml:"command"`
	ServerConfig
}

// ReadConf load configuration file and return Config structure
// TODO(blacknon): リファクタリング！(v0.6.1) 外出しや処理のまとめなど
func ReadConf(confPath string) (config Config) {
	// user path
	usr, _ := user.Current()

	if !common.IsExist(confPath) {
		fmt.Printf("Config file(%s) Not Found.\nPlease create file.\n\n", confPath)
		fmt.Printf("sample: %s\n", "https://raw.githubusercontent.com/blacknon/lssh/master/example/config.tml")
		os.Exit(1)
	}

	config.Server = map[string]ServerConfig{}
	config.SSHConfig = map[string]OpenSshConfig{}

	// Read config file
	_, err := toml.DecodeFile(confPath, &config)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// reduce common setting (in .lssh.conf servers)
	for key, value := range config.Server {
		setValue := serverConfigReduct(config.Common, value)
		config.Server[key] = setValue
	}

	// Read Openssh configs
	if len(config.SSHConfig) == 0 {
		openSshServerConfig, err := getOpenSshConfig("~/.ssh/config", "")
		if err == nil {
			// append data
			for key, value := range openSshServerConfig {
				value := serverConfigReduct(config.Common, value)
				config.Server[key] = value
			}
		}
	} else {
		for _, sshConfig := range config.SSHConfig {
			openSshServerConfig, err := getOpenSshConfig(sshConfig.Path, sshConfig.Command)
			if err == nil {
				// append data
				for key, value := range openSshServerConfig {
					setCommon := serverConfigReduct(config.Common, sshConfig.ServerConfig)
					value = serverConfigReduct(setCommon, value)
					config.Server[key] = value
				}
			}
		}
	}

	// for append includes to include.path
	if config.Includes.Path != nil {
		if config.Include == nil {
			config.Include = map[string]IncludeConfig{}
		}

		for _, includePath := range config.Includes.Path {
			unixTime := time.Now().Unix()
			keyString := strings.Join([]string{string(unixTime), includePath}, "_")

			// key to md5
			hasher := md5.New()
			hasher.Write([]byte(keyString))
			key := string(hex.EncodeToString(hasher.Sum(nil)))

			// append config.Include[key]
			config.Include[key] = IncludeConfig{strings.Replace(includePath, "~", usr.HomeDir, 1)}
		}
	}

	// Read include files
	if config.Include != nil {
		for _, v := range config.Include {
			var includeConf Config

			// user path
			path := strings.Replace(v.Path, "~", usr.HomeDir, 1)

			// Read include config file
			_, err := toml.DecodeFile(path, &includeConf)
			if err != nil {
				panic(err)
			}

			// reduce common setting
			setCommon := serverConfigReduct(config.Common, includeConf.Common)

			// map init
			if len(config.Server) == 0 {
				config.Server = map[string]ServerConfig{}
			}

			// add include file serverconf
			for key, value := range includeConf.Server {
				// reduce common setting
				setValue := serverConfigReduct(setCommon, value)
				config.Server[key] = setValue
			}
		}
	}

	// Check Config Parameter
	checkAlertFlag := checkFormatServerConf(config)
	if !checkAlertFlag {
		os.Exit(1)
	}

	return
}

// checkFormatServerConf checkes format of server config.
//
// Note: Checking Addr, User and authentications
// having a value. No checking a validity of each fields.
//
// See also: checkFormatServerConfAuth function.
func checkFormatServerConf(c Config) (isFormat bool) {
	isFormat = true
	for k, v := range c.Server {
		// Address Set Check
		if v.Addr == "" {
			fmt.Printf("%s: 'addr' is not set.\n", k)
			isFormat = false
		}

		// User Set Check
		if v.User == "" {
			fmt.Printf("%s: 'user' is not set.\n", k)
			isFormat = false
		}

		if !checkFormatServerConfAuth(v) {
			fmt.Printf("%s: Authentication information is not set.\n", k)
			isFormat = false
		}
	}
	return
}

// checkFormatServerConfAuth checkes format of server config authentication.
//
// Note: Checking Pass, Key, Cert, AgentAuth, PKCS11Use, PKCS11Provider, Keys or
// Passes having a value. No checking a validity of each fields.
func checkFormatServerConfAuth(c ServerConfig) (isFormat bool) {
	isFormat = false
	if c.Pass != "" || c.Key != "" || c.Cert != "" {
		isFormat = true
	}

	if c.AgentAuth == true {
		isFormat = true
	}

	if c.PKCS11Use == true {
		_, err := os.Stat(c.PKCS11Provider)
		if err == nil {
			isFormat = true
		}
	}

	if len(c.Keys) > 0 || len(c.Passes) > 0 {
		isFormat = true
	}

	return
}

// serverConfigReduct returns a new server config that set perConfig field to
// childConfig empty filed.
func serverConfigReduct(perConfig, childConfig ServerConfig) ServerConfig {
	result := ServerConfig{}

	// struct to map
	perConfigMap, _ := common.StructToMap(&perConfig)
	childConfigMap, _ := common.StructToMap(&childConfig)

	resultMap := common.MapReduce(perConfigMap, childConfigMap)
	_ = common.MapToStruct(resultMap, &result)

	return result
}

// GetNameList return a list of server names from the Config structure.
func GetNameList(listConf Config) (nameList []string) {
	for k := range listConf.Server {
		nameList = append(nameList, k)
	}
	return
}
<commit_msg>update comment<commit_after>// Copyright (c) 2019 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

/*
conf is a package used to read configuration file (~/.lssh.conf).
*/
package conf

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/blacknon/lssh/common"
)

// Config is Struct that stores the entire configuration file
type Config struct {
	Log      LogConfig
	Shell    ShellConfig
	Include  map[string]IncludeConfig
	Includes IncludesConfig
	Common   ServerConfig
	Server   map[string]ServerConfig
	Proxy    map[string]ProxyConfig

	SSHConfig map[string]OpenSshConfig
}

// LogConfig store the contents about the terminal log.
// The log file name is created in "YYYYmmdd_HHMMSS_servername.log" of the specified directory.
type LogConfig struct {
	// Enable terminal logging.
	Enable bool `toml:"enable"`

	// Add a timestamp at the beginning of the terminal log line.
	Timestamp bool `toml:"timestamp"`

	// Specifies the directory for creating terminal logs.
	Dir string `toml:"dirpath"`
}

// ShellConfig Structure for storing lssh-shell settings.
type ShellConfig struct {
	// prompt
	Prompt  string `toml:"PROMPT"`  // lssh shell prompt
	OPrompt string `toml:"OPROMPT"` // lssh shell output prompt

	// message,title etc...
	Title string `toml:"title"`

	// history file
	HistoryFile string `toml:"histfile"`

	// pre | post command setting
	PreCmd  string `toml:"pre_cmd"`
	PostCmd string `toml:"post_cmd"`
}

// IncludeConfig specify the configuration file to include (ServerConfig only).
type IncludeConfig struct {
	Path string `toml:"path"`
}

// IncludesConfig specify the configuration file to include (ServerConfig only).
// Struct that can specify multiple files in array.
type IncludesConfig struct {
	// example:
	// 	path = [
	// 		 "~/.lssh.d/home.conf"
	// 		,"~/.lssh.d/cloud.conf"
	// 	]
	Path []string `toml:"path"`
}

// ServerConfig Structure for holding SSH connection information
type ServerConfig struct {
	// Connect basic Setting
	Addr string `toml:"addr"`
	Port string `toml:"port"`
	User string `toml:"user"`

	// Connect auth Setting
	Pass            string   `toml:"pass"`
	Passes          []string `toml:"passes"`
	Key             string   `toml:"key"`
	KeyCommand      string   `toml:"keycmd"`
	KeyCommandPass  string   `toml:"keycmdpass"`
	KeyPass         string   `toml:"keypass"`
	Keys            []string `toml:"keys"` // "keypath::passphrase"
	Cert            string   `toml:"cert"`
	CertKey         string   `toml:"certkey"`
	CertKeyPass     string   `toml:"certkeypass"`
	CertPKCS11      bool     `toml:"certpkcs11"`
	AgentAuth       bool     `toml:"agentauth"`
	SSHAgentUse     bool     `toml:"ssh_agent"`
	SSHAgentKeyPath []string `toml:"ssh_agent_key"` // "keypath::passphrase"
	PKCS11Use       bool     `toml:"pkcs11"`
	PKCS11Provider  string   `toml:"pkcs11provider"` // PKCS11 Provider PATH
	PKCS11PIN       string   `toml:"pkcs11pin"`      // PKCS11 PIN code

	// pre | post command setting
	PreCmd  string `toml:"pre_cmd"`
	PostCmd string `toml:"post_cmd"`

	// proxy setting
	ProxyType    string `toml:"proxy_type"`
	Proxy        string `toml:"proxy"`
	ProxyCommand string `toml:"proxy_cmd"` // OpenSSH type proxy setting

	// local rcfile setting
	LocalRcUse       string   `toml:"local_rc"` // yes|no (default: yes)
	LocalRcPath      []string `toml:"local_rc_file"`
	LocalRcDecodeCmd string   `toml:"local_rc_decode_cmd"`

	// local/remote port forwarding setting
	PortForwardMode   string `toml:"port_forward"`        // [`L`,`l`,`LOCAL`,`local`]|[`R`,`r`,`REMOTE`,`remote`]
	PortForwardLocal  string `toml:"port_forward_local"`  // port forward (local). "host:port"
	PortForwardRemote string `toml:"port_forward_remote"` // port forward (remote). "host:port"

	// local/remote port forwarding settings
	// TODO(blacknon): 複数のLocal/Remote Port Forwardingの追加

	// Dynamic Port Forwarding setting
	DynamicPortForward string `toml:"dynamic_port_forward"` // ex.) "11080"

	// Dynamic Port Forwarding settings
	// TODO(blacknon): 複数のDynamic Forwardingの追加

	// x11 forwarding setting
	X11 bool `toml:"x11"`

	// Connection Timeout second
	ConnectTimeout int `toml:"connect_timeout"`

	// Server Alive
	ServerAliveCountMax      int `toml:"alive_max"`
	ServerAliveCountInterval int `toml:"alive_interval"`

	// note
	Note string `toml:"note"`
}

//　ProxyConfig is that stores Proxy server settings connected via http and socks5.
type ProxyConfig struct {
	Addr      string `toml:"addr"`
	Port      string `toml:"port"`
	User      string `toml:"user"`
	Pass      string `toml:"pass"`
	Proxy     string `toml:"proxy"`
	ProxyType string `toml:"proxy_type"`
	Note      string `toml:"note"`
}

// OpenSshConfig is  read OpenSSH configuration file.
//
// WARN: This struct is not use...
type OpenSshConfig struct {
	Path    string `toml:"path"` // This is preferred
	Command string `toml:"command"`
	ServerConfig
}

// ReadConf load configuration file and return Config structure
// TODO(blacknon): リファクタリング！(v0.6.1) 外出しや処理のまとめなど
func ReadConf(confPath string) (config Config) {
	// user path
	usr, _ := user.Current()

	if !common.IsExist(confPath) {
		fmt.Printf("Config file(%s) Not Found.\nPlease create file.\n\n", confPath)
		fmt.Printf("sample: %s\n", "https://raw.githubusercontent.com/blacknon/lssh/master/example/config.tml")
		os.Exit(1)
	}

	config.Server = map[string]ServerConfig{}
	config.SSHConfig = map[string]OpenSshConfig{}

	// Read config file
	_, err := toml.DecodeFile(confPath, &config)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// reduce common setting (in .lssh.conf servers)
	for key, value := range config.Server {
		setValue := serverConfigReduct(config.Common, value)
		config.Server[key] = setValue
	}

	// Read Openssh configs
	if len(config.SSHConfig) == 0 {
		openSshServerConfig, err := getOpenSshConfig("~/.ssh/config", "")
		if err == nil {
			// append data
			for key, value := range openSshServerConfig {
				value := serverConfigReduct(config.Common, value)
				config.Server[key] = value
			}
		}
	} else {
		for _, sshConfig := range config.SSHConfig {
			openSshServerConfig, err := getOpenSshConfig(sshConfig.Path, sshConfig.Command)
			if err == nil {
				// append data
				for key, value := range openSshServerConfig {
					setCommon := serverConfigReduct(config.Common, sshConfig.ServerConfig)
					value = serverConfigReduct(setCommon, value)
					config.Server[key] = value
				}
			}
		}
	}

	// for append includes to include.path
	if config.Includes.Path != nil {
		if config.Include == nil {
			config.Include = map[string]IncludeConfig{}
		}

		for _, includePath := range config.Includes.Path {
			unixTime := time.Now().Unix()
			keyString := strings.Join([]string{string(unixTime), includePath}, "_")

			// key to md5
			hasher := md5.New()
			hasher.Write([]byte(keyString))
			key := string(hex.EncodeToString(hasher.Sum(nil)))

			// append config.Include[key]
			config.Include[key] = IncludeConfig{strings.Replace(includePath, "~", usr.HomeDir, 1)}
		}
	}

	// Read include files
	if config.Include != nil {
		for _, v := range config.Include {
			var includeConf Config

			// user path
			path := strings.Replace(v.Path, "~", usr.HomeDir, 1)

			// Read include config file
			_, err := toml.DecodeFile(path, &includeConf)
			if err != nil {
				panic(err)
			}

			// reduce common setting
			setCommon := serverConfigReduct(config.Common, includeConf.Common)

			// map init
			if len(config.Server) == 0 {
				config.Server = map[string]ServerConfig{}
			}

			// add include file serverconf
			for key, value := range includeConf.Server {
				// reduce common setting
				setValue := serverConfigReduct(setCommon, value)
				config.Server[key] = setValue
			}
		}
	}

	// Check Config Parameter
	checkAlertFlag := checkFormatServerConf(config)
	if !checkAlertFlag {
		os.Exit(1)
	}

	return
}

// checkFormatServerConf checkes format of server config.
//
// Note: Checking Addr, User and authentications
// having a value. No checking a validity of each fields.
//
// See also: checkFormatServerConfAuth function.
func checkFormatServerConf(c Config) (isFormat bool) {
	isFormat = true
	for k, v := range c.Server {
		// Address Set Check
		if v.Addr == "" {
			fmt.Printf("%s: 'addr' is not set.\n", k)
			isFormat = false
		}

		// User Set Check
		if v.User == "" {
			fmt.Printf("%s: 'user' is not set.\n", k)
			isFormat = false
		}

		if !checkFormatServerConfAuth(v) {
			fmt.Printf("%s: Authentication information is not set.\n", k)
			isFormat = false
		}
	}
	return
}

// checkFormatServerConfAuth checkes format of server config authentication.
//
// Note: Checking Pass, Key, Cert, AgentAuth, PKCS11Use, PKCS11Provider, Keys or
// Passes having a value. No checking a validity of each fields.
func checkFormatServerConfAuth(c ServerConfig) (isFormat bool) {
	isFormat = false
	if c.Pass != "" || c.Key != "" || c.Cert != "" {
		isFormat = true
	}

	if c.AgentAuth == true {
		isFormat = true
	}

	if c.PKCS11Use == true {
		_, err := os.Stat(c.PKCS11Provider)
		if err == nil {
			isFormat = true
		}
	}

	if len(c.Keys) > 0 || len(c.Passes) > 0 {
		isFormat = true
	}

	return
}

// serverConfigReduct returns a new server config that set perConfig field to
// childConfig empty filed.
func serverConfigReduct(perConfig, childConfig ServerConfig) ServerConfig {
	result := ServerConfig{}

	// struct to map
	perConfigMap, _ := common.StructToMap(&perConfig)
	childConfigMap, _ := common.StructToMap(&childConfig)

	resultMap := common.MapReduce(perConfigMap, childConfigMap)
	_ = common.MapToStruct(resultMap, &result)

	return result
}

// GetNameList return a list of server names from the Config structure.
func GetNameList(listConf Config) (nameList []string) {
	for k := range listConf.Server {
		nameList = append(nameList, k)
	}
	return
}
<|endoftext|>


<commit_before>// Description: conf 项目配置项，也用于读取当前目录下的app.conf中的项目
// Author: ZHU HAIHUA
// Since: 2016-02-26 19:19
package conf

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
)

type Config struct {
	SERVER struct {
		RUNMODE string `yaml:runmode`
		PORT    string `yaml:"port"`
	}

	DATABASE struct {
		HOST     string `yaml:"host"`
		NAME     string `yaml:"name"`
		USER     string `yaml:"user"`
		PASSWORD string `yaml:"password"`
	}

	EXT map[string]interface{} `yaml:"ext,flow"`
}

var (
	Conf = Config{}
)

func init() {
	c, err := ioutil.ReadFile("conf.yml")
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return
	}
	yaml.Unmarshal(c, &Conf)
}
<commit_msg>update<commit_after>// Description: conf 项目配置项，也用于读取当前目录下的app.conf中的项目
// Author: ZHU HAIHUA
// Since: 2016-02-26 19:19
package conf

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
)

type Config struct {
	SERVER struct {
		RUNMODE string `yaml:runmode`
		PORT    string `yaml:"port"`
	}

	DATABASE struct {
		HOST     string `yaml:"host"`
		NAME     string `yaml:"name"`
		USER     string `yaml:"user"`
		PASSWORD string `yaml:"password"`
	}

	EXT map[string]interface{} `yaml:"ext,flow"`
}

var (
	Conf = Config{}
)

func LoadConf(path string) {
	c, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return
	}
	yaml.Unmarshal(c, &Conf)
}

func init() {
	LoadConf("conf.yml")
}
<|endoftext|>


<commit_before>// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 config

import "golang.org/x/sys/windows/registry"

const (
	// envSkipDomainJoinCheck is an environment setting that can be used to skip
	// domain join check validation. This is useful for integration and
	// functional-tests but should not be set for any non-test use-case.
	envSkipDomainJoinCheck  = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION"
	releaseId2004SAC        = "2004"
	releaseId1909SAC        = "1909"
	windowsServer2019       = "Windows Server 2019"
	windowsServer2016       = "Windows Server 2016"
	windowsServerDataCenter = "Windows Server Datacenter"
	installationTypeCore    = "Server Core"
	installationTypeFull    = "Server"
	unsupportedWindowsOS    = "windows"
	osTypeFormat            = "WINDOWS_SERVER_%s_%s"
	ecsWinRegistryRootKey   = registry.LOCAL_MACHINE
	ecsWinRegistryRootPath  = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
)
<commit_msg>Changes to advertise OSType while registering the container instance with cluster<commit_after>// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 config

import "golang.org/x/sys/windows/registry"

const OSType = "windows"
const (
	// envSkipDomainJoinCheck is an environment setting that can be used to skip
	// domain join check validation. This is useful for integration and
	// functional-tests but should not be set for any non-test use-case.
	envSkipDomainJoinCheck  = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION"
	releaseId2004SAC        = "2004"
	releaseId1909SAC        = "1909"
	windowsServer2019       = "Windows Server 2019"
	windowsServer2016       = "Windows Server 2016"
	windowsServerDataCenter = "Windows Server Datacenter"
	installationTypeCore    = "Server Core"
	installationTypeFull    = "Server"
	unsupportedWindowsOS    = "windows"
	osTypeFormat            = "WINDOWS_SERVER_%s_%s"
	ecsWinRegistryRootKey   = registry.LOCAL_MACHINE
	ecsWinRegistryRootPath  = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
)
<|endoftext|>


<commit_before>// ATM - Automatic TempUrl Maker
// A builder of Swift TempURLs
// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/user"

	_ "github.com/go-sql-driver/mysql"

	"github.com/glennsb/atm"
	"github.com/howeyc/gopass"
	"github.com/labstack/echo"
	mw "github.com/labstack/echo/middleware"
)

const (
	HOST     = "https://o3.omrf.org"
	DURATION = int64(300)
)

var (
	Key              string
	Database         string
	Database_host    string
	Database_user    string
	Database_pass    string
	Database_port    int
	Default_duration int64
	Object_host      string
	ds               *atm.Datastore
)

func init() {
	current_user, _ := user.Current()
	Database_user = current_user.Username
	parseFlags()

	fmt.Printf("%s@%s/%s password: ", Database_user, Database_host, Database)
	Database_pass = string(gopass.GetPasswd())
}

func parseFlags() {
	flag.StringVar(&Database, "database", "atm", "Database name")
	flag.StringVar(&Database_host, "database-host", "localhost", "Database server hostname")
	flag.IntVar(&Database_port, "database-port", 3306, "Database server port")
	flag.StringVar(&Database_user, "database-user", Database_user, "Username for database")
	flag.Int64Var(&Default_duration, "duration", DURATION, "Default lifetime of tempurl")
	flag.StringVar(&Object_host, "host", HOST, "Swift host prefix")

	flag.Parse()
}

func keyFinder(a string) (string, error) {
	return ds.ApiKeySecret(a)
}

func main() {
	var err error
	ds, err = atm.NewDatastore("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
		Database_user, Database_pass, Database_host,
		Database_port, Database))
	if nil != err {
		log.Fatal(err)
	}
	Database_pass = ""
	defer ds.Close()

	e := echo.New()

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())
	auth_opts := atm.NewHmacOpts(keyFinder)
	e.Use(atm.HMACAuth(auth_opts))

	e.Post("/urls", createUrl)
	e.Put("/keys/:name", setKey)
	e.Run(":8080")
}

type keyRequest struct {
	Key string `json:key`
}

func setKey(c *echo.Context) error {
	k := &keyRequest{}
	if err := c.Bind(k); nil != err {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg(err.Error()))
	}
	a, err := ds.Account(c.Param("name"))
	if nil != err || a.Id == "" {
		return c.JSON(http.StatusGone, atm.ErrMsg(http.StatusText(http.StatusNotFound)))
	}
	ds.AddSigningKeyForAccount(k.Key, a.Id)
	return c.JSON(http.StatusOK, a)
}

func createUrl(c *echo.Context) error {
	o := &atm.UrlRequest{Host: Object_host, Duration: Default_duration}
	if err := c.Bind(o); nil != err {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg(err.Error()))
	}

	if !o.Valid() {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg("Missing account, container, object, or method"))
	}

	duration := int64(0)
	var err error
	o.Key, duration, err = ds.KeyForRequest(o, c.Request().Header.Get("authorization"))
	if nil != err {
		log.Printf("keyForRequest: %v, %s. Error: %s", o, "", err.Error())
		return c.JSON(http.StatusInternalServerError, atm.ErrMsg("Trouble checking authorization"))
	}
	if "" == o.Key {
		return c.JSON(http.StatusForbidden, atm.ErrMsg("Not authorized for this resource"))
	}
	if duration > 0 && duration > o.Duration {
		o.Duration = duration
	}

	u := &atm.Tmpurl{
		Url:  o.SignedUrl(),
		Path: o.Path(),
	}

	c.Response().Header().Set("Location", u.Url)
	return c.JSON(http.StatusCreated, u)
}
<commit_msg>Only set signing key on your own account from auth<commit_after>// ATM - Automatic TempUrl Maker
// A builder of Swift TempURLs
// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/user"

	_ "github.com/go-sql-driver/mysql"

	"github.com/glennsb/atm"
	"github.com/howeyc/gopass"
	"github.com/labstack/echo"
	mw "github.com/labstack/echo/middleware"
)

const (
	HOST     = "https://o3.omrf.org"
	DURATION = int64(300)
)

var (
	Key              string
	Database         string
	Database_host    string
	Database_user    string
	Database_pass    string
	Database_port    int
	Default_duration int64
	Object_host      string
	ds               *atm.Datastore
)

func init() {
	current_user, _ := user.Current()
	Database_user = current_user.Username
	parseFlags()

	fmt.Printf("%s@%s/%s password: ", Database_user, Database_host, Database)
	Database_pass = string(gopass.GetPasswd())
}

func parseFlags() {
	flag.StringVar(&Database, "database", "atm", "Database name")
	flag.StringVar(&Database_host, "database-host", "localhost", "Database server hostname")
	flag.IntVar(&Database_port, "database-port", 3306, "Database server port")
	flag.StringVar(&Database_user, "database-user", Database_user, "Username for database")
	flag.Int64Var(&Default_duration, "duration", DURATION, "Default lifetime of tempurl")
	flag.StringVar(&Object_host, "host", HOST, "Swift host prefix")

	flag.Parse()
}

func keyFinder(a string) (string, error) {
	return ds.ApiKeySecret(a)
}

func main() {
	var err error
	ds, err = atm.NewDatastore("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
		Database_user, Database_pass, Database_host,
		Database_port, Database))
	if nil != err {
		log.Fatal(err)
	}
	Database_pass = ""
	defer ds.Close()

	e := echo.New()

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())
	auth_opts := atm.NewHmacOpts(keyFinder)
	e.Use(atm.HMACAuth(auth_opts))

	e.Post("/urls", createUrl)
	e.Put("/keys/:name", setKey)
	e.Run(":8080")
}

type keyRequest struct {
	Key string `json:key`
}

func setKey(c *echo.Context) error {
	k := &keyRequest{}
	if err := c.Bind(k); nil != err {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg(err.Error()))
	}
	a, err := ds.Account(c.Param("name"))
	if nil != err || a.Id == "" {
		return c.JSON(http.StatusGone, atm.ErrMsg(http.StatusText(http.StatusNotFound)))
	}
	if c.Get(atm.API_KEY) != a.Id {
		return c.JSON(http.StatusForbidden, atm.ErrMsg("Not authorized for this account"))
	}
	ds.AddSigningKeyForAccount(k.Key, a.Id)
	return c.JSON(http.StatusOK, a)
}

func createUrl(c *echo.Context) error {
	o := &atm.UrlRequest{Host: Object_host, Duration: Default_duration}
	if err := c.Bind(o); nil != err {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg(err.Error()))
	}

	if !o.Valid() {
		return c.JSON(http.StatusBadRequest, atm.ErrMsg("Missing account, container, object, or method"))
	}

	duration := int64(0)
	var err error
	o.Key, duration, err = ds.KeyForRequest(o, c.Request().Header.Get("authorization"))
	if nil != err {
		log.Printf("keyForRequest: %v, %s. Error: %s", o, "", err.Error())
		return c.JSON(http.StatusInternalServerError, atm.ErrMsg("Trouble checking authorization"))
	}
	if "" == o.Key {
		return c.JSON(http.StatusForbidden, atm.ErrMsg("Not authorized for this resource"))
	}
	if duration > 0 && duration > o.Duration {
		o.Duration = duration
	}

	u := &atm.Tmpurl{
		Url:  o.SignedUrl(),
		Path: o.Path(),
	}

	c.Response().Header().Set("Location", u.Url)
	return c.JSON(http.StatusCreated, u)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

var tests = []struct {
	ins  []string
	want []node
}{
	{
		ins:  []string{"", " ", "\n"},
		want: nil,
	},
	{
		ins:  []string{"# foo", "# foo\n"},
		want: []node{
			comment{text: " foo"},
		},
	},
	{
		ins: []string{"foo", "foo ", " foo"},
		want: []node{
			command{args: []lit{"foo"}},
		},
	},
	{
		ins: []string{"foo; bar", "foo; bar;", "\nfoo\nbar\n"},
		want: []node{
			command{args: []lit{"foo"}},
			command{args: []lit{"bar"}},
		},
	},
	{
		ins: []string{"foo a b", " foo  a  b ", "foo \\\n a b"},
		want: []node{
			command{args: []lit{"foo", "a", "b"}},
		},
	},
	{
		ins: []string{"( foo; )", "(foo;)", "(\nfoo\n)"},
		want: []node{
			subshell{stmts: []node{
				command{args: []lit{"foo"}},
			}},
		},
	},
	{
		ins: []string{"{ foo; }", "{foo;}", "{\nfoo\n}"},
		want: []node{
			block{stmts: []node{
				command{args: []lit{"foo"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then b; fi",
			"if a\nthen\nb\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"b"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then b; else c; fi",
			"if a\nthen b\nelse\nc\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"b"}},
			},
			elseStmts: []node{
				command{args: []lit{"c"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then a; elif b; then b; elif c; then c; else d; fi",
			"if a\nthen a\nelif b\nthen b\nelif c\nthen c\nelse\nd\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"a"}},
			},
			elifs: []node{
				elif{cond: command{args: []lit{"b"}},
					thenStmts: []node{
						command{args: []lit{"b"}},
					}},
				elif{cond: command{args: []lit{"c"}},
					thenStmts: []node{
						command{args: []lit{"c"}},
					}},
			},
			elseStmts: []node{
				command{args: []lit{"d"}},
			}},
		},
	},
	{
		ins: []string{"while a; do b; done", "while a\ndo\nb\ndone"},
		want: []node{whileStmt{
			cond: command{args: []lit{"a"}},
			doStmts: []node{
				command{args: []lit{"b"}},
			}},
		},
	},
	{
		ins: []string{"echo ' ' \"foo bar\""},
		want: []node{
			command{args: []lit{"echo", "' '", "\"foo bar\""}},
		},
	},
	{
		ins: []string{"$a ${b} s{s s=s"},
		want: []node{
			command{args: []lit{"$a", "${b}", "s{s", "s=s"}},
		},
	},
	{
		ins: []string{"foo && bar", "foo&&bar"},
		want: []node{binaryExpr{
			op: "&&",
			X:  command{args: []lit{"foo"}},
			Y:  command{args: []lit{"bar"}},
		}},
	},
	{
		ins: []string{"foo || bar", "foo||bar"},
		want: []node{binaryExpr{
			op: "||",
			X:  command{args: []lit{"foo"}},
			Y:  command{args: []lit{"bar"}},
		}},
	},
	{
		ins: []string{"foo && bar || else"},
		want: []node{binaryExpr{
			op: "&&",
			X:  command{args: []lit{"foo"}},
			Y: binaryExpr{
				op: "||",
				X:  command{args: []lit{"bar"}},
				Y:  command{args: []lit{"else"}},
			},
		}},
	},
}

func TestParseAST(t *testing.T) {
	for _, c := range tests {
		want := prog{
			stmts: c.want,
		}
		for _, in := range c.ins {
			r := strings.NewReader(in)
			got, err := parse(r, "")
			if err != nil {
				t.Fatalf("Unexpected error in %q: %v", in, err)
			}
			if !reflect.DeepEqual(got, want) {
				t.Fatalf("AST mismatch in %q\nwant: %s\ngot:  %s\ndumps:\n%#v\n%#v",
					in, want.String(), got.String(), want, got)
			}
		}
	}
}
<commit_msg>Start testing printing of ASTs<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

var tests = []struct {
	ins  []string
	want []node
}{
	{
		ins:  []string{"", " ", "\n"},
		want: nil,
	},
	{
		ins: []string{"# foo", "# foo\n"},
		want: []node{
			comment{text: " foo"},
		},
	},
	{
		ins: []string{"foo", "foo ", " foo"},
		want: []node{
			command{args: []lit{"foo"}},
		},
	},
	{
		ins: []string{"foo; bar", "foo; bar;", "\nfoo\nbar\n"},
		want: []node{
			command{args: []lit{"foo"}},
			command{args: []lit{"bar"}},
		},
	},
	{
		ins: []string{"foo a b", " foo  a  b ", "foo \\\n a b"},
		want: []node{
			command{args: []lit{"foo", "a", "b"}},
		},
	},
	{
		ins: []string{"( foo; )", "(foo;)", "(\nfoo\n)"},
		want: []node{
			subshell{stmts: []node{
				command{args: []lit{"foo"}},
			}},
		},
	},
	{
		ins: []string{"{ foo; }", "{foo;}", "{\nfoo\n}"},
		want: []node{
			block{stmts: []node{
				command{args: []lit{"foo"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then b; fi",
			"if a\nthen\nb\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"b"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then b; else c; fi",
			"if a\nthen b\nelse\nc\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"b"}},
			},
			elseStmts: []node{
				command{args: []lit{"c"}},
			}},
		},
	},
	{
		ins: []string{
			"if a; then a; elif b; then b; elif c; then c; else d; fi",
			"if a\nthen a\nelif b\nthen b\nelif c\nthen c\nelse\nd\nfi",
		},
		want: []node{ifStmt{
			cond: command{args: []lit{"a"}},
			thenStmts: []node{
				command{args: []lit{"a"}},
			},
			elifs: []node{
				elif{cond: command{args: []lit{"b"}},
					thenStmts: []node{
						command{args: []lit{"b"}},
					}},
				elif{cond: command{args: []lit{"c"}},
					thenStmts: []node{
						command{args: []lit{"c"}},
					}},
			},
			elseStmts: []node{
				command{args: []lit{"d"}},
			}},
		},
	},
	{
		ins: []string{"while a; do b; done", "while a\ndo\nb\ndone"},
		want: []node{whileStmt{
			cond: command{args: []lit{"a"}},
			doStmts: []node{
				command{args: []lit{"b"}},
			}},
		},
	},
	{
		ins: []string{"echo ' ' \"foo bar\""},
		want: []node{
			command{args: []lit{"echo", "' '", "\"foo bar\""}},
		},
	},
	{
		ins: []string{"$a ${b} s{s s=s"},
		want: []node{
			command{args: []lit{"$a", "${b}", "s{s", "s=s"}},
		},
	},
	{
		ins: []string{"foo && bar", "foo&&bar"},
		want: []node{binaryExpr{
			op: "&&",
			X:  command{args: []lit{"foo"}},
			Y:  command{args: []lit{"bar"}},
		}},
	},
	{
		ins: []string{"foo || bar", "foo||bar"},
		want: []node{binaryExpr{
			op: "||",
			X:  command{args: []lit{"foo"}},
			Y:  command{args: []lit{"bar"}},
		}},
	},
	{
		ins: []string{"foo && bar || else"},
		want: []node{binaryExpr{
			op: "&&",
			X:  command{args: []lit{"foo"}},
			Y: binaryExpr{
				op: "||",
				X:  command{args: []lit{"bar"}},
				Y:  command{args: []lit{"else"}},
			},
		}},
	},
}

func TestParseAST(t *testing.T) {
	for _, c := range tests {
		want := prog{
			stmts: c.want,
		}
		for _, in := range c.ins {
			r := strings.NewReader(in)
			got, err := parse(r, "")
			if err != nil {
				t.Fatalf("Unexpected error in %q: %v", in, err)
			}
			if !reflect.DeepEqual(got, want) {
				t.Fatalf("AST mismatch in %q\nwant: %s\ngot:  %s\ndumps:\n%#v\n%#v",
					in, want.String(), got.String(), want, got)
			}
		}
	}
}

func TestPrintAST(t *testing.T) {
	for _, c := range tests {
		in := prog{
			stmts: c.want,
		}
		want := c.ins[0]
		got := in.String()
		if got != want {
			t.Fatalf("AST print mismatch\nwant: %s\ngot:  %s",
				want, got)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package configure provides a very simple gnu configure/make style configure
// script generating a simple Makefile and go file containing all the configured
// variables.
package configure

import (
	"bytes"
	"fmt"
	"github.com/jessevdk/go-flags"
	"io"
	"os"
	"path"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"runtime"
)

// Options contains all the standard configure options to specify various
// directories. Use NewOptions to create an instance of this type with the
// common default values for each variable.
type Options struct {
	Prefix        string `long:"prefix" description:"install architecture-independent files in PREFIX"`
	ExecPrefix    string `long:"execprefix" description:"install architecture-dependent files in EPREFIX"`
	BinDir        string `long:"bindir" description:"user executables"`
	LibExecDir    string `long:"libexecdir" description:"program executables"`
	SysConfDir    string `long:"sysconfdir" description:"read-only single-machine data"`
	LibDir        string `long:"libdir" description:"program executables"`
	DataRootDir   string `long:"datarootdir" description:"read-only arch.-independent data root"`
	DataDir       string `long:"datadir" description:"read-only arc.-independent data"`
	ManDir        string `long:"mandir" description:"man documentation"`
}

// NewOptions creates a new Options with common default values.
func NewOptions() *Options {
	return &Options{
		Prefix:        "/usr/local",
		ExecPrefix:    "${prefix}",
		BinDir:        "${execprefix}/bin",
		LibExecDir:    "${execprefix}/libexec",
		LibDir:        "${execprefix}/lib",
		SysConfDir:    "${prefix}/etc",
		DataRootDir:   "${prefix}/share",
		DataDir:       "${datarootdir}",
		ManDir:        "${datarootdir}/man",
	}
}

// Package is the package name in which the GoConfig file will be written
var Package = "main"

// Makefile is the filename of the makefile that will be generated
var Makefile = "go.make"

// GoConfig is the filename of the go file that will be generated containing
// all the variable values.
var GoConfig = "appconfig"

// GoConfigVariable is the name of the variable inside the GoConfig file
// containing all the variable values.
var GoConfigVariable = "AppConfig"

// Target is the executable name to build. If left empty, the name is deduced
// from the directory (similar to what go does)
var Target = ""

// Version is the application version
var Version []int = []int{0, 1}

type expandStringPart struct {
	Value      string
	IsVariable bool
}

func (x *expandStringPart) expand(m map[string]*expandString) (string, []string) {
	if x.IsVariable {
		s, ok := m[x.Value]

		if !ok {
			return "", nil
		} else {
			ret := s.expand(m)
			rets := make([]string, len(s.dependencies), len(s.dependencies)+1)

			copy(rets, s.dependencies)

			return ret, append(rets, x.Value)
		}
	}

	return x.Value, nil
}

type expandString struct {
	Name  string
	Parts []expandStringPart

	dependencies []string
	value        string
	hasExpanded  bool
}

func (x *expandString) dependsOn(name string) bool {
	i := sort.SearchStrings(x.dependencies, name)

	return i < len(x.dependencies) && x.dependencies[i] == name
}

func (x *expandString) expand(m map[string]*expandString) string {
	if !x.hasExpanded {
		// Prevent infinite loop by circular dependencies
		x.hasExpanded = true
		buf := bytes.Buffer{}

		for _, v := range x.Parts {
			s, deps := v.expand(m)
			buf.WriteString(s)

			x.dependencies = append(x.dependencies, deps...)
		}

		sort.Strings(x.dependencies)
		x.value = buf.String()
	}

	return x.value
}

// Config represents the current configuration. See Configure for more
// information.
type Config struct {
	*flags.Parser

	values   map[string]interface{}
	expanded map[string]*expandString
}

func (x *Config) extract() map[string]interface{} {
	ret := make(map[string]interface{})

	for _, grp := range x.Parser.Groups {
		for longname, option := range grp.LongNames {
			ret[longname] = option.Value.Interface()
		}
	}

	return ret
}

func (x *Config) expand() map[string]*expandString {
	ret := make(map[string]*expandString)

	r, _ := regexp.Compile(`\$\{[^}]*\}`)

	for name, val := range x.values {
		es := expandString{
			Name: name,
		}

		// Find all variable references
		s := fmt.Sprintf("%v", val)

		matches := r.FindAllStringIndex(s, -1)

		for i, match := range matches {
			var prefix string

			if i == 0 {
				prefix = s[0:match[0]]
			} else {
				prefix = s[matches[i-1][1]:match[0]]
			}

			if len(prefix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: prefix, IsVariable: false})
			}

			varname := s[match[0]+2 : match[1]-1]
			es.Parts = append(es.Parts, expandStringPart{Value: varname, IsVariable: true})
		}

		if len(matches) == 0 {
			es.Parts = append(es.Parts, expandStringPart{Value: s, IsVariable: false})
		} else {
			last := matches[len(matches)-1]
			suffix := s[last[1]:]

			if len(suffix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: suffix, IsVariable: false})
			}
		}

		ret[name] = &es
	}

	for _, val := range ret {
		val.expand(ret)
	}

	return ret
}

// Configure runs the configure process with options as provided by the given
// data variable. If data is nil, the default options will be used
// (see NewOptions). Note that the data provided is simply passed to go-flags.
// For more information on flags parsing, see the documentation of go-flags.
// If GoConfig is not empty, then the go configuration will be written to the
// GoConfig file. Similarly, if Makefile is not empty, the Makefile will be
// written.
func Configure(data interface{}) (*Config, error) {
	if data == nil {
		data = NewOptions()
	}

	parser := flags.NewParser(data, flags.PrintErrors | flags.IgnoreUnknown)

	if _, err := parser.Parse(); err != nil {
		return nil, err
	}

	ret := &Config{
		Parser: parser,
	}

	ret.values = ret.extract()
	ret.expanded = ret.expand()

	if len(GoConfig) != 0 {
		filename := GoConfig

		if !strings.HasSuffix(filename, ".go") {
			filename += ".go"
		}

		f, err := os.Create(filename)

		if err != nil {
			return nil, err
		}

		ret.WriteGoConfig(f)
		f.Close()
	}

	if len(Makefile) != 0 {
		f, err := os.Create(Makefile)

		if err != nil {
			return nil, err
		}

		ret.WriteMakefile(f)
		f.Close()

		os.Chmod(Makefile, 0755)

		f, err = os.OpenFile(path.Join(path.Dir(Makefile), "Makefile"),
		                     os.O_CREATE | os.O_EXCL | os.O_WRONLY,
		                     0644)

		if err == nil {
			fmt.Fprintf(f, "include %s\n", path.Base(Makefile))
			f.Close()
		}
	}

	return ret, nil
}

// Expand expands the variable value indicated by name
func (x *Config) Expand(name string) string {
	return x.expanded[name].expand(x.expanded)
}

// WriteGoConfig writes the go configuration file containing all the variable
// values to the given writer. Note that it will write a package line if
// the Package variable is not empty. The GoConfigVariable name will
// be used as the variable name for the configuration.
func (x *Config) WriteGoConfig(writer io.Writer) {
	if len(Package) > 0 {
		fmt.Fprintf(writer, "package %v\n\n", Package)
	}

	fmt.Fprintf(writer, "var %s = struct {\n", GoConfigVariable)
	values := make([]string, 0)

	variables := make([]string, 0, len(x.values))
	optionmap := make(map[string]*flags.Option)

	// Write all options
	for _, grp := range x.Parser.Groups {
		for _, option := range grp.LongNames {
			name := option.Field.Name

			variables = append(variables, name)
			optionmap[name] = option
		}
	}

	sort.Strings(variables)

	for i, name := range variables {
		if i != 0 {
			io.WriteString(writer, "\n")
		}

		option := optionmap[name]
		val := option.Value.Interface()

		fmt.Fprintf(writer, "\t// %s\n", option.Description)
		fmt.Fprintf(writer, "\t%v %T\n", name, val)

		var value string

		if option.Value.Type().Kind() == reflect.String {
			value = fmt.Sprintf("%#v", x.Expand(option.LongName))
		} else {
			value = fmt.Sprintf("%#v", val)
		}

		values = append(values, value)
	}

	if len(variables) > 0 {
		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "\t// Application version\n")
	io.WriteString(writer, "\tVersion []int\n")
	fmt.Fprintln(writer, "}{")

	for _, v := range values {
		fmt.Fprintf(writer, "\t%v,\n", v)
	}

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ", ")
		} else {
			io.WriteString(writer, "\t[]int{")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	fmt.Fprintln(writer, "},")
	fmt.Fprintln(writer, "}")
}

// WriteMakefile writes a Makefile for the given parser to the given writer.
// The Makefile contains the common build, clean, distclean, install and
// uninstall rules.
func (x *Config) WriteMakefile(writer io.Writer) {
	// Write a very basic makefile
	io.WriteString(writer, "#!/usr/bin/make -f\n\n")

	vars := make([]*expandString, 0, len(x.expanded))

	for name, v := range x.expanded {
		inserted := false

		// Insert into vars based on dependencies
		for i, vv := range vars {
			if vv.dependsOn(name) {
				tail := make([]*expandString, len(vars)-i)
				copy(tail, vars[i:])

				if i == 0 {
					vars = append([]*expandString{v}, vars...)
				} else {
					vars = append(append(vars[0:i], v), tail...)
				}

				inserted = true
				break
			}
		}

		if !inserted {
			vars = append(vars, v)
		}
	}

	io.WriteString(writer, "# Variables\n")

	for _, v := range vars {
		fmt.Fprintf(writer, "%s = ", v.Name)

		for _, part := range v.Parts {
			if part.IsVariable {
				fmt.Fprintf(writer, "$(%s)", part.Value)
			} else {
				fmt.Fprintf(writer, "%s", part.Value)
			}
		}

		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "version = ")

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ".")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	io.WriteString(writer, "\n")
	fmt.Fprintf(writer, "major_version = %v\n", Version[0])

	if len(Version) > 1 {
		fmt.Fprintf(writer, "minor_version = %v\n", Version[1])
	}

	if len(Version) > 2 {
		fmt.Fprintf(writer, "micro_version = %v\n", Version[2])
	}

	io.WriteString(writer, "\n")

	target := Target

	if len(target) == 0 {
		pc := make([]uintptr, 3)
		n := runtime.Callers(1, pc)

		me, _ := runtime.FuncForPC(pc[0]).FileLine(pc[0])

		for i := 1; i < n; i++ {
			f := runtime.FuncForPC(pc[i])
			fname, _ := f.FileLine(pc[i])

			if fname != me {
				target = path.Base(path.Dir(fname))
				break
			}
		}
	}

	fmt.Fprintf(writer, "TARGET = %s\n", target)

	io.WriteString(writer, "\nSOURCES ?=")
	io.WriteString(writer, "\nSOURCES += $(wildcard *.go)")
	io.WriteString(writer, "\nSOURCES_UNIQUE = $(sort $(SOURCES))")

	io.WriteString(writer, "\n\n")

	io.WriteString(writer, "# Rules\n")
	io.WriteString(writer, "$(TARGET): $(SOURCES_UNIQUE)\n")
	io.WriteString(writer, "\tgo build -o $@\n\n")

	io.WriteString(writer, "clean:\n")
	io.WriteString(writer, "\trm -f $(TARGET)\n\n")

	io.WriteString(writer, "distclean: clean\n\n")

	io.WriteString(writer, "install: $(TARGET)\n")
	io.WriteString(writer, "\tmkdir -p $(DESTDIR)$(bindir) && cp $(TARGET) $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, "uninstall:\n")
	io.WriteString(writer, "\trm -f $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, ".PHONY: install uninstall distclean clean")
}
<commit_msg>Make all variables optional<commit_after>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package configure provides a very simple gnu configure/make style configure
// script generating a simple Makefile and go file containing all the configured
// variables.
package configure

import (
	"bytes"
	"fmt"
	"github.com/jessevdk/go-flags"
	"io"
	"os"
	"path"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"runtime"
)

// Options contains all the standard configure options to specify various
// directories. Use NewOptions to create an instance of this type with the
// common default values for each variable.
type Options struct {
	Prefix        string `long:"prefix" description:"install architecture-independent files in PREFIX"`
	ExecPrefix    string `long:"execprefix" description:"install architecture-dependent files in EPREFIX"`
	BinDir        string `long:"bindir" description:"user executables"`
	LibExecDir    string `long:"libexecdir" description:"program executables"`
	SysConfDir    string `long:"sysconfdir" description:"read-only single-machine data"`
	LibDir        string `long:"libdir" description:"program executables"`
	DataRootDir   string `long:"datarootdir" description:"read-only arch.-independent data root"`
	DataDir       string `long:"datadir" description:"read-only arc.-independent data"`
	ManDir        string `long:"mandir" description:"man documentation"`
}

// NewOptions creates a new Options with common default values.
func NewOptions() *Options {
	return &Options{
		Prefix:        "/usr/local",
		ExecPrefix:    "${prefix}",
		BinDir:        "${execprefix}/bin",
		LibExecDir:    "${execprefix}/libexec",
		LibDir:        "${execprefix}/lib",
		SysConfDir:    "${prefix}/etc",
		DataRootDir:   "${prefix}/share",
		DataDir:       "${datarootdir}",
		ManDir:        "${datarootdir}/man",
	}
}

// Package is the package name in which the GoConfig file will be written
var Package = "main"

// Makefile is the filename of the makefile that will be generated
var Makefile = "go.make"

// GoConfig is the filename of the go file that will be generated containing
// all the variable values.
var GoConfig = "appconfig"

// GoConfigVariable is the name of the variable inside the GoConfig file
// containing all the variable values.
var GoConfigVariable = "AppConfig"

// Target is the executable name to build. If left empty, the name is deduced
// from the directory (similar to what go does)
var Target = ""

// Version is the application version
var Version []int = []int{0, 1}

type expandStringPart struct {
	Value      string
	IsVariable bool
}

func (x *expandStringPart) expand(m map[string]*expandString) (string, []string) {
	if x.IsVariable {
		s, ok := m[x.Value]

		if !ok {
			return "", nil
		} else {
			ret := s.expand(m)
			rets := make([]string, len(s.dependencies), len(s.dependencies)+1)

			copy(rets, s.dependencies)

			return ret, append(rets, x.Value)
		}
	}

	return x.Value, nil
}

type expandString struct {
	Name  string
	Parts []expandStringPart

	dependencies []string
	value        string
	hasExpanded  bool
}

func (x *expandString) dependsOn(name string) bool {
	i := sort.SearchStrings(x.dependencies, name)

	return i < len(x.dependencies) && x.dependencies[i] == name
}

func (x *expandString) expand(m map[string]*expandString) string {
	if !x.hasExpanded {
		// Prevent infinite loop by circular dependencies
		x.hasExpanded = true
		buf := bytes.Buffer{}

		for _, v := range x.Parts {
			s, deps := v.expand(m)
			buf.WriteString(s)

			x.dependencies = append(x.dependencies, deps...)
		}

		sort.Strings(x.dependencies)
		x.value = buf.String()
	}

	return x.value
}

// Config represents the current configuration. See Configure for more
// information.
type Config struct {
	*flags.Parser

	values   map[string]interface{}
	expanded map[string]*expandString
}

func (x *Config) extract() map[string]interface{} {
	ret := make(map[string]interface{})

	for _, grp := range x.Parser.Groups {
		for longname, option := range grp.LongNames {
			ret[longname] = option.Value.Interface()
		}
	}

	return ret
}

func (x *Config) expand() map[string]*expandString {
	ret := make(map[string]*expandString)

	r, _ := regexp.Compile(`\$\{[^}]*\}`)

	for name, val := range x.values {
		es := expandString{
			Name: name,
		}

		// Find all variable references
		s := fmt.Sprintf("%v", val)

		matches := r.FindAllStringIndex(s, -1)

		for i, match := range matches {
			var prefix string

			if i == 0 {
				prefix = s[0:match[0]]
			} else {
				prefix = s[matches[i-1][1]:match[0]]
			}

			if len(prefix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: prefix, IsVariable: false})
			}

			varname := s[match[0]+2 : match[1]-1]
			es.Parts = append(es.Parts, expandStringPart{Value: varname, IsVariable: true})
		}

		if len(matches) == 0 {
			es.Parts = append(es.Parts, expandStringPart{Value: s, IsVariable: false})
		} else {
			last := matches[len(matches)-1]
			suffix := s[last[1]:]

			if len(suffix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: suffix, IsVariable: false})
			}
		}

		ret[name] = &es
	}

	for _, val := range ret {
		val.expand(ret)
	}

	return ret
}

// Configure runs the configure process with options as provided by the given
// data variable. If data is nil, the default options will be used
// (see NewOptions). Note that the data provided is simply passed to go-flags.
// For more information on flags parsing, see the documentation of go-flags.
// If GoConfig is not empty, then the go configuration will be written to the
// GoConfig file. Similarly, if Makefile is not empty, the Makefile will be
// written.
func Configure(data interface{}) (*Config, error) {
	if data == nil {
		data = NewOptions()
	}

	parser := flags.NewParser(data, flags.PrintErrors | flags.IgnoreUnknown)

	if _, err := parser.Parse(); err != nil {
		return nil, err
	}

	ret := &Config{
		Parser: parser,
	}

	ret.values = ret.extract()
	ret.expanded = ret.expand()

	if len(GoConfig) != 0 {
		filename := GoConfig

		if !strings.HasSuffix(filename, ".go") {
			filename += ".go"
		}

		f, err := os.Create(filename)

		if err != nil {
			return nil, err
		}

		ret.WriteGoConfig(f)
		f.Close()
	}

	if len(Makefile) != 0 {
		f, err := os.Create(Makefile)

		if err != nil {
			return nil, err
		}

		ret.WriteMakefile(f)
		f.Close()

		os.Chmod(Makefile, 0755)

		f, err = os.OpenFile(path.Join(path.Dir(Makefile), "Makefile"),
		                     os.O_CREATE | os.O_EXCL | os.O_WRONLY,
		                     0644)

		if err == nil {
			fmt.Fprintf(f, "include %s\n", path.Base(Makefile))
			f.Close()
		}
	}

	return ret, nil
}

// Expand expands the variable value indicated by name
func (x *Config) Expand(name string) string {
	return x.expanded[name].expand(x.expanded)
}

// WriteGoConfig writes the go configuration file containing all the variable
// values to the given writer. Note that it will write a package line if
// the Package variable is not empty. The GoConfigVariable name will
// be used as the variable name for the configuration.
func (x *Config) WriteGoConfig(writer io.Writer) {
	if len(Package) > 0 {
		fmt.Fprintf(writer, "package %v\n\n", Package)
	}

	fmt.Fprintf(writer, "var %s = struct {\n", GoConfigVariable)
	values := make([]string, 0)

	variables := make([]string, 0, len(x.values))
	optionmap := make(map[string]*flags.Option)

	// Write all options
	for _, grp := range x.Parser.Groups {
		for _, option := range grp.LongNames {
			name := option.Field.Name

			variables = append(variables, name)
			optionmap[name] = option
		}
	}

	sort.Strings(variables)

	for i, name := range variables {
		if i != 0 {
			io.WriteString(writer, "\n")
		}

		option := optionmap[name]
		val := option.Value.Interface()

		fmt.Fprintf(writer, "\t// %s\n", option.Description)
		fmt.Fprintf(writer, "\t%v %T\n", name, val)

		var value string

		if option.Value.Type().Kind() == reflect.String {
			value = fmt.Sprintf("%#v", x.Expand(option.LongName))
		} else {
			value = fmt.Sprintf("%#v", val)
		}

		values = append(values, value)
	}

	if len(variables) > 0 {
		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "\t// Application version\n")
	io.WriteString(writer, "\tVersion []int\n")
	fmt.Fprintln(writer, "}{")

	for _, v := range values {
		fmt.Fprintf(writer, "\t%v,\n", v)
	}

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ", ")
		} else {
			io.WriteString(writer, "\t[]int{")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	fmt.Fprintln(writer, "},")
	fmt.Fprintln(writer, "}")
}

// WriteMakefile writes a Makefile for the given parser to the given writer.
// The Makefile contains the common build, clean, distclean, install and
// uninstall rules.
func (x *Config) WriteMakefile(writer io.Writer) {
	// Write a very basic makefile
	io.WriteString(writer, "#!/usr/bin/make -f\n\n")

	vars := make([]*expandString, 0, len(x.expanded))

	for name, v := range x.expanded {
		inserted := false

		// Insert into vars based on dependencies
		for i, vv := range vars {
			if vv.dependsOn(name) {
				tail := make([]*expandString, len(vars)-i)
				copy(tail, vars[i:])

				if i == 0 {
					vars = append([]*expandString{v}, vars...)
				} else {
					vars = append(append(vars[0:i], v), tail...)
				}

				inserted = true
				break
			}
		}

		if !inserted {
			vars = append(vars, v)
		}
	}

	io.WriteString(writer, "# Variables\n")

	for _, v := range vars {
		fmt.Fprintf(writer, "%s ?= ", v.Name)

		for _, part := range v.Parts {
			if part.IsVariable {
				fmt.Fprintf(writer, "$(%s)", part.Value)
			} else {
				fmt.Fprintf(writer, "%s", part.Value)
			}
		}

		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "version ?= ")

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ".")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	io.WriteString(writer, "\n")
	fmt.Fprintf(writer, "major_version = %v\n", Version[0])

	if len(Version) > 1 {
		fmt.Fprintf(writer, "minor_version = %v\n", Version[1])
	}

	if len(Version) > 2 {
		fmt.Fprintf(writer, "micro_version = %v\n", Version[2])
	}

	io.WriteString(writer, "\n")

	target := Target

	if len(target) == 0 {
		pc := make([]uintptr, 3)
		n := runtime.Callers(1, pc)

		me, _ := runtime.FuncForPC(pc[0]).FileLine(pc[0])

		for i := 1; i < n; i++ {
			f := runtime.FuncForPC(pc[i])
			fname, _ := f.FileLine(pc[i])

			if fname != me {
				target = path.Base(path.Dir(fname))
				break
			}
		}
	}

	fmt.Fprintf(writer, "TARGET ?= %s\n", target)

	io.WriteString(writer, "\nSOURCES ?=")
	io.WriteString(writer, "\nSOURCES += $(wildcard *.go)")
	io.WriteString(writer, "\nSOURCES_UNIQUE = $(sort $(SOURCES))")

	io.WriteString(writer, "\n\n")

	io.WriteString(writer, "# Rules\n")
	io.WriteString(writer, "$(TARGET): $(SOURCES_UNIQUE)\n")
	io.WriteString(writer, "\tgo build -o $@\n\n")

	io.WriteString(writer, "clean:\n")
	io.WriteString(writer, "\trm -f $(TARGET)\n\n")

	io.WriteString(writer, "distclean: clean\n\n")

	io.WriteString(writer, "install: $(TARGET)\n")
	io.WriteString(writer, "\tmkdir -p $(DESTDIR)$(bindir) && cp $(TARGET) $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, "uninstall:\n")
	io.WriteString(writer, "\trm -f $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, ".PHONY: install uninstall distclean clean")
}
<|endoftext|>


<commit_before>package axe

import (
	"reflect"
	"time"

	"github.com/globalsign/mgo/bson"
)

// Error is used to signal failed job executions.
type Error struct {
	Reason string
	Retry  bool
}

// Error implements the error interface.
func (c *Error) Error() string {
	return c.Reason
}

// E is a short-hand to construct an error.
func E(reason string, retry bool) *Error {
	return &Error{
		Reason: reason,
		Retry:  retry,
	}
}

// Model can be any BSON serializable type.
type Model interface{}

// Task is task that is executed asynchronously.
type Task struct {
	// Name is the unique name of the task.
	Name string

	// Model is the model that holds task related data.
	Model Model

	// Queue is the queue that is used to managed the jobs.
	Queue *Queue

	// Handler is the callback called with tasks.
	Handler func(Model) (bson.M, error)

	// Workers defines the number for spawned workers.
	//
	// Default: 1.
	Workers int

	// MaxAttempts defines the maximum attempts to complete a task.
	//
	// Default: 1
	MaxAttempts int

	// Interval is interval at which the worker will request a job from the queue.
	//
	// Default: 100ms.
	Interval time.Duration
}

func (t *Task) start(p *Pool) {
	// set default workers
	if t.Workers == 0 {
		t.Workers = 1
	}

	// set default max attempts
	if t.MaxAttempts == 0 {
		t.MaxAttempts = 1
	}

	// set default interval
	if t.Interval == 0 {
		t.Interval = 100 * time.Millisecond
	}

	// start workers
	for i := 0; i < t.Workers; i++ {
		go t.worker(p)
	}
}

func (t *Task) worker(p *Pool) {
	// run forever
	for {
		// return if closed
		select {
		case <-p.closed:
			return
		default:
		}

		// attempt to get job from queue
		job := t.Queue.get(t.Name)
		if job == nil {
			// wait some time and try again
			time.Sleep(t.Interval)

			continue
		}

		// execute worker and report errors
		err := t.execute(job)
		if err != nil {
			if p.Reporter != nil {
				p.Reporter(err)
			}
		}
	}
}

func (t *Task) execute(job *Job) error {
	// get store
	store := t.Queue.Store.Copy()
	defer store.Close()

	// dequeue job
	job, err := dequeue(store, job.ID(), time.Hour)
	if err != nil {
		return err
	}

	// return if missing
	if job == nil {
		return nil
	}

	// instantiate model
	data := reflect.New(reflect.TypeOf(t.Model).Elem()).Interface()

	// unmarshal data
	err = job.Data.Unmarshal(data)
	if err != nil {
		return err
	}

	// start handler
	result, err := t.Handler(data)
	if _, ok := err.(*Error); !ok && err != nil {
		return err
	}

	// check error
	if e, ok := err.(*Error); ok {
		// check retry and attempts
		if !e.Retry || job.Attempts >= t.MaxAttempts {
			// cancel job
			err = cancel(store, job.ID(), e.Reason)
			if err != nil {
				return err
			}

			return nil
		}

		// fail job
		err = fail(store, job.ID(), e.Reason, 0)
		if err != nil {
			return err
		}
	}

	// complete job
	err = complete(store, job.ID(), result)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>make delay configurable<commit_after>package axe

import (
	"reflect"
	"time"

	"github.com/globalsign/mgo/bson"
)

// Error is used to signal failed job executions.
type Error struct {
	Reason string
	Retry  bool
}

// Error implements the error interface.
func (c *Error) Error() string {
	return c.Reason
}

// E is a short-hand to construct an error.
func E(reason string, retry bool) *Error {
	return &Error{
		Reason: reason,
		Retry:  retry,
	}
}

// Model can be any BSON serializable type.
type Model interface{}

// Task is task that is executed asynchronously.
type Task struct {
	// Name is the unique name of the task.
	Name string

	// Model is the model that holds task related data.
	Model Model

	// Queue is the queue that is used to managed the jobs.
	Queue *Queue

	// Handler is the callback called with tasks.
	Handler func(Model) (bson.M, error)

	// Workers defines the number for spawned workers.
	//
	// Default: 1.
	Workers int

	// MaxAttempts defines the maximum attempts to complete a task.
	//
	// Default: 1
	MaxAttempts int

	// Interval is interval at which the worker will request a job from the queue.
	//
	// Default: 100ms.
	Interval time.Duration

	// Delay is the time after a failed task is retried.
	//
	// Default: 1s.
	Delay time.Duration
}

func (t *Task) start(p *Pool) {
	// set default workers
	if t.Workers == 0 {
		t.Workers = 1
	}

	// set default max attempts
	if t.MaxAttempts == 0 {
		t.MaxAttempts = 1
	}

	// set default interval
	if t.Interval == 0 {
		t.Interval = 100 * time.Millisecond
	}

	// set default delay
	if t.Delay == 0 {
		t.Delay = time.Second
	}

	// start workers
	for i := 0; i < t.Workers; i++ {
		go t.worker(p)
	}
}

func (t *Task) worker(p *Pool) {
	// run forever
	for {
		// return if closed
		select {
		case <-p.closed:
			return
		default:
		}

		// attempt to get job from queue
		job := t.Queue.get(t.Name)
		if job == nil {
			// wait some time and try again
			time.Sleep(t.Interval)

			continue
		}

		// execute worker and report errors
		err := t.execute(job)
		if err != nil {
			if p.Reporter != nil {
				p.Reporter(err)
			}
		}
	}
}

func (t *Task) execute(job *Job) error {
	// get store
	store := t.Queue.Store.Copy()
	defer store.Close()

	// dequeue job
	job, err := dequeue(store, job.ID(), time.Hour)
	if err != nil {
		return err
	}

	// return if missing
	if job == nil {
		return nil
	}

	// instantiate model
	data := reflect.New(reflect.TypeOf(t.Model).Elem()).Interface()

	// unmarshal data
	err = job.Data.Unmarshal(data)
	if err != nil {
		return err
	}

	// start handler
	result, err := t.Handler(data)
	if _, ok := err.(*Error); !ok && err != nil {
		return err
	}

	// check error
	if e, ok := err.(*Error); ok {
		// check retry and attempts
		if !e.Retry || job.Attempts >= t.MaxAttempts {
			// cancel job
			err = cancel(store, job.ID(), e.Reason)
			if err != nil {
				return err
			}

			return nil
		}

		// fail job
		err = fail(store, job.ID(), e.Reason, t.Delay)
		if err != nil {
			return err
		}
	}

	// complete job
	err = complete(store, job.ID(), result)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package http2

import (
	"bytes"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func BenchmarkConnReadWriteC1_1K(b *testing.B) {
	benchmark(b, 1, 1024)
}

func BenchmarkConnReadWriteC8_1K(b *testing.B) {
	benchmark(b, 8, 1024)
}

func BenchmarkConnReadWriteC64_1K(b *testing.B) {
	benchmark(b, 64, 1024)
}

func BenchmarkConnReadWriteC512_1K(b *testing.B) {
	benchmark(b, 512, 1024)
}

func benchmark(b *testing.B, c, n int) {
	sc, cc := pipe(true)
	server, client := &conn{Conn: sc, pending: map[uint32]int64{}}, &conn{Conn: cc, pending: map[uint32]int64{}}
	go server.serve()
	go client.serve()
	ch := make(chan int, c*4)
	var wg sync.WaitGroup
	for i := 0; i < c; i++ {
		wg.Add(1)
		go func() {
			for range ch {
				streamID, err := client.NextStreamID()
				if err != nil {
					b.Fatal(err)
				}
				err = client.writeBytes(streamID, n)
				if err != nil {
					b.Fatal(err)
				}
			}
			wg.Done()
		}()
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		ch <- i
	}
	b.StopTimer()
	close(ch)
	wg.Wait()
	if err := client.Close(); err != nil {
		b.Fatal(err)
	}
	if err := server.Close(); err != nil {
		b.Fatal(err)
	}
	if atomic.LoadInt64(&client.tx) != server.rx {
		b.Fatal("lost data")
	}
	if atomic.LoadInt64(&server.tx) != client.rx {
		b.Fatal("lost data")
	}
	if int64(b.N*n) != client.rx {
		b.Fatal("lost data")
	}
}

type conn struct {
	*Conn
	rx, tx  int64
	rb      bytes.Buffer
	pending map[uint32]int64
}

func (c *conn) serve() {
	for !c.Closed() {
		frame, err := c.ReadFrame()
		if err != nil {
			return
		}
		var endStream bool
		switch v := frame.(type) {
		case *DataFrame:
			c.rb.Reset()
			var n int64
			n, err = c.rb.ReadFrom(v.Data)
			c.rx += n
			c.pending[v.StreamID] += n
			if err != nil {
				return
			}
			endStream = v.EndStream
		case *HeadersFrame:
			endStream = v.EndStream
		}
		if endStream && c.server {
			go c.writeBytes(frame.streamID(), int(c.pending[frame.streamID()]))
		}
	}
}

func (c *conn) writeBytes(streamID uint32, n int) (err error) {
	if streamID == 0 {
		if streamID, err = c.NextStreamID(); err != nil {
			return
		}
	}
	err = c.WriteFrame(&HeadersFrame{streamID, nil, Priority{}, 0, n == 0})
	if n > 0 && err == nil {
		if err = c.WriteFrame(&DataFrame{streamID, bytes.NewBuffer(make([]byte, n)), n, 0, true}); err == nil {
			atomic.AddInt64(&c.tx, int64(n))
		}
	}
	return
}

func pipe(tcp bool) (server *Conn, client *Conn) {
	if tcp {
		done := make(chan struct{})
		addr := &net.TCPAddr{Port: 8989}
		for {
			lis, err := net.ListenTCP("tcp", addr)
			if err != nil {
				if addr.Port > 65535 {
					panic(err)
				}
				addr.Port++
				continue
			}
			lis.SetDeadline(time.Now().Add(300 * time.Millisecond))
			go func() {
				s, err := lis.AcceptTCP()
				if err != nil {
					panic(err)
				}
				s.SetNoDelay(true)
				server = NewConn(s, true)
				lis.Close()
				close(done)
			}()
			break
		}
		c, err := net.DialTCP("tcp", nil, addr)
		if err != nil {
			panic(err)
		}
		c.SetNoDelay(true)
		client = NewConn(c, false)
		<-done
	} else {
		type rwc struct {
			io.Reader
			io.Writer
			io.Closer
		}
		sr, cw := io.Pipe()
		cr, sw := io.Pipe()
		server = NewConn(&rwc{sr, sw, sw}, true)
		client = NewConn(&rwc{cr, cw, cw}, false)
	}
	return
}
<commit_msg>wait a little until the loop is stopped<commit_after>package http2

import (
	"bytes"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func Test(t *testing.T) {
}

func BenchmarkConnReadWriteC1_1K(b *testing.B) {
	benchmark(b, 1, 1024)
}

func BenchmarkConnReadWriteC8_1K(b *testing.B) {
	benchmark(b, 8, 1024)
}

func BenchmarkConnReadWriteC64_1K(b *testing.B) {
	benchmark(b, 64, 1024)
}

func BenchmarkConnReadWriteC512_1K(b *testing.B) {
	benchmark(b, 512, 1024)
}

func benchmark(b *testing.B, c, n int) {
	sc, cc := pipe(true)
	server, client := &conn{Conn: sc, pending: map[uint32]int64{}}, &conn{Conn: cc, pending: map[uint32]int64{}}
	go server.serve()
	go client.serve()
	ch := make(chan int, c*4)
	var wg sync.WaitGroup
	for i := 0; i < c; i++ {
		wg.Add(1)
		go func() {
			for range ch {
				streamID, err := client.NextStreamID()
				if err != nil {
					b.Fatal(err)
				}
				err = client.writeBytes(streamID, n)
				if err != nil {
					b.Fatal(err)
				}
			}
			wg.Done()
		}()
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		ch <- i
	}
	b.StopTimer()
	close(ch)
	wg.Wait()
	time.Sleep(100 * time.Millisecond)
	if err := client.Close(); err != nil {
		b.Fatal(err)
	}
	if err := server.Close(); err != nil {
		b.Fatal(err)
	}
	if atomic.LoadInt64(&client.tx) != server.rx {
		b.Fatal("lost data")
	}
	if atomic.LoadInt64(&server.tx) != client.rx {
		b.Fatal("lost data")
	}
	if int64(b.N*n) != client.rx {
		b.Fatal("lost data")
	}
}

type conn struct {
	*Conn
	rx, tx  int64
	rb      bytes.Buffer
	pending map[uint32]int64
}

func (c *conn) serve() {
	for !c.Closed() {
		frame, err := c.ReadFrame()
		if err != nil {
			return
		}
		var endStream bool
		switch v := frame.(type) {
		case *DataFrame:
			c.rb.Reset()
			var n int64
			n, err = c.rb.ReadFrom(v.Data)
			c.rx += n
			c.pending[v.StreamID] += n
			if err != nil {
				return
			}
			endStream = v.EndStream
		case *HeadersFrame:
			endStream = v.EndStream
		}
		if endStream && c.server {
			go c.writeBytes(frame.streamID(), int(c.pending[frame.streamID()]))
		}
	}
}

func (c *conn) writeBytes(streamID uint32, n int) (err error) {
	if streamID == 0 {
		if streamID, err = c.NextStreamID(); err != nil {
			return
		}
	}
	err = c.WriteFrame(&HeadersFrame{streamID, nil, Priority{}, 0, n == 0})
	if n > 0 && err == nil {
		if err = c.WriteFrame(&DataFrame{streamID, bytes.NewBuffer(make([]byte, n)), n, 0, true}); err == nil {
			atomic.AddInt64(&c.tx, int64(n))
		}
	}
	return
}

func pipe(tcp bool) (server *Conn, client *Conn) {
	if tcp {
		done := make(chan struct{})
		addr := &net.TCPAddr{Port: 8989}
		for {
			lis, err := net.ListenTCP("tcp", addr)
			if err != nil {
				if addr.Port > 65535 {
					panic(err)
				}
				addr.Port++
				continue
			}
			lis.SetDeadline(time.Now().Add(300 * time.Millisecond))
			go func() {
				s, err := lis.AcceptTCP()
				if err != nil {
					panic(err)
				}
				s.SetNoDelay(true)
				server = NewConn(s, true)
				lis.Close()
				close(done)
			}()
			break
		}
		c, err := net.DialTCP("tcp", nil, addr)
		if err != nil {
			panic(err)
		}
		c.SetNoDelay(true)
		client = NewConn(c, false)
		<-done
	} else {
		type rwc struct {
			io.Reader
			io.Writer
			io.Closer
		}
		sr, cw := io.Pipe()
		cr, sw := io.Pipe()
		server = NewConn(&rwc{sr, sw, sw}, true)
		client = NewConn(&rwc{cr, cw, cw}, false)
	}
	return
}
<|endoftext|>


<commit_before>// Package gpio implements a general purpose digital I/O port.
// Works with:
//   ATmega48/88/168
package gpio

import (
    "fmt"
    "github.com/kierdavis/avr/emulator"
)

// TODO: pullup-disable bit in MCUCR (see http://www.atmel.com/Images/doc2545.pdf, page 87)

// Values in DDR register
const (
    Input = 0
    Output = 1
)

type GPIO struct {
    letter byte
    width uint
    dirs uint8
    outputs uint8
    pullups uint8
    inputAdapters [8]InputPinAdapter
    outputAdapters [8]OutputPinAdapter
}

func New(portLetter byte, width uint) (g *GPIO) {
    return &GPIO{
        letter: portLetter,
        width: width,
    }
}

func (g *GPIO) SetInputAdapter(pinNumber uint, adapter InputPinAdapter) {
    g.inputAdapters[pinNumber] = adapter
}

func (g *GPIO) SetOutputAdapter(pinNumber uint, adapter OutputPinAdapter) {
    g.outputAdapters[pinNumber] = adapter
}

func (g *GPIO) AddTo(em *emulator.Emulator) {
    em.RegisterPortByName(fmt.Sprintf("PORT%c", g.letter), port{g})
    em.RegisterPortByName(fmt.Sprintf("DDR%c", g.letter), ddr{g})
    em.RegisterPortByName(fmt.Sprintf("PIN%c", g.letter), pin{g})
}

// called when a PIN port is read
func (g *GPIO) getInputs() (x uint8) {
    if g.getInput(7) {x |= 0x80}
    if g.getInput(6) {x |= 0x40}
    if g.getInput(5) {x |= 0x20}
    if g.getInput(4) {x |= 0x10}
    if g.getInput(3) {x |= 0x08}
    if g.getInput(2) {x |= 0x04}
    if g.getInput(1) {x |= 0x02}
    if g.getInput(0) {x |= 0x01}
    return x
}

func (g *GPIO) getInput(pinNumber uint) bool {
    adapter := g.inputAdapters[pinNumber]
    if adapter != nil {
        return adapter.GetState()
    } else {
        return false
    }
}

// called when a PORT/DDR/PIN port is written
func (g *GPIO) updateOutputs(changed uint8) {
    if changed & 0x80 != 0 {g.updateOutput(7)}
    if changed & 0x40 != 0 {g.updateOutput(6)}
    if changed & 0x20 != 0 {g.updateOutput(5)}
    if changed & 0x10 != 0 {g.updateOutput(4)}
    if changed & 0x08 != 0 {g.updateOutput(3)}
    if changed & 0x04 != 0 {g.updateOutput(2)}
    if changed & 0x02 != 0 {g.updateOutput(1)}
    if changed & 0x01 != 0 {g.updateOutput(0)}
}

func (g *GPIO) updateOutput(pinNumber uint) {
    if pinNumber < g.width { // ignore out-of-range pins
        if (g.dirs >> pinNumber) & 1 == Input {
            adapter := g.inputAdapters[pinNumber]
            if adapter != nil {
                adapter.SetPullupEnabled((g.pullups >> pinNumber) & 1 != 0)
            }

        } else {
            adapter := g.outputAdapters[pinNumber]
            if adapter != nil {
                adapter.SetState((g.outputs >> pinNumber) & 1 != 0)
            }
        }
    }
}
<commit_msg>Change compatibility comment in gpio.go<commit_after>// Package gpio implements a general purpose digital I/O port.
// Tested compatibility:
//   ATmega48/88/168
// Untested compatibility:
//   ATtiny4/5/9/10
package gpio

import (
    "fmt"
    "github.com/kierdavis/avr/emulator"
)

// TODO: pullup-disable bit in MCUCR (see http://www.atmel.com/Images/doc2545.pdf, page 87)

// Values in DDR register
const (
    Input = 0
    Output = 1
)

type GPIO struct {
    letter byte
    width uint
    dirs uint8
    outputs uint8
    pullups uint8
    inputAdapters [8]InputPinAdapter
    outputAdapters [8]OutputPinAdapter
}

func New(portLetter byte, width uint) (g *GPIO) {
    return &GPIO{
        letter: portLetter,
        width: width,
    }
}

func (g *GPIO) SetInputAdapter(pinNumber uint, adapter InputPinAdapter) {
    g.inputAdapters[pinNumber] = adapter
}

func (g *GPIO) SetOutputAdapter(pinNumber uint, adapter OutputPinAdapter) {
    g.outputAdapters[pinNumber] = adapter
}

func (g *GPIO) AddTo(em *emulator.Emulator) {
    em.RegisterPortByName(fmt.Sprintf("PORT%c", g.letter), port{g})
    em.RegisterPortByName(fmt.Sprintf("DDR%c", g.letter), ddr{g})
    em.RegisterPortByName(fmt.Sprintf("PIN%c", g.letter), pin{g})
}

// called when a PIN port is read
func (g *GPIO) getInputs() (x uint8) {
    if g.getInput(7) {x |= 0x80}
    if g.getInput(6) {x |= 0x40}
    if g.getInput(5) {x |= 0x20}
    if g.getInput(4) {x |= 0x10}
    if g.getInput(3) {x |= 0x08}
    if g.getInput(2) {x |= 0x04}
    if g.getInput(1) {x |= 0x02}
    if g.getInput(0) {x |= 0x01}
    return x
}

func (g *GPIO) getInput(pinNumber uint) bool {
    adapter := g.inputAdapters[pinNumber]
    if adapter != nil {
        return adapter.GetState()
    } else {
        return false
    }
}

// called when a PORT/DDR/PIN port is written
func (g *GPIO) updateOutputs(changed uint8) {
    if changed & 0x80 != 0 {g.updateOutput(7)}
    if changed & 0x40 != 0 {g.updateOutput(6)}
    if changed & 0x20 != 0 {g.updateOutput(5)}
    if changed & 0x10 != 0 {g.updateOutput(4)}
    if changed & 0x08 != 0 {g.updateOutput(3)}
    if changed & 0x04 != 0 {g.updateOutput(2)}
    if changed & 0x02 != 0 {g.updateOutput(1)}
    if changed & 0x01 != 0 {g.updateOutput(0)}
}

func (g *GPIO) updateOutput(pinNumber uint) {
    if pinNumber < g.width { // ignore out-of-range pins
        if (g.dirs >> pinNumber) & 1 == Input {
            adapter := g.inputAdapters[pinNumber]
            if adapter != nil {
                adapter.SetPullupEnabled((g.pullups >> pinNumber) & 1 != 0)
            }

        } else {
            adapter := g.outputAdapters[pinNumber]
            if adapter != nil {
                adapter.SetState((g.outputs >> pinNumber) & 1 != 0)
            }
        }
    }
}
<|endoftext|>