<commit_before>// Copyright 2015-present Oursky Ltd.
//
// 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 skyconfig

import (
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/joho/godotenv"
	"github.com/skygeario/skygear-server/pkg/server/uuid"
)

// parseBool parses a string representation of boolean value into a boolean
// type. In addition to what strconv.ParseBool recognize, it also recognize
// Yes, yes, YES, y, No, no, NO, n. Any other value returns an error.
func parseBool(str string) (bool, error) {
	switch str {
	case "Yes", "yes", "YES", "y":
		return true, nil
	case "No", "no", "NO", "n":
		return false, nil
	default:
		return strconv.ParseBool(str)
	}
}

type PluginConfig struct {
	Transport string
	Path      string
	Args      []string
}

// Configuration is Skygear's configuration
// The configuration will load in following order:
// 1. The ENV
// 2. The key/value in .env file
// 3. The config in *.ini (To-be depreacted)
type Configuration struct {
	HTTP struct {
		Host string `json:"host"`
	} `json:"http"`
	App struct {
		Name          string `json:"name"`
		APIKey        string `json:"api_key"`
		MasterKey     string `json:"master_key"`
		AccessControl string `json:"access_control"`
		DevMode       bool   `json:"dev_mode"`
		CORSHost      string `json:"cors_host"`
		Slave         bool   `json:"slave"`
	} `json:"app"`
	DB struct {
		ImplName string `json:"implementation"`
		Option   string `json:"option"`
	} `json:"database"`
	TokenStore struct {
		ImplName string `json:"implementation"`
		Path     string `json:"path"`
		Prefix   string `json:"prefix"`
		Expiry   int64  `json:"expiry"`
		Secret   string `json:"secret"`
	} `json:"-"`
	AssetStore struct {
		ImplName string `json:"implementation"`
		Public   bool   `json:"public"`

		// followings only used when ImplName = fs
		Path string `json:"-"`

		// followings only used when ImplName = s3
		AccessToken string `json:"access_key"`
		SecretToken string `json:"secret_key"`
		Region      string `json:"region"`
		Bucket      string `json:"bucket"`

		// followings only used when ImplName = cloud
		CloudAssetHost          string `json:"cloud_asset_host"`
		CloudAssetToken         string `json:"cloud_asset_token"`
		CloudAssetPublicPrefix  string `json:"cloud_asset_public_prefix"`
		CloudAssetPrivatePrefix string `json:"cloud_asset_private_prefix"`
	} `json:"asset_store"`
	AssetURLSigner struct {
		URLPrefix string `json:"url_prefix"`
		Secret    string `json:"secret"`
	} `json:"asset_signer"`
	APNS struct {
		Enable   bool   `json:"enable"`
		Env      string `json:"env"`
		Cert     string `json:"cert"`
		Key      string `json:"key"`
		CertPath string `json:"-"`
		KeyPath  string `json:"-"`
	} `json:"apns"`
	GCM struct {
		Enable bool   `json:"enable"`
		APIKey string `json:"api_key"`
	} `json:"gcm"`
	LOG struct {
		Level        string            `json:"-"`
		LoggersLevel map[string]string `json:"-"`
	} `json:"log"`
	LogHook struct {
		SentryDSN   string
		SentryLevel string
	} `json:"-"`
	Plugin map[string]*PluginConfig `json:"-"`
}

func NewConfiguration() Configuration {
	config := Configuration{}
	config.HTTP.Host = ":3000"
	config.App.Name = "myapp"
	config.App.AccessControl = "role"
	config.App.DevMode = true
	config.App.CORSHost = "*"
	config.App.Slave = false
	config.DB.ImplName = "pq"
	config.DB.Option = "postgres://postgres:@localhost/postgres?sslmode=disable"
	config.TokenStore.ImplName = "fs"
	config.TokenStore.Path = "data/token"
	config.TokenStore.Expiry = 0
	config.AssetStore.ImplName = "fs"
	config.AssetStore.Path = "data/asset"
	config.AssetURLSigner.URLPrefix = "http://localhost:3000/files"
	config.APNS.Enable = false
	config.APNS.Env = "sandbox"
	config.GCM.Enable = false
	config.LOG.Level = "debug"
	config.LOG.LoggersLevel = map[string]string{
		"plugin": "info",
	}
	config.Plugin = map[string]*PluginConfig{}
	return config
}

func NewConfigurationWithKeys() Configuration {
	config := NewConfiguration()
	config.App.APIKey = uuid.New()
	config.App.MasterKey = uuid.New()
	return config
}

func (config *Configuration) Validate() error {
	if config.App.Name == "" {
		return errors.New("APP_NAME is not set")
	}
	if config.App.APIKey == "" {
		return errors.New("API_KEY is not set")
	}
	if config.App.MasterKey == "" {
		return errors.New("MASTER_KEY is not set")
	}
	if !regexp.MustCompile("^[A-Za-z0-9_]+$").MatchString(config.App.Name) {
		return fmt.Errorf("APP_NAME '%s' contains invalid characters other than alphanumerics or underscores", config.App.Name)
	}
	if config.APNS.Enable && !regexp.MustCompile("^(sandbox|production)$").MatchString(config.APNS.Env) {
		return fmt.Errorf("APNS_ENV must be sandbox or production")
	}
	return nil
}

func (config *Configuration) ReadFromEnv() {
	envErr := godotenv.Load()
	if envErr != nil {
		log.Print("Error in loading .env file")
	}

	config.readHost()

	appAPIKey := os.Getenv("API_KEY")
	if appAPIKey != "" {
		config.App.APIKey = appAPIKey
	}

	appMasterKey := os.Getenv("MASTER_KEY")
	if appMasterKey != "" {
		config.App.MasterKey = appMasterKey
	}

	appName := os.Getenv("APP_NAME")
	if appName != "" {
		config.App.Name = appName
	}

	corsHost := os.Getenv("CORS_HOST")
	if corsHost != "" {
		config.App.CORSHost = corsHost
	}

	accessControl := os.Getenv("ACCESS_CONRTOL")
	if accessControl != "" {
		config.App.AccessControl = accessControl
	}

	if devMode, err := parseBool(os.Getenv("DEV_MODE")); err == nil {
		config.App.DevMode = devMode
	}

	dbImplName := os.Getenv("DB_IMPL_NAME")
	if dbImplName != "" {
		config.DB.ImplName = dbImplName
	}

	if config.DB.ImplName == "pq" && os.Getenv("DATABASE_URL") != "" {
		config.DB.Option = os.Getenv("DATABASE_URL")
	}

	if slave, err := parseBool(os.Getenv("SLAVE")); err == nil {
		config.App.Slave = slave
	}

	config.readTokenStore()
	config.readAssetStore()
	config.readAPNS()
	config.readGCM()
	config.readLog()
	config.readPlugins()
}

func (config *Configuration) readHost() {
	// Default to :3000 if both HOST and PORT is missing
	host := os.Getenv("HOST")
	if host != "" {
		config.HTTP.Host = host
	}
	if config.HTTP.Host == "" {
		port := os.Getenv("PORT")
		if port != "" {
			config.HTTP.Host = ":" + port
		}
	}
}

func (config *Configuration) readTokenStore() {
	tokenStore := os.Getenv("TOKEN_STORE")
	if tokenStore != "" {
		config.TokenStore.ImplName = tokenStore
	}
	tokenStorePath := os.Getenv("TOKEN_STORE_PATH")
	if tokenStorePath != "" {
		config.TokenStore.Path = tokenStorePath
	}

	tokenStorePrefix := os.Getenv("TOKEN_STORE_PREFIX")
	if tokenStorePrefix != "" {
		config.TokenStore.Prefix = tokenStorePrefix
	}

	if expiry, err := strconv.ParseInt(os.Getenv("TOKEN_STORE_EXPIRY"), 10, 64); err == nil {
		config.TokenStore.Expiry = expiry
	}

	tokenStoreSecret := os.Getenv("TOKEN_STORE_SECRET")
	if tokenStoreSecret != "" {
		config.TokenStore.Secret = tokenStoreSecret
	} else {
		config.TokenStore.Secret = config.App.MasterKey
	}
}

func (config *Configuration) readAssetStore() {
	assetStore := os.Getenv("ASSET_STORE")
	if assetStore != "" {
		config.AssetStore.ImplName = assetStore
	}

	if assetStorePublic, err := parseBool(os.Getenv("ASSET_STORE_PUBLIC")); err == nil {
		config.AssetStore.Public = assetStorePublic
	}

	// Local Storage related
	assetStorePath := os.Getenv("ASSET_STORE_PATH")
	if assetStorePath != "" {
		config.AssetStore.Path = assetStorePath
	}
	assetStorePrefix := os.Getenv("ASSET_STORE_URL_PREFIX")
	if assetStorePrefix != "" {
		config.AssetURLSigner.URLPrefix = assetStorePrefix
	}
	assetStoreSecret := os.Getenv("ASSET_STORE_SECRET")
	if assetStoreSecret != "" {
		config.AssetURLSigner.Secret = assetStoreSecret
	}

	// S3 related
	assetStoreAccessKey := os.Getenv("ASSET_STORE_ACCESS_KEY")
	if assetStoreAccessKey != "" {
		config.AssetStore.AccessToken = assetStoreAccessKey
	}
	assetStoreSecretKey := os.Getenv("ASSET_STORE_SECRET_KEY")
	if assetStoreSecretKey != "" {
		config.AssetStore.SecretToken = assetStoreSecretKey
	}
	assetStoreRegion := os.Getenv("ASSET_STORE_REGION")
	if assetStoreRegion != "" {
		config.AssetStore.Region = assetStoreRegion
	}
	assetStoreBucket := os.Getenv("ASSET_STORE_BUCKET")
	if assetStoreBucket != "" {
		config.AssetStore.Bucket = assetStoreBucket
	}

	// Cloud Asset related
	cloudAssetHost := os.Getenv("CLOUD_ASSET_HOST")
	if cloudAssetHost != "" {
		config.AssetStore.CloudAssetHost = cloudAssetHost
	}
	cloudAssetToken := os.Getenv("CLOUD_ASSET_TOKEN")
	if cloudAssetToken != "" {
		config.AssetStore.CloudAssetToken = cloudAssetToken
	}
	cloudAssetPublicPrefix := os.Getenv("CLOUD_ASSET_PUBLIC_PREFIX")
	if cloudAssetPublicPrefix != "" {
		config.AssetStore.CloudAssetPublicPrefix = cloudAssetPublicPrefix
	}
	cloudAssetPrivatePrefix := os.Getenv("CLOUD_ASSET_PRIVATE_PREFIX")
	if cloudAssetPrivatePrefix != "" {
		config.AssetStore.CloudAssetPrivatePrefix = cloudAssetPrivatePrefix
	}
}

func (config *Configuration) readAPNS() {
	if shouldEnableAPNS, err := parseBool(os.Getenv("APNS_ENABLE")); err == nil {
		config.APNS.Enable = shouldEnableAPNS
	}

	if !config.APNS.Enable {
		return
	}

	env := os.Getenv("APNS_ENV")
	if env != "" {
		config.APNS.Env = env
	}

	cert, key := os.Getenv("APNS_CERTIFICATE"), os.Getenv("APNS_PRIVATE_KEY")
	if cert != "" {
		config.APNS.Cert = cert
	}
	if key != "" {
		config.APNS.Key = key
	}

	certPath, keyPath := os.Getenv("APNS_CERTIFICATE_PATH"), os.Getenv("APNS_PRIVATE_KEY_PATH")
	if certPath != "" {
		config.APNS.CertPath = certPath
	}
	if keyPath != "" {
		config.APNS.KeyPath = keyPath
	}

}

func (config *Configuration) readGCM() {
	if shouldEnableGCM, err := parseBool(os.Getenv("GCM_ENABLE")); err == nil {
		config.GCM.Enable = shouldEnableGCM
	}

	gcmAPIKey := os.Getenv("GCM_APIKEY")
	if gcmAPIKey != "" {
		config.GCM.APIKey = gcmAPIKey
	}
}

func (config *Configuration) readLog() {
	logLevel := os.Getenv("LOG_LEVEL")
	if logLevel != "" {
		config.LOG.Level = logLevel
	}

	for _, environ := range os.Environ() {
		if !strings.HasPrefix(environ, "LOG_LEVEL_") {
			continue
		}

		components := strings.SplitN(environ, "=", 2)
		loggerName := strings.ToLower(strings.TrimPrefix(components[0], "LOG_LEVEL_"))
		loggerLevel := components[1]
		config.LOG.LoggersLevel[loggerName] = loggerLevel
	}

	sentry := os.Getenv("SENTRY_DSN")
	if sentry != "" {
		config.LogHook.SentryDSN = sentry
	}

	sentryLevel := os.Getenv("SENTRY_LEVEL")
	if sentryLevel != "" {
		config.LogHook.SentryLevel = sentryLevel
	}
}

func (config *Configuration) readPlugins() {
	plugin := os.Getenv("PLUGINS")
	if plugin == "" {
		return
	}

	plugins := strings.Split(plugin, ",")
	for _, p := range plugins {
		pluginConfig := &PluginConfig{}
		pluginConfig.Transport = os.Getenv(p + "_TRANSPORT")
		pluginConfig.Path = os.Getenv(p + "_PATH")
		args := os.Getenv(p + "_ARGS")
		if args != "" {
			pluginConfig.Args = strings.Split(args, ",")
		}
		config.Plugin[p] = pluginConfig
	}
}
<commit_msg>Provide default to sentry log level<commit_after>// Copyright 2015-present Oursky Ltd.
//
// 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 skyconfig

import (
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/joho/godotenv"
	"github.com/skygeario/skygear-server/pkg/server/uuid"
)

// parseBool parses a string representation of boolean value into a boolean
// type. In addition to what strconv.ParseBool recognize, it also recognize
// Yes, yes, YES, y, No, no, NO, n. Any other value returns an error.
func parseBool(str string) (bool, error) {
	switch str {
	case "Yes", "yes", "YES", "y":
		return true, nil
	case "No", "no", "NO", "n":
		return false, nil
	default:
		return strconv.ParseBool(str)
	}
}

type PluginConfig struct {
	Transport string
	Path      string
	Args      []string
}

// Configuration is Skygear's configuration
// The configuration will load in following order:
// 1. The ENV
// 2. The key/value in .env file
// 3. The config in *.ini (To-be depreacted)
type Configuration struct {
	HTTP struct {
		Host string `json:"host"`
	} `json:"http"`
	App struct {
		Name          string `json:"name"`
		APIKey        string `json:"api_key"`
		MasterKey     string `json:"master_key"`
		AccessControl string `json:"access_control"`
		DevMode       bool   `json:"dev_mode"`
		CORSHost      string `json:"cors_host"`
		Slave         bool   `json:"slave"`
	} `json:"app"`
	DB struct {
		ImplName string `json:"implementation"`
		Option   string `json:"option"`
	} `json:"database"`
	TokenStore struct {
		ImplName string `json:"implementation"`
		Path     string `json:"path"`
		Prefix   string `json:"prefix"`
		Expiry   int64  `json:"expiry"`
		Secret   string `json:"secret"`
	} `json:"-"`
	AssetStore struct {
		ImplName string `json:"implementation"`
		Public   bool   `json:"public"`

		// followings only used when ImplName = fs
		Path string `json:"-"`

		// followings only used when ImplName = s3
		AccessToken string `json:"access_key"`
		SecretToken string `json:"secret_key"`
		Region      string `json:"region"`
		Bucket      string `json:"bucket"`

		// followings only used when ImplName = cloud
		CloudAssetHost          string `json:"cloud_asset_host"`
		CloudAssetToken         string `json:"cloud_asset_token"`
		CloudAssetPublicPrefix  string `json:"cloud_asset_public_prefix"`
		CloudAssetPrivatePrefix string `json:"cloud_asset_private_prefix"`
	} `json:"asset_store"`
	AssetURLSigner struct {
		URLPrefix string `json:"url_prefix"`
		Secret    string `json:"secret"`
	} `json:"asset_signer"`
	APNS struct {
		Enable   bool   `json:"enable"`
		Env      string `json:"env"`
		Cert     string `json:"cert"`
		Key      string `json:"key"`
		CertPath string `json:"-"`
		KeyPath  string `json:"-"`
	} `json:"apns"`
	GCM struct {
		Enable bool   `json:"enable"`
		APIKey string `json:"api_key"`
	} `json:"gcm"`
	LOG struct {
		Level        string            `json:"-"`
		LoggersLevel map[string]string `json:"-"`
	} `json:"log"`
	LogHook struct {
		SentryDSN   string
		SentryLevel string
	} `json:"-"`
	Plugin map[string]*PluginConfig `json:"-"`
}

func NewConfiguration() Configuration {
	config := Configuration{}
	config.HTTP.Host = ":3000"
	config.App.Name = "myapp"
	config.App.AccessControl = "role"
	config.App.DevMode = true
	config.App.CORSHost = "*"
	config.App.Slave = false
	config.DB.ImplName = "pq"
	config.DB.Option = "postgres://postgres:@localhost/postgres?sslmode=disable"
	config.TokenStore.ImplName = "fs"
	config.TokenStore.Path = "data/token"
	config.TokenStore.Expiry = 0
	config.AssetStore.ImplName = "fs"
	config.AssetStore.Path = "data/asset"
	config.AssetURLSigner.URLPrefix = "http://localhost:3000/files"
	config.APNS.Enable = false
	config.APNS.Env = "sandbox"
	config.GCM.Enable = false
	config.LOG.Level = "debug"
	config.LOG.LoggersLevel = map[string]string{
		"plugin": "info",
	}
	config.LogHook.SentryLevel = "error"
	config.Plugin = map[string]*PluginConfig{}
	return config
}

func NewConfigurationWithKeys() Configuration {
	config := NewConfiguration()
	config.App.APIKey = uuid.New()
	config.App.MasterKey = uuid.New()
	return config
}

func (config *Configuration) Validate() error {
	if config.App.Name == "" {
		return errors.New("APP_NAME is not set")
	}
	if config.App.APIKey == "" {
		return errors.New("API_KEY is not set")
	}
	if config.App.MasterKey == "" {
		return errors.New("MASTER_KEY is not set")
	}
	if !regexp.MustCompile("^[A-Za-z0-9_]+$").MatchString(config.App.Name) {
		return fmt.Errorf("APP_NAME '%s' contains invalid characters other than alphanumerics or underscores", config.App.Name)
	}
	if config.APNS.Enable && !regexp.MustCompile("^(sandbox|production)$").MatchString(config.APNS.Env) {
		return fmt.Errorf("APNS_ENV must be sandbox or production")
	}
	return nil
}

func (config *Configuration) ReadFromEnv() {
	envErr := godotenv.Load()
	if envErr != nil {
		log.Print("Error in loading .env file")
	}

	config.readHost()

	appAPIKey := os.Getenv("API_KEY")
	if appAPIKey != "" {
		config.App.APIKey = appAPIKey
	}

	appMasterKey := os.Getenv("MASTER_KEY")
	if appMasterKey != "" {
		config.App.MasterKey = appMasterKey
	}

	appName := os.Getenv("APP_NAME")
	if appName != "" {
		config.App.Name = appName
	}

	corsHost := os.Getenv("CORS_HOST")
	if corsHost != "" {
		config.App.CORSHost = corsHost
	}

	accessControl := os.Getenv("ACCESS_CONRTOL")
	if accessControl != "" {
		config.App.AccessControl = accessControl
	}

	if devMode, err := parseBool(os.Getenv("DEV_MODE")); err == nil {
		config.App.DevMode = devMode
	}

	dbImplName := os.Getenv("DB_IMPL_NAME")
	if dbImplName != "" {
		config.DB.ImplName = dbImplName
	}

	if config.DB.ImplName == "pq" && os.Getenv("DATABASE_URL") != "" {
		config.DB.Option = os.Getenv("DATABASE_URL")
	}

	if slave, err := parseBool(os.Getenv("SLAVE")); err == nil {
		config.App.Slave = slave
	}

	config.readTokenStore()
	config.readAssetStore()
	config.readAPNS()
	config.readGCM()
	config.readLog()
	config.readPlugins()
}

func (config *Configuration) readHost() {
	// Default to :3000 if both HOST and PORT is missing
	host := os.Getenv("HOST")
	if host != "" {
		config.HTTP.Host = host
	}
	if config.HTTP.Host == "" {
		port := os.Getenv("PORT")
		if port != "" {
			config.HTTP.Host = ":" + port
		}
	}
}

func (config *Configuration) readTokenStore() {
	tokenStore := os.Getenv("TOKEN_STORE")
	if tokenStore != "" {
		config.TokenStore.ImplName = tokenStore
	}
	tokenStorePath := os.Getenv("TOKEN_STORE_PATH")
	if tokenStorePath != "" {
		config.TokenStore.Path = tokenStorePath
	}

	tokenStorePrefix := os.Getenv("TOKEN_STORE_PREFIX")
	if tokenStorePrefix != "" {
		config.TokenStore.Prefix = tokenStorePrefix
	}

	if expiry, err := strconv.ParseInt(os.Getenv("TOKEN_STORE_EXPIRY"), 10, 64); err == nil {
		config.TokenStore.Expiry = expiry
	}

	tokenStoreSecret := os.Getenv("TOKEN_STORE_SECRET")
	if tokenStoreSecret != "" {
		config.TokenStore.Secret = tokenStoreSecret
	} else {
		config.TokenStore.Secret = config.App.MasterKey
	}
}

func (config *Configuration) readAssetStore() {
	assetStore := os.Getenv("ASSET_STORE")
	if assetStore != "" {
		config.AssetStore.ImplName = assetStore
	}

	if assetStorePublic, err := parseBool(os.Getenv("ASSET_STORE_PUBLIC")); err == nil {
		config.AssetStore.Public = assetStorePublic
	}

	// Local Storage related
	assetStorePath := os.Getenv("ASSET_STORE_PATH")
	if assetStorePath != "" {
		config.AssetStore.Path = assetStorePath
	}
	assetStorePrefix := os.Getenv("ASSET_STORE_URL_PREFIX")
	if assetStorePrefix != "" {
		config.AssetURLSigner.URLPrefix = assetStorePrefix
	}
	assetStoreSecret := os.Getenv("ASSET_STORE_SECRET")
	if assetStoreSecret != "" {
		config.AssetURLSigner.Secret = assetStoreSecret
	}

	// S3 related
	assetStoreAccessKey := os.Getenv("ASSET_STORE_ACCESS_KEY")
	if assetStoreAccessKey != "" {
		config.AssetStore.AccessToken = assetStoreAccessKey
	}
	assetStoreSecretKey := os.Getenv("ASSET_STORE_SECRET_KEY")
	if assetStoreSecretKey != "" {
		config.AssetStore.SecretToken = assetStoreSecretKey
	}
	assetStoreRegion := os.Getenv("ASSET_STORE_REGION")
	if assetStoreRegion != "" {
		config.AssetStore.Region = assetStoreRegion
	}
	assetStoreBucket := os.Getenv("ASSET_STORE_BUCKET")
	if assetStoreBucket != "" {
		config.AssetStore.Bucket = assetStoreBucket
	}

	// Cloud Asset related
	cloudAssetHost := os.Getenv("CLOUD_ASSET_HOST")
	if cloudAssetHost != "" {
		config.AssetStore.CloudAssetHost = cloudAssetHost
	}
	cloudAssetToken := os.Getenv("CLOUD_ASSET_TOKEN")
	if cloudAssetToken != "" {
		config.AssetStore.CloudAssetToken = cloudAssetToken
	}
	cloudAssetPublicPrefix := os.Getenv("CLOUD_ASSET_PUBLIC_PREFIX")
	if cloudAssetPublicPrefix != "" {
		config.AssetStore.CloudAssetPublicPrefix = cloudAssetPublicPrefix
	}
	cloudAssetPrivatePrefix := os.Getenv("CLOUD_ASSET_PRIVATE_PREFIX")
	if cloudAssetPrivatePrefix != "" {
		config.AssetStore.CloudAssetPrivatePrefix = cloudAssetPrivatePrefix
	}
}

func (config *Configuration) readAPNS() {
	if shouldEnableAPNS, err := parseBool(os.Getenv("APNS_ENABLE")); err == nil {
		config.APNS.Enable = shouldEnableAPNS
	}

	if !config.APNS.Enable {
		return
	}

	env := os.Getenv("APNS_ENV")
	if env != "" {
		config.APNS.Env = env
	}

	cert, key := os.Getenv("APNS_CERTIFICATE"), os.Getenv("APNS_PRIVATE_KEY")
	if cert != "" {
		config.APNS.Cert = cert
	}
	if key != "" {
		config.APNS.Key = key
	}

	certPath, keyPath := os.Getenv("APNS_CERTIFICATE_PATH"), os.Getenv("APNS_PRIVATE_KEY_PATH")
	if certPath != "" {
		config.APNS.CertPath = certPath
	}
	if keyPath != "" {
		config.APNS.KeyPath = keyPath
	}

}

func (config *Configuration) readGCM() {
	if shouldEnableGCM, err := parseBool(os.Getenv("GCM_ENABLE")); err == nil {
		config.GCM.Enable = shouldEnableGCM
	}

	gcmAPIKey := os.Getenv("GCM_APIKEY")
	if gcmAPIKey != "" {
		config.GCM.APIKey = gcmAPIKey
	}
}

func (config *Configuration) readLog() {
	logLevel := os.Getenv("LOG_LEVEL")
	if logLevel != "" {
		config.LOG.Level = logLevel
	}

	for _, environ := range os.Environ() {
		if !strings.HasPrefix(environ, "LOG_LEVEL_") {
			continue
		}

		components := strings.SplitN(environ, "=", 2)
		loggerName := strings.ToLower(strings.TrimPrefix(components[0], "LOG_LEVEL_"))
		loggerLevel := components[1]
		config.LOG.LoggersLevel[loggerName] = loggerLevel
	}

	sentry := os.Getenv("SENTRY_DSN")
	if sentry != "" {
		config.LogHook.SentryDSN = sentry
	}

	sentryLevel := os.Getenv("SENTRY_LEVEL")
	if sentryLevel != "" {
		config.LogHook.SentryLevel = sentryLevel
	}
}

func (config *Configuration) readPlugins() {
	plugin := os.Getenv("PLUGINS")
	if plugin == "" {
		return
	}

	plugins := strings.Split(plugin, ",")
	for _, p := range plugins {
		pluginConfig := &PluginConfig{}
		pluginConfig.Transport = os.Getenv(p + "_TRANSPORT")
		pluginConfig.Path = os.Getenv(p + "_PATH")
		args := os.Getenv(p + "_ARGS")
		if args != "" {
			pluginConfig.Args = strings.Split(args, ",")
		}
		config.Plugin[p] = pluginConfig
	}
}
<|endoftext|>


<commit_before>package testutil

import (
	"fmt"
	"reflect"
)

// Checks whether two slices contain the same elements.
func ShouldSetEqual(actual interface{}, expected ...interface{}) (msg string) {
	actualSlice, expectedSlice := actual.([]string), expected[0].([]string)

	msg = fmt.Sprintf("Expected:\t%q\nActual:\t%q\n(Should have the same elements)", expectedSlice, actualSlice)

	if len(actualSlice) != len(expectedSlice) {
		return
	}

	actualSet := make(map[string]bool)
	for _, a := range actualSlice {
		actualSet[a] = true
	}

	for _, e := range expectedSlice {
		if actualSet[e] != true {
			return
		}
	}

	expectedSet := make(map[string]bool)
	for _, e := range expectedSlice {
		expectedSet[e] = true
	}

	for _, a := range actualSlice {
		if expectedSet[a] != true {
			return
		}
	}

	return ""
}

func ShouldConsistOf(actual interface{}, expected ...interface{}) (msg string) {
	msg = fmt.Sprintf("Expected:\t%v\nActual:\t%v\n(Should consist of the given elements)", expected, actual)

	switch actual.(type) {
	case []int:
		for i, v := range actual.([]int) {
			if !reflect.DeepEqual(v, expected[i]) {
				return
			}
		}
	case []string:
		for i, v := range actual.([]string) {
			if !reflect.DeepEqual(v, expected[i]) {
				return
			}
		}
	}

	return ""
}

func ShouldBeSuperSetOf(super interface{}, sub ...interface{}) (msg string) {
	superSlice, subSlice := super.([]string), sub[0].([]string)

	msg = fmt.Sprintf("SubSet:\t%q\nSuperSet:\t%q\n(Should contain every element of the given slice)", subSlice, superSlice)

	if len(superSlice) < len(subSlice) {
		return
	}

	superSet := make(map[string]bool)
	for _, e := range superSlice {
		superSet[e] = true
	}

	for _, a := range subSlice {
		if _, ok := superSet[a]; ok != true {
			return
		}
	}

	return ""
}
<commit_msg>Fix testutil assertion<commit_after>package testutil

import (
	"fmt"
	"reflect"
)

// Checks whether two slices contain the same elements.
func ShouldSetEqual(actual interface{}, expected ...interface{}) (msg string) {
	actualSlice, expectedSlice := actual.([]string), expected[0].([]string)

	msg = fmt.Sprintf("Expected:\t%q\nActual:\t%q\n(Should have the same elements)", expectedSlice, actualSlice)

	if len(actualSlice) != len(expectedSlice) {
		return
	}

	actualSet := make(map[string]bool)
	for _, a := range actualSlice {
		actualSet[a] = true
	}

	for _, e := range expectedSlice {
		if actualSet[e] != true {
			return
		}
	}

	expectedSet := make(map[string]bool)
	for _, e := range expectedSlice {
		expectedSet[e] = true
	}

	for _, a := range actualSlice {
		if expectedSet[a] != true {
			return
		}
	}

	return ""
}

func ShouldConsistOf(actual interface{}, expected ...interface{}) (msg string) {
	msg = fmt.Sprintf("Expected:\n%q\nActual:\n%q\n(Should consist of the given elements)", expected, actual)

	switch actual.(type) {
	case []int:
		for i, v := range actual.([]int) {
			if !reflect.DeepEqual(v, expected[i]) {
				return
			}
		}
	case []string:
		for i, v := range actual.([]string) {
			if !reflect.DeepEqual(v, expected[i]) {
				return
			}
		}
	}

	return ""
}

func ShouldBeSuperSetOf(super interface{}, sub ...interface{}) (msg string) {
	superSlice, subSlice := super.([]string), sub[0].([]string)

	msg = fmt.Sprintf("SubSet:\t%q\nSuperSet:\t%q\n(Should contain every element of the given slice)", subSlice, superSlice)

	if len(superSlice) < len(subSlice) {
		return
	}

	superSet := make(map[string]bool)
	for _, e := range superSlice {
		superSet[e] = true
	}

	for _, a := range subSlice {
		if _, ok := superSet[a]; ok != true {
			return
		}
	}

	return ""
}
<|endoftext|>


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

// This file implements a typechecker test harness. The packages specified
// in tests are typechecked. Error messages reported by the typechecker are
// compared against the error messages expected in the test files.
//
// Expected errors are indicated in the test files by putting a comment
// of the form /* ERROR "rx" */ immediately following an offending token.
// The harness will verify that an error matching the regular expression
// rx is reported at that source position. Consecutive comments may be
// used to indicate multiple errors for the same token position.
//
// For instance, the following test file indicates that a "not declared"
// error should be reported for the undeclared variable x:
//
//	package p
//	func f() {
//		_ = x /* ERROR "not declared" */ + 1
//	}

package types

import (
	"flag"
	"go/ast"
	"go/parser"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"regexp"
	"strings"
	"testing"
)

var (
	listErrors = flag.Bool("list", false, "list errors")
	testFiles  = flag.String("files", "", "space-separated list of test files")
)

// The test filenames do not end in .go so that they are invisible
// to gofmt since they contain comments that must not change their
// positions relative to surrounding tokens.

// Each tests entry is list of files belonging to the same package.
var tests = [][]string{
	{"testdata/cycles.src"},
	{"testdata/decls0.src"},
	{"testdata/decls1.src"},
	{"testdata/decls2a.src", "testdata/decls2b.src"},
	{"testdata/decls3.src"},
	{"testdata/const0.src"},
	{"testdata/constdecl.src"},
	{"testdata/vardecl.src"},
	{"testdata/expr0.src"},
	{"testdata/expr1.src"},
	{"testdata/expr2.src"},
	{"testdata/expr3.src"},
	{"testdata/methodsets.src"},
	{"testdata/shifts.src"},
	{"testdata/builtins.src"},
	{"testdata/conversions.src"},
	{"testdata/stmt0.src"},
	{"testdata/stmt1.src"},
}

var fset = token.NewFileSet()

// Positioned errors are of the form filename:line:column: message .
var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)

// splitError splits an error's error message into a position string
// and the actual error message. If there's no position information,
// pos is the empty string, and msg is the entire error message.
//
func splitError(err error) (pos, msg string) {
	msg = err.Error()
	if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
		pos = m[1]
		msg = m[2]
	}
	return
}

func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
	var files []*ast.File
	var errlist []error
	for _, filename := range filenames {
		file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
		if file == nil {
			t.Fatalf("%s: %s", filename, err)
		}
		files = append(files, file)
		if err != nil {
			if list, _ := err.(scanner.ErrorList); len(list) > 0 {
				for _, err := range list {
					errlist = append(errlist, err)
				}
			} else {
				errlist = append(errlist, err)
			}
		}
	}
	return files, errlist
}

// ERROR comments must be of the form /* ERROR "rx" */ and rx is
// a regular expression that matches the expected error message.
//
var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)

// errMap collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
	// map of position strings to lists of error message patterns
	errmap := make(map[string][]string)

	for _, file := range files {
		filename := fset.Position(file.Package).Filename
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			t.Fatalf("%s: could not read %s", testname, filename)
		}

		var s scanner.Scanner
		s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
		var prev string // position string of last non-comment, non-semicolon token

	scanFile:
		for {
			pos, tok, lit := s.Scan()
			switch tok {
			case token.EOF:
				break scanFile
			case token.COMMENT:
				if s := errRx.FindStringSubmatch(lit); len(s) == 2 {
					errmap[prev] = append(errmap[prev], s[1])
				}
			case token.SEMICOLON:
				// ignore automatically inserted semicolon
				if lit == "\n" {
					continue scanFile
				}
				fallthrough
			default:
				prev = fset.Position(pos).String()
			}
		}
	}

	return errmap
}

func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
	for _, err := range errlist {
		pos, gotMsg := splitError(err)
		list := errmap[pos]
		index := -1 // list index of matching message, if any
		// we expect one of the messages in list to match the error at pos
		for i, wantRx := range list {
			rx, err := regexp.Compile(wantRx)
			if err != nil {
				t.Errorf("%s: %v", pos, err)
				continue
			}
			if rx.MatchString(gotMsg) {
				index = i
				break
			}
		}
		if index >= 0 {
			// eliminate from list
			if n := len(list) - 1; n > 0 {
				// not the last entry - swap in last element and shorten list by 1
				list[index] = list[n]
				errmap[pos] = list[:n]
			} else {
				// last entry - remove list from map
				delete(errmap, pos)
			}
		} else {
			t.Errorf("%s: no error expected: %q", pos, gotMsg)
		}
	}
}

func checkFiles(t *testing.T, testfiles []string) {
	// parse files and collect parser errors
	files, errlist := parseFiles(t, testfiles)

	pkgName := "<no package>"
	if len(files) > 0 {
		pkgName = files[0].Name.Name
	}

	// typecheck and collect typechecker errors
	var ctxt Context
	ctxt.Error = func(err error) {
		// Ignore error messages containing "previous declaration":
		// They are follow-up error messages after a redeclaration
		// error.
		if !strings.Contains(err.Error(), "previous declaration") {
			errlist = append(errlist, err)
		}
	}
	ctxt.Check(pkgName, fset, files...)

	if *listErrors {
		t.Errorf("--- %s: %d errors found", pkgName, len(errlist))
		for _, err := range errlist {
			t.Error(err)
		}
		return
	}

	// match and eliminate errors;
	// we are expecting the following errors
	errmap := errMap(t, pkgName, files)
	eliminate(t, errmap, errlist)

	// there should be no expected errors left
	if len(errmap) > 0 {
		t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
		for pos, list := range errmap {
			for _, rx := range list {
				t.Errorf("%s: %q", pos, rx)
			}
		}
	}
}

var testBuiltinsDeclared = false

func TestCheck(t *testing.T) {
	// Declare builtins for testing.
	// Not done in an init func to avoid an init race with
	// the construction of the Universe var.
	if !testBuiltinsDeclared {
		testBuiltinsDeclared = true
		// Pkg == nil for Universe objects
		def(NewFunc(token.NoPos, nil, "assert", &Builtin{_Assert, "assert", 1, false, true}))
		def(NewFunc(token.NoPos, nil, "trace", &Builtin{_Trace, "trace", 0, true, true}))
	}

	// If explicit test files are specified, only check those.
	if files := *testFiles; files != "" {
		checkFiles(t, strings.Split(files, " "))
		return
	}

	// Otherwise, run all the tests.
	for _, files := range tests {
		checkFiles(t, files)
	}
}
<commit_msg>go.tools/go/types: report test errors immediately in -list mode<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements a typechecker test harness. The packages specified
// in tests are typechecked. Error messages reported by the typechecker are
// compared against the error messages expected in the test files.
//
// Expected errors are indicated in the test files by putting a comment
// of the form /* ERROR "rx" */ immediately following an offending token.
// The harness will verify that an error matching the regular expression
// rx is reported at that source position. Consecutive comments may be
// used to indicate multiple errors for the same token position.
//
// For instance, the following test file indicates that a "not declared"
// error should be reported for the undeclared variable x:
//
//	package p
//	func f() {
//		_ = x /* ERROR "not declared" */ + 1
//	}

package types

import (
	"flag"
	"go/ast"
	"go/parser"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"regexp"
	"strings"
	"testing"
)

var (
	listErrors = flag.Bool("list", false, "list errors")
	testFiles  = flag.String("files", "", "space-separated list of test files")
)

// The test filenames do not end in .go so that they are invisible
// to gofmt since they contain comments that must not change their
// positions relative to surrounding tokens.

// Each tests entry is list of files belonging to the same package.
var tests = [][]string{
	{"testdata/cycles.src"},
	{"testdata/decls0.src"},
	{"testdata/decls1.src"},
	{"testdata/decls2a.src", "testdata/decls2b.src"},
	{"testdata/decls3.src"},
	{"testdata/const0.src"},
	{"testdata/constdecl.src"},
	{"testdata/vardecl.src"},
	{"testdata/expr0.src"},
	{"testdata/expr1.src"},
	{"testdata/expr2.src"},
	{"testdata/expr3.src"},
	{"testdata/methodsets.src"},
	{"testdata/shifts.src"},
	{"testdata/builtins.src"},
	{"testdata/conversions.src"},
	{"testdata/stmt0.src"},
	{"testdata/stmt1.src"},
}

var fset = token.NewFileSet()

// Positioned errors are of the form filename:line:column: message .
var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)

// splitError splits an error's error message into a position string
// and the actual error message. If there's no position information,
// pos is the empty string, and msg is the entire error message.
//
func splitError(err error) (pos, msg string) {
	msg = err.Error()
	if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
		pos = m[1]
		msg = m[2]
	}
	return
}

func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
	var files []*ast.File
	var errlist []error
	for _, filename := range filenames {
		file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
		if file == nil {
			t.Fatalf("%s: %s", filename, err)
		}
		files = append(files, file)
		if err != nil {
			if list, _ := err.(scanner.ErrorList); len(list) > 0 {
				for _, err := range list {
					errlist = append(errlist, err)
				}
			} else {
				errlist = append(errlist, err)
			}
		}
	}
	return files, errlist
}

// ERROR comments must be of the form /* ERROR "rx" */ and rx is
// a regular expression that matches the expected error message.
//
var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)

// errMap collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
	// map of position strings to lists of error message patterns
	errmap := make(map[string][]string)

	for _, file := range files {
		filename := fset.Position(file.Package).Filename
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			t.Fatalf("%s: could not read %s", testname, filename)
		}

		var s scanner.Scanner
		s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
		var prev string // position string of last non-comment, non-semicolon token

	scanFile:
		for {
			pos, tok, lit := s.Scan()
			switch tok {
			case token.EOF:
				break scanFile
			case token.COMMENT:
				if s := errRx.FindStringSubmatch(lit); len(s) == 2 {
					errmap[prev] = append(errmap[prev], s[1])
				}
			case token.SEMICOLON:
				// ignore automatically inserted semicolon
				if lit == "\n" {
					continue scanFile
				}
				fallthrough
			default:
				prev = fset.Position(pos).String()
			}
		}
	}

	return errmap
}

func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
	for _, err := range errlist {
		pos, gotMsg := splitError(err)
		list := errmap[pos]
		index := -1 // list index of matching message, if any
		// we expect one of the messages in list to match the error at pos
		for i, wantRx := range list {
			rx, err := regexp.Compile(wantRx)
			if err != nil {
				t.Errorf("%s: %v", pos, err)
				continue
			}
			if rx.MatchString(gotMsg) {
				index = i
				break
			}
		}
		if index >= 0 {
			// eliminate from list
			if n := len(list) - 1; n > 0 {
				// not the last entry - swap in last element and shorten list by 1
				list[index] = list[n]
				errmap[pos] = list[:n]
			} else {
				// last entry - remove list from map
				delete(errmap, pos)
			}
		} else {
			t.Errorf("%s: no error expected: %q", pos, gotMsg)
		}
	}
}

func checkFiles(t *testing.T, testfiles []string) {
	// parse files and collect parser errors
	files, errlist := parseFiles(t, testfiles)

	pkgName := "<no package>"
	if len(files) > 0 {
		pkgName = files[0].Name.Name
	}

	if *listErrors {
		t.Errorf("--- %s:", pkgName)
		for _, err := range errlist {
			t.Error(err)
		}
	}

	// typecheck and collect typechecker errors
	var ctxt Context
	ctxt.Error = func(err error) {
		if *listErrors {
			t.Error(err)
			return
		}
		// Ignore error messages containing "previous declaration":
		// They are follow-up error messages after a redeclaration
		// error.
		if !strings.Contains(err.Error(), "previous declaration") {
			errlist = append(errlist, err)
		}
	}
	ctxt.Check(pkgName, fset, files...)

	if *listErrors {
		return
	}

	// match and eliminate errors;
	// we are expecting the following errors
	errmap := errMap(t, pkgName, files)
	eliminate(t, errmap, errlist)

	// there should be no expected errors left
	if len(errmap) > 0 {
		t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
		for pos, list := range errmap {
			for _, rx := range list {
				t.Errorf("%s: %q", pos, rx)
			}
		}
	}
}

var testBuiltinsDeclared = false

func TestCheck(t *testing.T) {
	// Declare builtins for testing.
	// Not done in an init func to avoid an init race with
	// the construction of the Universe var.
	if !testBuiltinsDeclared {
		testBuiltinsDeclared = true
		// Pkg == nil for Universe objects
		def(NewFunc(token.NoPos, nil, "assert", &Builtin{_Assert, "assert", 1, false, true}))
		def(NewFunc(token.NoPos, nil, "trace", &Builtin{_Trace, "trace", 0, true, true}))
	}

	// If explicit test files are specified, only check those.
	if files := *testFiles; files != "" {
		checkFiles(t, strings.Split(files, " "))
		return
	}

	// Otherwise, run all the tests.
	for _, files := range tests {
		checkFiles(t, files)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Joseph D Poirier
// Distributable under the terms of The New BSD License
// that can be found in the LICENSE file.

// Package godump978 wraps libdump978, a 978MHz UAT demodulator.
//
// Build example
//
// dump978.so:
//   $ gcc -c -O2 -g -Wall -Werror -Ifec -fpic -DBUILD_LIB=1 dump978.c fec.c fec/decode_rs_char.c fec/init_rs_char.c
//   $ gcc -shared -lm -o ../libdump978.so dump978.o fec.o decode_rs_char.o init_rs_char.o
//
// dump978 go wrapper:
//   $ go build -o dump978.a dump978.go dump978_exports.go
//
// uat_read executable:
//   $ go build uat_read.go

package godump978

/*
#cgo linux LDFLAGS: -L${SRCDIR}/.. -ldump978
#cgo darwin LDFLAGS: -L${SRCDIR}/.. -ldump978
#cgo windows CFLAGS: -IC:/WINDOWS/system32
#cgo windows LDFLAGS: -L. -lrtlsdr -LC:/WINDOWS/system32

#include <stdlib.h>
#include <stdint.h>
#include "../dump978/dump978.h"

extern void dump978Cb(char updown, uint8_t *data, int len);
static inline CallBack GetGoCb() {
	return (CallBack)dump978Cb;
}
*/
import "C"
import "unsafe"

// Current version.
var PackageVersion = "v0.1"

// InChan is a buffered input channel for raw data.
var InChan = make(chan []byte, 100)

type UserCbT func(C.char, *C.uint8_t, C.int)

// Dump978Init must be the first function called in this package.
func Dump978Init() {
	C.Dump978Init((C.CallBack)(C.GetGoCb()))
}

// ProcessData passes buf (modulated data) to dump978 for demodulation.
func ProcessData(buf []byte) {
	C.process_data((*C.char)(unsafe.Pointer(&buf[0])), C.int(len(buf)))
}

func ProcessDataFromChannel() {
	for {
		inData := <-InChan
		ProcessData(inData)
	}
}
<commit_msg>circleci changes<commit_after>// Copyright (c) 2015 Joseph D Poirier
// Distributable under the terms of The New BSD License
// that can be found in the LICENSE file.

// Package godump978 wraps libdump978, a 978MHz UAT demodulator.
//
// Build example
//
// dump978.so:
//   $ gcc -c -O2 -g -Wall -Werror -Ifec -fpic -DBUILD_LIB=1 dump978.c fec.c fec/decode_rs_char.c fec/init_rs_char.c
//   $ gcc -shared -lm -o ../libdump978.so dump978.o fec.o decode_rs_char.o init_rs_char.o
//
// dump978 go wrapper:
//   $ go build -o dump978.a dump978.go dump978_exports.go
//
// uat_read executable:
//   $ go build uat_read.go

package godump978

/*
#cgo linux LDFLAGS: -L.. -ldump978
#cgo darwin LDFLAGS: -L.. -ldump978
#cgo windows CFLAGS: -IC:/WINDOWS/system32
#cgo windows LDFLAGS: -L. -lrtlsdr -LC:/WINDOWS/system32

#include <stdlib.h>
#include <stdint.h>
#include "../dump978/dump978.h"

extern void dump978Cb(char updown, uint8_t *data, int len);
static inline CallBack GetGoCb() {
	return (CallBack)dump978Cb;
}
*/
import "C"
import "unsafe"

// Current version.
var PackageVersion = "v0.1"

// InChan is a buffered input channel for raw data.
var InChan = make(chan []byte, 100)

type UserCbT func(C.char, *C.uint8_t, C.int)

// Dump978Init must be the first function called in this package.
func Dump978Init() {
	C.Dump978Init((C.CallBack)(C.GetGoCb()))
}

// ProcessData passes buf (modulated data) to dump978 for demodulation.
func ProcessData(buf []byte) {
	C.process_data((*C.char)(unsafe.Pointer(&buf[0])), C.int(len(buf)))
}

func ProcessDataFromChannel() {
	for {
		inData := <-InChan
		ProcessData(inData)
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. 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.
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 network

import (
	"errors"
	"flag"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/vim25/types"
)

type add struct {
	*flags.VirtualMachineFlag
	*flags.NetworkFlag
}

func init() {
	cli.Register("vm.network.add", &add{})
}

func (cmd *add) Register(f *flag.FlagSet) {}

func (cmd *add) Process() error { return nil }

func (cmd *add) Run(f *flag.FlagSet) error {
	vm, err := cmd.VirtualMachineFlag.VirtualMachine()
	if err != nil {
		return err
	}

	if vm == nil {
		return errors.New("please specify a vm")
	}

	// Set network if specified as extra argument.
	if f.NArg() > 0 {
		_ = cmd.NetworkFlag.Set(f.Arg(0))
	}

	net, err := cmd.NetworkFlag.Device()
	if err != nil {
		return err
	}

	config := &types.VirtualDeviceConfigSpec{
		Device:    net,
		Operation: types.VirtualDeviceConfigSpecOperationAdd,
	}

	spec := types.VirtualMachineConfigSpec{}
	spec.DeviceChange = append(spec.DeviceChange, config)

	task, err := vm.Reconfigure(spec)
	if err != nil {
		return err
	}

	return task.Wait()
}
<commit_msg>Use DeviceAdd helper in vm.network.add command<commit_after>/*
Copyright (c) 2014 VMware, Inc. 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.
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 network

import (
	"errors"
	"flag"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
)

type add struct {
	*flags.VirtualMachineFlag
	*flags.NetworkFlag
}

func init() {
	cli.Register("vm.network.add", &add{})
}

func (cmd *add) Register(f *flag.FlagSet) {}

func (cmd *add) Process() error { return nil }

func (cmd *add) Run(f *flag.FlagSet) error {
	vm, err := cmd.VirtualMachineFlag.VirtualMachine()
	if err != nil {
		return err
	}

	if vm == nil {
		return errors.New("please specify a vm")
	}

	// Set network if specified as extra argument.
	if f.NArg() > 0 {
		_ = cmd.NetworkFlag.Set(f.Arg(0))
	}

	net, err := cmd.NetworkFlag.Device()
	if err != nil {
		return err
	}

	return vm.AddDevice(net)
}
<|endoftext|>


<commit_before>package gce

import (
	"fmt"
	"github.com/viant/endly"
	"github.com/viant/toolbox"
	"google.golang.org/api/compute/v1"
)

const (
	//ServiceID represents cce service id.
	ServiceID = "gce"
)

//google comput engine service
type service struct {
	*endly.AbstractService
}

func (s *service) fetchInstanceList(context *endly.Context, request *CallRequest) (CallResponse, error) {
	var response interface{}
	config, err := context.Secrets.GetCredentials(request.Credentials)
	if err != nil {
		return nil, err
	}
	computeClient, ctx, err := NewComputeService(config)
	if err != nil {
		return nil, err
	}
	project := toolbox.AsString(request.Parameters[0])
	zone := toolbox.AsString(request.Parameters[1])
	req := computeClient.Instances.List(project, zone)
	var instances = make([]*compute.Instance, 0)
	err = req.Pages(ctx, func(list *compute.InstanceList) error {
		instances = append(instances, list.Items...)
		return nil
	})
	if err != nil {
		return nil, err
	}
	response = instances
	return response, nil
}

func (s *service) call(context *endly.Context, request *CallRequest) (CallResponse, error) {
	if request.Service == "Instances" && request.Method == "List" {
		return s.fetchInstanceList(context, request)
	}
	config, err := context.Secrets.GetCredentials(request.Credentials)
	if err != nil {
		return nil, err
	}
	var response interface{}
	computeClient, ctx, err := NewComputeService(config)
	if err != nil {
		return nil, err
	}
	computeService, err := GetComputeService(computeClient, request.Service)

	method, err := toolbox.GetFunction(computeService, request.Method)
	if err != nil {
		return nil, err
	}
	parameters, err := toolbox.AsCompatibleFunctionParameters(method, request.Parameters)
	if err != nil {
		return nil, err
	}
	functionResult := toolbox.CallFunction(method, parameters...)
	if len(functionResult) != 1 {
		return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
	}
	callRequest := functionResult[0]
	contextFunction, err := toolbox.GetFunction(callRequest, "Context")
	if err != nil {
		return nil, err
	}
	contextResult := toolbox.CallFunction(contextFunction, ctx)
	if len(contextResult) != 1 {
		return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
	}

	if doFunction, err := toolbox.GetFunction(callRequest, "Do"); err == nil {
		doResult := toolbox.CallFunction(doFunction)
		if len(doResult) > 0 {
			if err, ok := doResult[len(doResult)-1].(error); ok {
				return nil, err
			}
			response = doResult[0]
			return response, nil
		}
	}
	return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
}

const (
	gceGetInstanceStatusExample = `{
  "Credentials": "${env.HOME}/.secret/gce.json",
  "Service": "Instances",
  "Method": "Get",
  "Parameters":["myProject","us-west1-b","instance-1"]
}
`
	gceGetInstanceStatusResponseExample = `{
  "cpuPlatform": "Intel Skylake",
  "id": "2222222254251138088",
  "kind": "compute#instance",
  "machineType": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b/machineTypes/n1-standard-8",
  "metadata": {
    "kind": "compute#metadata"
  },
  "name": "instance-1",
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "kind": "compute#accessConfig",
          "name": "External NAT",
          "natIP": "235.147.19.148",
          "type": "ONE_TO_ONE_NAT"
        }
      ],
      "kind": "compute#networkInterface",
      "name": "nic0",
      "network": "https://www.googleapis.com/compute/v1/projects/myproject/global/networks/default",
      "networkIP": "11.128.0.2",
      "subnetwork": "https://www.googleapis.com/compute/v1/projects/myproject/regions/us-west1/subnetworks/default"
    }
  ],
  "scheduling": {
    "automaticRestart": false,
    "onHostMaintenance": "TERMINATE",
    "preemptible": true
  },
  "selfLink": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b/instances/instance-1",
  "serviceAccounts": [
    {
      "email": "zzzzzzz-compute@developer.gserviceaccount.com",
      "scopes": [
        "https://www.googleapis.com/auth/cloud-platform"
      ]
    }
  ],
  "status": "RUNNING",
  "tags": {
    "items": [
      "http-server",
      "https-server"
    ]
  },
  "zone": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b"
}`
)

func (s *service) registerRoutes() {
	s.Register(&endly.Route{
		Action: "call",
		RequestInfo: &endly.ActionInfo{
			Description: "call proxies request into google.golang.org/api/compute/v1.Service client",
			Examples: []*endly.UseCase{
				{
					Description: "get instance status",
					Data:        gceGetInstanceStatusExample,
				},
				{
					Description: "get instance status response",
					Data:        gceGetInstanceStatusResponseExample,
				},
			},
		},
		RequestProvider: func() interface{} {
			return &CallRequest{}
		},
		ResponseProvider: func() interface{} {
			return struct{}{}
		},
		Handler: func(context *endly.Context, request interface{}) (interface{}, error) {
			if req, ok := request.(*CallRequest); ok {
				return s.call(context, req)
			}
			return nil, fmt.Errorf("unsupported request type: %T", request)
		},
	})
}

//New creates a new NoOperation service.
func New() endly.Service {
	var result = &service{
		AbstractService: endly.NewAbstractService(ServiceID),
	}
	result.AbstractService.Service = result
	result.registerRoutes()
	return result
}
<commit_msg>corrected documentation<commit_after>package gce

import (
	"fmt"
	"github.com/viant/endly"
	"github.com/viant/toolbox"
	"google.golang.org/api/compute/v1"
)

const (
	//ServiceID represents gce service id.
	ServiceID = "gce"
)

//google comput engine service
type service struct {
	*endly.AbstractService
}

func (s *service) fetchInstanceList(context *endly.Context, request *CallRequest) (CallResponse, error) {
	var response interface{}
	config, err := context.Secrets.GetCredentials(request.Credentials)
	if err != nil {
		return nil, err
	}
	computeClient, ctx, err := NewComputeService(config)
	if err != nil {
		return nil, err
	}
	project := toolbox.AsString(request.Parameters[0])
	zone := toolbox.AsString(request.Parameters[1])
	req := computeClient.Instances.List(project, zone)
	var instances = make([]*compute.Instance, 0)
	err = req.Pages(ctx, func(list *compute.InstanceList) error {
		instances = append(instances, list.Items...)
		return nil
	})
	if err != nil {
		return nil, err
	}
	response = instances
	return response, nil
}

func (s *service) call(context *endly.Context, request *CallRequest) (CallResponse, error) {
	if request.Service == "Instances" && request.Method == "List" {
		return s.fetchInstanceList(context, request)
	}
	config, err := context.Secrets.GetCredentials(request.Credentials)
	if err != nil {
		return nil, err
	}
	var response interface{}
	computeClient, ctx, err := NewComputeService(config)
	if err != nil {
		return nil, err
	}
	computeService, err := GetComputeService(computeClient, request.Service)

	method, err := toolbox.GetFunction(computeService, request.Method)
	if err != nil {
		return nil, err
	}
	parameters, err := toolbox.AsCompatibleFunctionParameters(method, request.Parameters)
	if err != nil {
		return nil, err
	}
	functionResult := toolbox.CallFunction(method, parameters...)
	if len(functionResult) != 1 {
		return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
	}
	callRequest := functionResult[0]
	contextFunction, err := toolbox.GetFunction(callRequest, "Context")
	if err != nil {
		return nil, err
	}
	contextResult := toolbox.CallFunction(contextFunction, ctx)
	if len(contextResult) != 1 {
		return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
	}

	if doFunction, err := toolbox.GetFunction(callRequest, "Do"); err == nil {
		doResult := toolbox.CallFunction(doFunction)
		if len(doResult) > 0 {
			if err, ok := doResult[len(doResult)-1].(error); ok {
				return nil, err
			}
			response = doResult[0]
			return response, nil
		}
	}
	return nil, fmt.Errorf("unsupported operation: %v.%v", request.Service, request.Method)
}

const (
	gceGetInstanceStatusExample = `{
  "Credentials": "${env.HOME}/.secret/gce.json",
  "Service": "Instances",
  "Method": "Get",
  "Parameters":["myProject","us-west1-b","instance-1"]
}
`
	gceGetInstanceStatusResponseExample = `{
  "cpuPlatform": "Intel Skylake",
  "id": "2222222254251138088",
  "kind": "compute#instance",
  "machineType": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b/machineTypes/n1-standard-8",
  "metadata": {
    "kind": "compute#metadata"
  },
  "name": "instance-1",
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "kind": "compute#accessConfig",
          "name": "External NAT",
          "natIP": "235.147.19.148",
          "type": "ONE_TO_ONE_NAT"
        }
      ],
      "kind": "compute#networkInterface",
      "name": "nic0",
      "network": "https://www.googleapis.com/compute/v1/projects/myproject/global/networks/default",
      "networkIP": "11.128.0.2",
      "subnetwork": "https://www.googleapis.com/compute/v1/projects/myproject/regions/us-west1/subnetworks/default"
    }
  ],
  "scheduling": {
    "automaticRestart": false,
    "onHostMaintenance": "TERMINATE",
    "preemptible": true
  },
  "selfLink": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b/instances/instance-1",
  "serviceAccounts": [
    {
      "email": "zzzzzzz-compute@developer.gserviceaccount.com",
      "scopes": [
        "https://www.googleapis.com/auth/cloud-platform"
      ]
    }
  ],
  "status": "RUNNING",
  "tags": {
    "items": [
      "http-server",
      "https-server"
    ]
  },
  "zone": "https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-b"
}`
)

func (s *service) registerRoutes() {
	s.Register(&endly.Route{
		Action: "call",
		RequestInfo: &endly.ActionInfo{
			Description: "call proxies request into google.golang.org/api/compute/v1.Service client",
			Examples: []*endly.UseCase{
				{
					Description: "get instance status",
					Data:        gceGetInstanceStatusExample,
				},
				{
					Description: "get instance status response",
					Data:        gceGetInstanceStatusResponseExample,
				},
			},
		},
		RequestProvider: func() interface{} {
			return &CallRequest{}
		},
		ResponseProvider: func() interface{} {
			return struct{}{}
		},
		Handler: func(context *endly.Context, request interface{}) (interface{}, error) {
			if req, ok := request.(*CallRequest); ok {
				return s.call(context, req)
			}
			return nil, fmt.Errorf("unsupported request type: %T", request)
		},
	})
}

//New creates a new GCE service.
func New() endly.Service {
	var result = &service{
		AbstractService: endly.NewAbstractService(ServiceID),
	}
	result.AbstractService.Service = result
	result.registerRoutes()
	return result
}
<|endoftext|>


<commit_before>package capacitor

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"
)

var azureEndPoint string
var azureAuth string
var local bool

type CapacitorPoint struct {
	config      Configuration
	wkl         int
	performance float64
}

type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

type ML struct {
	capPoints []CapacitorPoint
	uslThrput *USL
}

type Points []Point

type USL struct {
	Points                 Points
	Alpha, Beta, R2, N, Y1 float64
	Y1IsMax                bool
}

func init() {
	local = true
	data, err := ioutil.ReadFile("config/azureml.yml")
	if err != nil {
		log.Printf("error reading azure file: %v\n", err)
		data, err = ioutil.ReadFile(os.Getenv("GOPATH") + "/src/github.com/mathcunha/CloudCapacitor/config/azureml.yml")
		if err != nil {
			log.Printf("error reading azure file: %v\n", err)
			return
		}
	}

	config := struct {
		Auth     string
		Endpoint string
	}{}
	err = yaml.Unmarshal([]byte(data), &config)
	if err != nil {
		log.Printf("error parsing azure file:%v\n", err)
		return
	}
	azureEndPoint = config.Endpoint
	azureAuth = config.Auth
	local = false
}

func NewML(capPoints []CapacitorPoint) (ml ML) {
	ml.capPoints = capPoints
	if points := pointsByThroughput(capPoints); len(points) > 1 {
		usl := USL{Points: points}
		usl.BuildUSL()
		ml.uslThrput = &usl
	}
	return
}

func (ml *ML) Predict(capPoint CapacitorPoint) (performance float64, model string) {
	performance = -1
	model = ""

	if points := pointsByConfiguration(ml.capPoints, capPoint); len(points) > 1 {
		usl := USL{Points: points}
		usl.BuildUSL()
		if isBetweenPoints(points, float64(capPoint.wkl)) {
			performance = usl.Predict(float64(capPoint.wkl))
			model = "uslByConfig"
			return
		}
	}
	/*
		if points := pointsByWorkload(capPoints, capPoint); len(points) > 1 {
			usl := USL{Points: points, Y1IsMax: true}
			usl.BuildUSL()
			if isBetweenPoints(points, workloadModelX(capPoint)) && usl.R2 >= 0.7 {
				performance = usl.Predict(workloadModelX(capPoint))
				model = "uslByWorkload"
				return
			}
		}
	*/
	if ml.uslThrput != nil && ml.uslThrput.R2 >= 0.7 {
		performance = float64(capPoint.wkl) / ml.uslThrput.Predict(workloadModelX(capPoint))
		model = "uslByThrput"
		return
	}
	return
}

func isBetweenPoints(points Points, x float64) bool {
	hasSmaller, hasHigher := false, false
	for _, v := range points {
		if v.X < x {
			hasSmaller = true
		}
		if v.X > x {
			hasHigher = true
		}
		if hasSmaller && hasHigher {
			break
		}
	}
	return hasSmaller && hasHigher
}

func pointsByThroughput(capPoints []CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		points = append(points, Point{workloadModelX(v), float64(v.wkl) / v.performance})
	}
	return
}

func pointsByConfiguration(capPoints []CapacitorPoint, capPoint CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		if v.config.Size == capPoint.config.Size && v.config.VM.Name == capPoint.config.VM.Name {
			points = append(points, Point{float64(v.wkl), v.performance})
		}
	}
	return
}

func workloadModelX(v CapacitorPoint) (x float64) {
	x = (0.3 * float64(v.config.Size)) + (0.7 * float64(v.config.VM.CPU))
	return
}

func pointsByWorkload(capPoints []CapacitorPoint, capPoint CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		if v.wkl == capPoint.wkl {
			points = append(points, Point{float64(workloadModelX(v)), float64(v.performance)})
		}
	}
	return
}

func (u *USL) Predict(x float64) (y float64) {
	kappa := u.Beta
	sigma := u.Alpha + kappa
	y = x / (1 + (sigma * (x - 1)) + (kappa * x * (x - 1)))
	y *= u.Y1
	return
}

func (u *USL) callRScript(points string) {
	cmd := exec.Command("Rscript", "--vanilla", os.Getenv("GOPATH")+"/src/github.com/mathcunha/CloudCapacitor/config/usl.R", points)
	var out bytes.Buffer
	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		fmt.Printf("callRScript error calling Rscript :%v\n", err)
		return
	}
	if err := json.Unmarshal(out.Bytes(), &[]*USL{u}); err != nil {
		log.Printf("callRScript error parsing response %v: %v\n", out.String(), err)
	}
	return
}

func (u *USL) callAzureML(points string) {
	body := fmt.Sprintf("{\"Inputs\": {\"input1\": {\"ColumnNames\": [\"points\"], \"Values\": [[%q]]}}, \"GlobalParameters\": {}}", points)
	out, err := CallAzureMLService(body, azureAuth, azureEndPoint)
	if err != nil {
		log.Printf("BuildUSL error calling AzureMLService: %v\n", err)
	} else {
		values := struct {
			Results struct {
				Output1 struct {
					//Value map[string][][]string
					Value struct {
						Values [][]string
					}
				} `json:"output1"`
			}
		}{}

		err = json.Unmarshal([]byte(out), &values)
		if err != nil {
			log.Printf("BuildUSL error parsing response %v: %v\n", out, err)
		} else {
			usl := values.Results.Output1.Value.Values[0][0]
			usl = usl[1 : len(usl)-1]
			err = json.Unmarshal([]byte(usl), u)
			if err != nil {
				log.Printf("BuildUSL error parsing response %v: %v\n", usl, err)
			}
		}
	}
}

func (u *USL) BuildUSL() {
	u.R2 = -1
	smaller := u.Points[0]
	for _, v := range u.Points {
		if v.X < smaller.X {
			smaller = v
		}
	}

	points := Points{}
	if smaller.X != 1 {
		//factor := 1.0 / smaller.X
		if u.Y1IsMax {
			smaller.X, smaller.Y = 1, smaller.Y*smaller.X
		} else {
			smaller.X, smaller.Y = 1, smaller.Y/smaller.X
		}
		points = append(points, smaller)
	}

	points = append(points, u.Points...)
	u.Y1 = points[0].Y

	buf := bytes.NewBufferString("")
	json.NewEncoder(buf).Encode(points)
	if local {
		u.callRScript(buf.String())
	} else {
		u.callAzureML(buf.String())
	}
}

func CallAzureMLService(body, auth, endpoint string) (out string, err error) {
	//fmt.Printf("calling AzureMLService, endpoint:%s, auth:%s, body:%s\n", endpoint, auth, body)
	req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(body))
	if err != nil {
		return "", err
	}
	cli := &http.Client{Timeout: 2 * time.Minute}
	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/json")
	res, err := cli.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()
	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	return string(resBody), nil
}
<commit_msg>AzurelML error handling<commit_after>package capacitor

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"
)

var azureEndPoint string
var azureAuth string
var local bool

type CapacitorPoint struct {
	config      Configuration
	wkl         int
	performance float64
}

type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

type ML struct {
	capPoints []CapacitorPoint
	uslThrput *USL
}

type Points []Point

type USL struct {
	Points                 Points
	Alpha, Beta, R2, N, Y1 float64
	Y1IsMax                bool
}

func init() {
	local = true
	data, err := ioutil.ReadFile("config/azureml.yml")
	if err != nil {
		log.Printf("error reading azure file: %v\n", err)
		data, err = ioutil.ReadFile(os.Getenv("GOPATH") + "/src/github.com/mathcunha/CloudCapacitor/config/azureml.yml")
		if err != nil {
			log.Printf("error reading azure file: %v\n", err)
			return
		}
	}

	config := struct {
		Auth     string
		Endpoint string
	}{}
	err = yaml.Unmarshal([]byte(data), &config)
	if err != nil {
		log.Printf("error parsing azure file:%v\n", err)
		return
	}
	azureEndPoint = config.Endpoint
	azureAuth = config.Auth
	local = false
}

func NewML(capPoints []CapacitorPoint) (ml ML) {
	ml.capPoints = capPoints
	if points := pointsByThroughput(capPoints); len(points) > 1 {
		usl := USL{Points: points}
		usl.BuildUSL()
		ml.uslThrput = &usl
	}
	return
}

func (ml *ML) Predict(capPoint CapacitorPoint) (performance float64, model string) {
	performance = -1
	model = ""

	if points := pointsByConfiguration(ml.capPoints, capPoint); len(points) > 1 {
		usl := USL{Points: points}
		usl.BuildUSL()
		if isBetweenPoints(points, float64(capPoint.wkl)) {
			performance = usl.Predict(float64(capPoint.wkl))
			model = "uslByConfig"
			return
		}
	}
	/*
		if points := pointsByWorkload(capPoints, capPoint); len(points) > 1 {
			usl := USL{Points: points, Y1IsMax: true}
			usl.BuildUSL()
			if isBetweenPoints(points, workloadModelX(capPoint)) && usl.R2 >= 0.7 {
				performance = usl.Predict(workloadModelX(capPoint))
				model = "uslByWorkload"
				return
			}
		}
	*/
	if ml.uslThrput != nil && ml.uslThrput.R2 >= 0.7 {
		performance = float64(capPoint.wkl) / ml.uslThrput.Predict(workloadModelX(capPoint))
		model = "uslByThrput"
		return
	}
	return
}

func isBetweenPoints(points Points, x float64) bool {
	hasSmaller, hasHigher := false, false
	for _, v := range points {
		if v.X < x {
			hasSmaller = true
		}
		if v.X > x {
			hasHigher = true
		}
		if hasSmaller && hasHigher {
			break
		}
	}
	return hasSmaller && hasHigher
}

func pointsByThroughput(capPoints []CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		points = append(points, Point{workloadModelX(v), float64(v.wkl) / v.performance})
	}
	return
}

func pointsByConfiguration(capPoints []CapacitorPoint, capPoint CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		if v.config.Size == capPoint.config.Size && v.config.VM.Name == capPoint.config.VM.Name {
			points = append(points, Point{float64(v.wkl), v.performance})
		}
	}
	return
}

func workloadModelX(v CapacitorPoint) (x float64) {
	x = (0.3 * float64(v.config.Size)) + (0.7 * float64(v.config.VM.CPU))
	return
}

func pointsByWorkload(capPoints []CapacitorPoint, capPoint CapacitorPoint) (points Points) {
	for _, v := range capPoints {
		if v.wkl == capPoint.wkl {
			points = append(points, Point{float64(workloadModelX(v)), float64(v.performance)})
		}
	}
	return
}

func (u *USL) Predict(x float64) (y float64) {
	kappa := u.Beta
	sigma := u.Alpha + kappa
	y = x / (1 + (sigma * (x - 1)) + (kappa * x * (x - 1)))
	y *= u.Y1
	return
}

func (u *USL) callRScript(points string) {
	cmd := exec.Command("Rscript", "--vanilla", os.Getenv("GOPATH")+"/src/github.com/mathcunha/CloudCapacitor/config/usl.R", points)
	var out bytes.Buffer
	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		fmt.Printf("callRScript error calling Rscript :%v\n", err)
		return
	}
	if err := json.Unmarshal(out.Bytes(), &[]*USL{u}); err != nil {
		log.Printf("callRScript error parsing response %v: %v\n", out.String(), err)
	}
	return
}

func (u *USL) callAzureML(points string) {
	body := fmt.Sprintf("{\"Inputs\": {\"input1\": {\"ColumnNames\": [\"points\"], \"Values\": [[%q]]}}, \"GlobalParameters\": {}}", points)
	out, err := CallAzureMLService(body, azureAuth, azureEndPoint)
	if err != nil {
		log.Printf("BuildUSL error calling AzureMLService: %v\n", err)
	} else {
		values := struct {
			Results struct {
				Output1 struct {
					//Value map[string][][]string
					Value struct {
						Values [][]string
					}
				} `json:"output1"`
			}
		}{}

		err = json.Unmarshal([]byte(out), &values)
		if err != nil {
			log.Printf("BuildUSL error parsing response %v: %v\n", out, err)
		} else {
			if len(values.Results.Output1.Value.Values) > 0 {
				usl := values.Results.Output1.Value.Values[0][0]
				usl = usl[1 : len(usl)-1]
				err = json.Unmarshal([]byte(usl), u)
				if err != nil {
					log.Printf("BuildUSL error parsing response %v: %v\n", usl, err)
				}
			} else {
				log.Printf("BuildUSL error parsing response %v\n", out)
			}
		}
	}
}

func (u *USL) BuildUSL() {
	u.R2 = -1
	smaller := u.Points[0]
	for _, v := range u.Points {
		if v.X < smaller.X {
			smaller = v
		}
	}

	points := Points{}
	if smaller.X != 1 {
		//factor := 1.0 / smaller.X
		if u.Y1IsMax {
			smaller.X, smaller.Y = 1, smaller.Y*smaller.X
		} else {
			smaller.X, smaller.Y = 1, smaller.Y/smaller.X
		}
		points = append(points, smaller)
	}

	points = append(points, u.Points...)
	u.Y1 = points[0].Y

	buf := bytes.NewBufferString("")
	json.NewEncoder(buf).Encode(points)
	if local {
		u.callRScript(buf.String())
	} else {
		u.callAzureML(buf.String())
	}
}

func CallAzureMLService(body, auth, endpoint string) (out string, err error) {
	//fmt.Printf("calling AzureMLService, endpoint:%s, auth:%s, body:%s\n", endpoint, auth, body)
	req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(body))
	if err != nil {
		return "", err
	}
	cli := &http.Client{Timeout: 2 * time.Minute}
	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/json")
	res, err := cli.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()
	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	return string(resBody), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

// <Document>
// <Element>
// <ATCCODE>A10BA02</ATCCODE>
// <馬偕碼>25703</馬偕碼>
// <起日>2016/6/2 上午 12:00:00</起日>
// <迄日>2113/12/28 上午 12:00:00</迄日>
// <用途>糖尿病用藥</用途>
// <名稱>METFORMIN</名稱>
// <商品名-中文>克醣錠500公絲</商品名-中文>
// <商品名-英文>UFORMIN TABLETS 500MG</商品名-英文>
// <藥品外觀>白色長橢圓形扁錠，一面中央有一刻痕並有"MF"及"1"字樣</藥品外觀>
// <用藥須知>常見不良反應為腸胃不適(如噁心、腹瀉)飯後服用可減少此作</用藥須知>
// <飲食方面注意事項>1.確實遵守醫師和營養師的運動和膳食建議，且食用健康膳食。 2.酒會影響血糖，服藥期間避免飲酒。</飲食方面注意事項>
// <藥品副作用與處置方式>
// 1.最常見但較輕微的副作用包括：腹瀉、噁心及胃不適，與食物併服會降低這些副作用。 2.偶爾會引起維它命B12吸收不良，導致貧血及周邊神經症狀。 3.最嚴重但較少發生的副作用就是“乳酸中毒”（不尋常的疲憊感、嚴重的嗜睡、眩暈、肌肉疼痛、呼吸困難或急促、心跳過慢或不規則、體液滯留）須立即就醫。
// </藥品副作用與處置方式>
// <圖片更新日期>2014/10/18 上午 11:02:55</圖片更新日期>
// <簡稱>■糖 Metformin 500mg(UFORMIN*)</簡稱>
// <商品名>■UFORMIN * 500mg Tab 克醣錠</商品名>
// </Element>
// <Version>20160621V1</Version>
// </Document>

// Query is an exported type that
type Query struct {
	Version string     `xml:"Version"`
	Element []drugInfo `xml:"Element"`
}

type drugInfo struct {
	ATCCODE        string `xml:"ATCCODE"`
	MMHCode        string `xml:"馬偕碼"`
	DateFrom       string `xml:"起日"`
	DateEnd        string `xml:"迄日"`
	Usage          string `xml:"用途"`
	NameCht        string `xml:"商品名-中文"`
	NameEng        string `xml:"商品名-英文"`
	DrugLook       string `xml:"藥品外觀"`
	DurgGuide      string `xml:"用藥須知"`
	Precautions    string `xml:"注意事項"`
	DrugSideEffect string `xml:"藥品副作用與處置方式"`
	ImageUdateDate string `xml:"圖片更新日期"`
	NameShort      string `xml:"簡稱"`
	Name           string `xml:"商品名"`
}

func main() {
	// curl --data "sDate=2000/01/01"  https://wapps.mmh.org.tw/WS_Drug/WebService.asmx/GetDrugInfo_DI
	resp, err := http.PostForm("https://wapps.mmh.org.tw/WS_Drug/WebService.asmx/GetDrugInfo_DI",
		url.Values{"sDate": {"2000/01/01"}})

	if err != nil {
		// handle error
	}

	defer resp.Body.Close()
	xmlFile, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		// handle error
	}

	var q Query
	err = xml.Unmarshal(xmlFile, &q)
	if err != nil {
		fmt.Println("Error XML Decode :", err)
		return
	}

	fmt.Println(q)

	for _, DrugInfo := range q.Element {
		fmt.Println(DrugInfo.ATCCODE, DrugInfo.Name, DrugInfo.Precautions)
	}
}
<commit_msg>Resolve empty element problem while decode XML<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// <Document>
// <Element>
// <ATCCODE>A10BA02</ATCCODE>
// <馬偕碼>25703</馬偕碼>
// <起日>2016/6/2 上午 12:00:00</起日>
// <迄日>2113/12/28 上午 12:00:00</迄日>
// <用途>糖尿病用藥</用途>
// <名稱>METFORMIN</名稱>
// <商品名-中文>克醣錠500公絲</商品名-中文>
// <商品名-英文>UFORMIN TABLETS 500MG</商品名-英文>
// <藥品外觀>白色長橢圓形扁錠，一面中央有一刻痕並有"MF"及"1"字樣</藥品外觀>
// <用藥須知>常見不良反應為腸胃不適(如噁心、腹瀉)飯後服用可減少此作</用藥須知>
// <飲食方面注意事項>1.確實遵守醫師和營養師的運動和膳食建議，且食用健康膳食。 2.酒會影響血糖，服藥期間避免飲酒。</飲食方面注意事項>
// <藥品副作用與處置方式>
// 1.最常見但較輕微的副作用包括：腹瀉、噁心及胃不適，與食物併服會降低這些副作用。 2.偶爾會引起維它命B12吸收不良，導致貧血及周邊神經症狀。 3.最嚴重但較少發生的副作用就是“乳酸中毒”（不尋常的疲憊感、嚴重的嗜睡、眩暈、肌肉疼痛、呼吸困難或急促、心跳過慢或不規則、體液滯留）須立即就醫。
// </藥品副作用與處置方式>
// <圖片更新日期>2014/10/18 上午 11:02:55</圖片更新日期>
// <簡稱>■糖 Metformin 500mg(UFORMIN*)</簡稱>
// <商品名>■UFORMIN * 500mg Tab 克醣錠</商品名>
// </Element>
// <Version>20160621V1</Version>
// </Document>

// Query is an exported type that
type Query struct {
	Version string     `xml:"Version"`
	Element []drugInfo `xml:"Element"`
}

type drugInfo struct {
	ATCCODE        string `xml:"ATCCODE"`
	MMHCode        string `xml:"馬偕碼"`
	DateFrom       string `xml:"起日"`
	DateEnd        string `xml:"迄日"`
	Usage          string `xml:"用途"`
	NameCht        string `xml:"商品名-中文"`
	NameEng        string `xml:"商品名-英文"`
	DrugLook       string `xml:"藥品外觀"`
	DurgGuide      string `xml:"用藥須知"`
	Precautions    string `xml:"注意事項"`
	DrugSideEffect string `xml:"藥品副作用與處置方式"`
	ImageUdateDate string `xml:"圖片更新日期"`
	NameShort      string `xml:"簡稱"`
	Name           string `xml:"商品名"`
}

func detectEmptyElement(input string) string {
	if len(strings.TrimSpace(input)) > 0 {
		return input
	}
	return "EMPTY"
}

func main() {
	// curl --data "sDate=2000/01/01"  https://wapps.mmh.org.tw/WS_Drug/WebService.asmx/GetDrugInfo_DI
	resp, err := http.PostForm("https://wapps.mmh.org.tw/WS_Drug/WebService.asmx/GetDrugInfo_DI",
		url.Values{"sDate": {"2000/01/01"}})

	if err != nil {
		fmt.Println("Error XML Server Error :", err)
	}

	defer resp.Body.Close()
	xmlFile, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error Read XML from resp.Body :", err)
	}

	var q Query
	err = xml.Unmarshal(xmlFile, &q)
	if err != nil {
		fmt.Println("Error XML Decode :", err)
		return
	}

	for _, DrugInfo := range q.Element {
		fmt.Printf("[ATTCCODE]%s [馬偕碼]%s [簡名]%s \n  ==> [商品名-英文]%s \n\n ", detectEmptyElement(DrugInfo.ATCCODE), detectEmptyElement(DrugInfo.MMHCode), detectEmptyElement(DrugInfo.Name), DrugInfo.NameEng)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"

	buildahcli "github.com/containers/buildah/pkg/cli"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

type jsonMount struct {
	Container  string `json:"container,omitempty"`
	MountPoint string `json:"mountPoint"`
}

func init() {
	var (
		mountDescription = `buildah mount
  mounts a working container's root filesystem for manipulation.

  Note:  In rootless mode you need to first execute buildah unshare, to put you
  into the usernamespace. Afterwards you can buildah mount the container and
  view/modify the content in the containers root file system.
`
		noTruncate bool
		outputJSON bool
	)
	mountCommand := &cobra.Command{
		Use:   "mount",
		Short: "Mount a working container's root filesystem",
		Long:  mountDescription,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mountCmd(cmd, args, outputJSON)
		},
		Example: `buildah mount
  buildah mount containerID
  buildah mount containerID1 containerID2

  In rootless mode you must use buildah unshare first.
  buildah unshare
  buildah mount containerID
`,
	}
	mountCommand.SetUsageTemplate(UsageTemplate())

	flags := mountCommand.Flags()
	flags.SetInterspersed(false)
	flags.BoolVar(&outputJSON, "json", false, "output in JSON format")
	flags.BoolVar(&noTruncate, "notruncate", false, "do not truncate output")
	rootCmd.AddCommand(mountCommand)
	if err := flags.MarkHidden("notruncate"); err != nil {
		logrus.Fatalf("error marking notruncate as hidden: %v", err)
	}
}

func mountCmd(c *cobra.Command, args []string, outputJSON bool) error {

	if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
		return err
	}

	store, err := getStore(c)
	if err != nil {
		return err
	}
	var jsonMounts []jsonMount
	var lastError error
	if len(args) > 0 {
		// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
		// of the mount command.
		// Differently, allow the mount if we are already in a userns, as the mount point will still
		// be accessible once "buildah mount" exits.
		if os.Geteuid() != 0 && store.GraphDriverName() != "vfs" {
			return errors.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", store.GraphDriverName())
		}

		for _, name := range args {
			builder, err := openBuilder(getContext(), store, name)
			if err != nil {
				if lastError != nil {
					fmt.Fprintln(os.Stderr, lastError)
				}
				lastError = errors.Wrapf(err, "error reading build container %q", name)
				continue
			}
			mountPoint, err := builder.Mount(builder.MountLabel)
			if err != nil {
				if lastError != nil {
					fmt.Fprintln(os.Stderr, lastError)
				}
				lastError = errors.Wrapf(err, "error mounting %q container %q", name, builder.Container)
				continue
			}
			if len(args) > 1 {
				if outputJSON {
					jsonMounts = append(jsonMounts, jsonMount{Container: name, MountPoint: mountPoint})
					continue
				}
				fmt.Printf("%s %s\n", name, mountPoint)
			} else {
				if outputJSON {
					jsonMounts = append(jsonMounts, jsonMount{MountPoint: mountPoint})
					continue
				}
				fmt.Printf("%s\n", mountPoint)
			}
		}
	} else {
		builders, err := openBuilders(store)
		if err != nil {
			return errors.Wrapf(err, "error reading build containers")
		}

		for _, builder := range builders {
			mounted, err := builder.Mounted()
			if err != nil {
				return err
			}
			if mounted {
				if outputJSON {
					jsonMounts = append(jsonMounts, jsonMount{Container: builder.Container, MountPoint: builder.MountPoint})
					continue
				}
				fmt.Printf("%s %s\n", builder.Container, builder.MountPoint)
			}
		}
	}

	if outputJSON {
		data, err := json.MarshalIndent(jsonMounts, "", "    ")
		if err != nil {
			return err
		}
		fmt.Printf("%s\n", data)
	}

	return lastError
}
<commit_msg>Create mountOptions for mount command flags<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"

	buildahcli "github.com/containers/buildah/pkg/cli"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

type jsonMount struct {
	Container  string `json:"container,omitempty"`
	MountPoint string `json:"mountPoint"`
}

type mountOptions struct {
	json bool
}

func init() {
	var (
		mountDescription = `buildah mount
  mounts a working container's root filesystem for manipulation.

  Note:  In rootless mode you need to first execute buildah unshare, to put you
  into the usernamespace. Afterwards you can buildah mount the container and
  view/modify the content in the containers root file system.
`
		opts       mountOptions
		noTruncate bool
	)
	mountCommand := &cobra.Command{
		Use:   "mount",
		Short: "Mount a working container's root filesystem",
		Long:  mountDescription,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mountCmd(cmd, args, opts)
		},
		Example: `buildah mount
  buildah mount containerID
  buildah mount containerID1 containerID2

  In rootless mode you must use buildah unshare first.
  buildah unshare
  buildah mount containerID
`,
	}
	mountCommand.SetUsageTemplate(UsageTemplate())

	flags := mountCommand.Flags()
	flags.SetInterspersed(false)
	flags.BoolVar(&opts.json, "json", false, "output in JSON format")
	flags.BoolVar(&noTruncate, "notruncate", false, "do not truncate output")
	rootCmd.AddCommand(mountCommand)
	if err := flags.MarkHidden("notruncate"); err != nil {
		logrus.Fatalf("error marking notruncate as hidden: %v", err)
	}
}

func mountCmd(c *cobra.Command, args []string, opts mountOptions) error {

	if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
		return err
	}

	store, err := getStore(c)
	if err != nil {
		return err
	}
	var jsonMounts []jsonMount
	var lastError error
	if len(args) > 0 {
		// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
		// of the mount command.
		// Differently, allow the mount if we are already in a userns, as the mount point will still
		// be accessible once "buildah mount" exits.
		if os.Geteuid() != 0 && store.GraphDriverName() != "vfs" {
			return errors.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", store.GraphDriverName())
		}

		for _, name := range args {
			builder, err := openBuilder(getContext(), store, name)
			if err != nil {
				if lastError != nil {
					fmt.Fprintln(os.Stderr, lastError)
				}
				lastError = errors.Wrapf(err, "error reading build container %q", name)
				continue
			}
			mountPoint, err := builder.Mount(builder.MountLabel)
			if err != nil {
				if lastError != nil {
					fmt.Fprintln(os.Stderr, lastError)
				}
				lastError = errors.Wrapf(err, "error mounting %q container %q", name, builder.Container)
				continue
			}
			if len(args) > 1 {
				if opts.json {
					jsonMounts = append(jsonMounts, jsonMount{Container: name, MountPoint: mountPoint})
					continue
				}
				fmt.Printf("%s %s\n", name, mountPoint)
			} else {
				if opts.json {
					jsonMounts = append(jsonMounts, jsonMount{MountPoint: mountPoint})
					continue
				}
				fmt.Printf("%s\n", mountPoint)
			}
		}
	} else {
		builders, err := openBuilders(store)
		if err != nil {
			return errors.Wrapf(err, "error reading build containers")
		}

		for _, builder := range builders {
			mounted, err := builder.Mounted()
			if err != nil {
				return err
			}
			if mounted {
				if opts.json {
					jsonMounts = append(jsonMounts, jsonMount{Container: builder.Container, MountPoint: builder.MountPoint})
					continue
				}
				fmt.Printf("%s %s\n", builder.Container, builder.MountPoint)
			}
		}
	}

	if opts.json {
		data, err := json.MarshalIndent(jsonMounts, "", "    ")
		if err != nil {
			return err
		}
		fmt.Printf("%s\n", data)
	}

	return lastError
}
<|endoftext|>


<commit_before>package main

const VERSION string = "1.2.2"
<commit_msg>oops, commit version string<commit_after>package main

const VERSION string = "1.3.0"
<|endoftext|>


<commit_before>// The go-vcs program exposes go-vcs's library functionality through a
// command-line interface.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"strings"

	"github.com/kr/text"
	"sourcegraph.com/sourcegraph/go-diff/diff"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/git"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/hg"
)

var (
	sshKeyFile = flag.String("i", "", "ssh key file")
	chdir      = flag.String("C", "", "change directory to this dir before doing anything")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	if flag.NArg() == 0 {
		log.Fatal("Must specify a subcommand.")
	}

	if *chdir != "" {
		if err := os.Chdir(*chdir); err != nil {
			log.Fatal("Chdir:", err)
		}
	}

	subcmd := flag.Arg(0)
	args := flag.Args()[1:]
	switch subcmd {
	case "git-clone-mirror":
		if len(args) != 2 {
			log.Fatal("git-clone requires 2 args: clone URL and dir.")
		}
		cloneURLStr, dir := args[0], args[1]

		cloneURL, err := url.Parse(cloneURLStr)
		if err != nil {
			log.Fatal(err)
		}

		if _, err := os.Stat(dir); !os.IsNotExist(err) {
			log.Fatalf("Clone destination dir must not exist: %s.", dir)
		}
		if _, err := os.Stat(filepath.Join(dir, "..")); err != nil {
			log.Fatalf("Clone destination dir parent must exist: %s.", filepath.Join(dir, ".."))
		}

		log.Printf("Cloning %s to %s...", cloneURL, dir)

		opt := vcs.CloneOpt{}
		if *sshKeyFile != "" {
			key, err := ioutil.ReadFile(*sshKeyFile)
			if err != nil {
				log.Fatal(err)
			}
			opt.RemoteOpts.SSH = &vcs.SSHConfig{PrivateKey: key}
		}
		repo, err := vcs.Clone("git", cloneURL.String(), dir, opt)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Cloned: %T.", repo)

	case "show":
		if len(args) != 1 {
			log.Fatal("show takes 1 argument (revspec).")
		}
		revspec := args[0]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision(revspec)
		if err != nil {
			log.Fatal(err)
		}

		commit, err := repo.GetCommit(commitID)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Revspec %q resolves to commit %s:\n", revspec, commitID)
		printCommit(commit)

	case "show-file":
		if len(args) != 2 {
			log.Fatal("show-file takes 2 arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		f, err := fs.Open(args[1])
		if err != nil {
			log.Fatal(err)
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := os.Stdout.Write(b); err != nil {
			log.Fatal(err)
		}
	case "log":
		if len(args) != 0 {
			log.Fatal("log takes no arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		master, err := repo.ResolveRevision("master")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: master})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Commits (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "diff", "diffstat":
		if len(args) != 2 {
			log.Fatalf("%s takes 2 args (base and head), behavior is like `git diff base...head` (note triple dot).", subcmd)
		}
		baseRev := args[0]
		headRev := args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		base, err := repo.ResolveRevision(baseRev)
		if err != nil {
			log.Fatal(err)
		}
		head, err := repo.ResolveRevision(headRev)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("git diff %s..%s", base, head)

		vdiff, err := repo.(vcs.Differ).Diff(base, head, &vcs.DiffOptions{ExcludeReachableFromBoth: true})
		if err != nil {
			log.Fatal(err)
		}

		switch subcmd {
		case "diff":
			fmt.Println(vdiff.Raw)
		case "diffstat":
			pdiff, err := diff.ParseMultiFileDiff([]byte(vdiff.Raw))
			if err != nil {
				log.Fatal(err)
			}
			for _, fdiff := range pdiff {
				var name string
				if fdiff.NewName == "/dev/null" {
					name = fdiff.OrigName
				} else {
					name = fdiff.NewName
				}
				fmt.Printf("%-50s    ", name)
				st := fdiff.Stat()
				const w = 30
				total := st.Added + st.Changed + st.Deleted
				if st.Added > 0 {
					st.Added = (st.Added*w)/total + 1
				}
				if st.Changed > 0 {
					st.Changed = (st.Changed*w)/total + 1
				}
				if st.Deleted > 0 {
					st.Deleted = (st.Deleted*w)/total + 1
				}
				fmt.Print(strings.Repeat("+", st.Added), strings.Repeat("Δ", st.Changed), strings.Repeat("-", st.Deleted), "\n")
			}
		}

	case "branches":
		if len(args) != 0 {
			log.Fatal("branches takes no arguments.")
		}

		repo, err := vcs.Open("hg", ".")
		if err != nil {
			log.Fatal(err)
		}

		branches, err := repo.Branches()
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Branches (%d total):\n", len(branches))
		for _, b := range branches {
			fmt.Printf("%s %s\n", b.Head, b.Name)
		}
	}
}

func printCommit(c *vcs.Commit) {
	fmt.Printf("%s\n%s <%s> at %s\n%s\n\n", c.ID, c.Author.Name, c.Author.Email, c.Author.Date, text.Indent(c.Message, "\t"))
}
<commit_msg>add blame cmd<commit_after>// The go-vcs program exposes go-vcs's library functionality through a
// command-line interface.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"strings"

	"github.com/kr/text"
	"sourcegraph.com/sourcegraph/go-diff/diff"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/gitcmd"
	//_ "sourcegraph.com/sourcegraph/go-vcs/vcs/git"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/hg"
)

var (
	sshKeyFile = flag.String("i", "", "ssh key file")
	chdir      = flag.String("C", "", "change directory to this dir before doing anything")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	if flag.NArg() == 0 {
		log.Fatal("Must specify a subcommand.")
	}

	if *chdir != "" {
		if err := os.Chdir(*chdir); err != nil {
			log.Fatal("Chdir:", err)
		}
	}

	subcmd := flag.Arg(0)
	args := flag.Args()[1:]
	switch subcmd {
	case "git-clone-mirror":
		if len(args) != 2 {
			log.Fatal("git-clone requires 2 args: clone URL and dir.")
		}
		cloneURLStr, dir := args[0], args[1]

		cloneURL, err := url.Parse(cloneURLStr)
		if err != nil {
			log.Fatal(err)
		}

		if _, err := os.Stat(dir); !os.IsNotExist(err) {
			log.Fatalf("Clone destination dir must not exist: %s.", dir)
		}
		if _, err := os.Stat(filepath.Join(dir, "..")); err != nil {
			log.Fatalf("Clone destination dir parent must exist: %s.", filepath.Join(dir, ".."))
		}

		log.Printf("Cloning %s to %s...", cloneURL, dir)

		opt := vcs.CloneOpt{}
		if *sshKeyFile != "" {
			key, err := ioutil.ReadFile(*sshKeyFile)
			if err != nil {
				log.Fatal(err)
			}
			opt.RemoteOpts.SSH = &vcs.SSHConfig{PrivateKey: key}
		}
		repo, err := vcs.Clone("git", cloneURL.String(), dir, opt)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Cloned: %T.", repo)

	case "show":
		if len(args) != 1 {
			log.Fatal("show takes 1 argument (revspec).")
		}
		revspec := args[0]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision(revspec)
		if err != nil {
			log.Fatal(err)
		}

		commit, err := repo.GetCommit(commitID)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Revspec %q resolves to commit %s:\n", revspec, commitID)
		printCommit(commit)

	case "show-file":
		if len(args) != 2 {
			log.Fatal("show-file takes 2 arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		f, err := fs.Open(args[1])
		if err != nil {
			log.Fatal(err)
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := os.Stdout.Write(b); err != nil {
			log.Fatal(err)
		}
	case "log":
		if len(args) != 0 {
			log.Fatal("log takes no arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		master, err := repo.ResolveRevision("master")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: master})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Commits (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "blame":
		newestCommit, file := args[0], args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		newestCommitID, err := repo.ResolveRevision(newestCommit)
		if err != nil {
			log.Fatal(err)
		}

		hunks, err := repo.(vcs.Blamer).BlameFile(file, &vcs.BlameOptions{NewestCommit: newestCommitID})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Hunks (%d total):\n", len(hunks))
		for _, c := range hunks {
			printHunk(c)
		}

	case "diff", "diffstat":
		if len(args) != 2 {
			log.Fatalf("%s takes 2 args (base and head), behavior is like `git diff base...head` (note triple dot).", subcmd)
		}
		baseRev := args[0]
		headRev := args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		base, err := repo.ResolveRevision(baseRev)
		if err != nil {
			log.Fatal(err)
		}
		head, err := repo.ResolveRevision(headRev)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("git diff %s..%s", base, head)

		vdiff, err := repo.(vcs.Differ).Diff(base, head, &vcs.DiffOptions{ExcludeReachableFromBoth: true})
		if err != nil {
			log.Fatal(err)
		}

		switch subcmd {
		case "diff":
			fmt.Println(vdiff.Raw)
		case "diffstat":
			pdiff, err := diff.ParseMultiFileDiff([]byte(vdiff.Raw))
			if err != nil {
				log.Fatal(err)
			}
			for _, fdiff := range pdiff {
				var name string
				if fdiff.NewName == "/dev/null" {
					name = fdiff.OrigName
				} else {
					name = fdiff.NewName
				}
				fmt.Printf("%-50s    ", name)
				st := fdiff.Stat()
				const w = 30
				total := st.Added + st.Changed + st.Deleted
				if st.Added > 0 {
					st.Added = (st.Added*w)/total + 1
				}
				if st.Changed > 0 {
					st.Changed = (st.Changed*w)/total + 1
				}
				if st.Deleted > 0 {
					st.Deleted = (st.Deleted*w)/total + 1
				}
				fmt.Print(strings.Repeat("+", st.Added), strings.Repeat("Δ", st.Changed), strings.Repeat("-", st.Deleted), "\n")
			}
		}

	case "branches":
		if len(args) != 0 {
			log.Fatal("branches takes no arguments.")
		}

		repo, err := vcs.Open("hg", ".")
		if err != nil {
			log.Fatal(err)
		}

		branches, err := repo.Branches()
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Branches (%d total):\n", len(branches))
		for _, b := range branches {
			fmt.Printf("%s %s\n", b.Head, b.Name)
		}
	}
}

func printCommit(c *vcs.Commit) {
	fmt.Printf("%s\n%s <%s> at %s\n%s\n\n", c.ID, c.Author.Name, c.Author.Email, c.Author.Date, text.Indent(c.Message, "\t"))
}

func printHunk(h *vcs.Hunk) {
	fmt.Printf("L%d-%d b%d-%d\t%s\t%v\n", h.StartLine, h.EndLine, h.StartByte, h.EndByte, h.CommitID, h.Author)
}
<|endoftext|>