<commit_before>// +build windows

/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mount

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"k8s.io/klog"
	utilexec "k8s.io/utils/exec"
	"k8s.io/utils/keymutex"
	utilpath "k8s.io/utils/path"
)

// Mounter provides the default implementation of mount.Interface
// for the windows platform.  This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type Mounter struct {
	mounterPath string
}

// New returns a mount.Interface for the current system.
// It provides options to override the default mounter behavior.
// mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface {
	return &Mounter{
		mounterPath: mounterPath,
	}
}

// acquire lock for smb mount
var getSMBMountMutex = keymutex.NewHashed(0)

// Mount : mounts source to target with given options.
// currently only supports cifs(smb), bind mount(for disk)
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	return mounter.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
}

// MountSensitive is the same as Mount() but this method allows
// sensitiveOptions to be passed in a separate parameter from the normal
// mount options and ensures the sensitiveOptions are never logged. This
// method should be used by callers that pass sensitive material (like
// passwords) as mount options.
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
	target = NormalizeWindowsPath(target)
	sanitizedOptionsForLogging := sanitizedOptionsForLogging(options, sensitiveOptions)

	if source == "tmpfs" {
		klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, sanitizedOptionsForLogging)
		return os.MkdirAll(target, 0755)
	}

	parentDir := filepath.Dir(target)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return err
	}

	klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
		sanitizedOptionsForLogging, source, target, fstype)
	bindSource := source

	// tell it's going to mount azure disk or azure file according to options
	if bind, _, _, _ := MakeBindOptsSensitive(options, sensitiveOptions); bind {
		// mount azure disk
		bindSource = NormalizeWindowsPath(source)
	} else {
		if len(options) < 2 {
			klog.Warningf("mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
				sanitizedOptionsForLogging, len(options), source, target)
			return nil
		}

		// currently only cifs mount is supported
		if strings.ToLower(fstype) != "cifs" {
			return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, sanitizedOptionsForLogging)
		}

		// lock smb mount for the same source
		getSMBMountMutex.LockKey(source)
		defer getSMBMountMutex.UnlockKey(source)

		if output, err := newSMBMapping(options[0], options[1], source); err != nil {
			if isSMBMappingExist(source) {
				klog.V(2).Infof("SMB Mapping(%s) already exists, now begin to remove and remount", source)
				if output, err := removeSMBMapping(source); err != nil {
					return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
				}
				if output, err := newSMBMapping(options[0], options[1], source); err != nil {
					return fmt.Errorf("New-SmbGlobalMapping remount failed: %v, output: %q", err, output)
				}
			} else {
				return fmt.Errorf("New-SmbGlobalMapping failed: %v, output: %q", err, output)
			}
		}
	}

	if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
		klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
		return err
	}

	return nil
}

// do the SMB mount with username, password, remotepath
// return (output, error)
func newSMBMapping(username, password, remotepath string) (string, error) {
	if username == "" || password == "" || remotepath == "" {
		return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, sensitiveOptionsRemoved, remotepath)
	}

	// use PowerShell Environment Variables to store user input string to prevent command line injection
	// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
	cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
		`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
		`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
	cmd := exec.Command("powershell", "/c", cmdLine)
	cmd.Env = append(os.Environ(),
		fmt.Sprintf("smbuser=%s", username),
		fmt.Sprintf("smbpassword=%s", password),
		fmt.Sprintf("smbremotepath=%s", remotepath))

	output, err := cmd.CombinedOutput()
	return string(output), err
}

// check whether remotepath is already mounted
func isSMBMappingExist(remotepath string) bool {
	cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
	cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
	_, err := cmd.CombinedOutput()
	return err == nil
}

// remove SMB mapping
func removeSMBMapping(remotepath string) (string, error) {
	cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
	cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
	output, err := cmd.CombinedOutput()
	return string(output), err
}

// Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error {
	klog.V(4).Infof("azureMount: Unmount target (%q)", target)
	target = NormalizeWindowsPath(target)
	if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
		klog.Errorf("rmdir failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// List returns a list of all mounted filesystems. todo
func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	stat, err := os.Lstat(file)
	if err != nil {
		return true, err
	}
	// If current file is a symlink, then it is a mountpoint.
	if stat.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(file)
		if err != nil {
			return true, fmt.Errorf("readlink error: %v", err)
		}
		exists, err := utilpath.Exists(utilpath.CheckFollowSymlink, target)
		if err != nil {
			return true, err
		}
		return !exists, nil
	}

	return true, nil
}

// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
	windowsPath := NormalizeWindowsPath(pathname)
	pathExists, pathErr := PathExists(windowsPath)
	if !pathExists {
		return []string{}, nil
	} else if IsCorruptedMnt(pathErr) {
		klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
		return []string{}, nil
	} else if pathErr != nil {
		return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
	}
	return []string{pathname}, nil
}

func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
	// Try to mount the disk
	klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)

	if err := ValidateDiskNumber(source); err != nil {
		klog.Errorf("diskMount: formatAndMount failed, err: %v", err)
		return err
	}

	if len(fstype) == 0 {
		// Use 'NTFS' as the default
		fstype = "NTFS"
	}

	// format disk if it is unformatted(raw)
	cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+
		" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
	if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil {
		return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
	}
	klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)

	driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
	if err != nil {
		return err
	}
	driverPath := driveLetter + ":"
	target = NormalizeWindowsPath(target)
	klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
	if output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput(); err != nil {
		klog.Errorf("mklink failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// Get drive letter according to windows disk number
func getDriveLetterByDiskNumber(diskNum string, exec utilexec.Interface) (string, error) {
	cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
	output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
	}
	if len(string(output)) < 1 {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
	}
	return string(output)[:1], nil
}

// getAllParentLinks walks all symbolic links and return all the parent targets recursively
func getAllParentLinks(path string) ([]string, error) {
	const maxIter = 255
	links := []string{}
	for {
		links = append(links, path)
		if len(links) > maxIter {
			return links, fmt.Errorf("unexpected length of parent links: %v", links)
		}

		fi, err := os.Lstat(path)
		if err != nil {
			return links, fmt.Errorf("Lstat: %v", err)
		}
		if fi.Mode()&os.ModeSymlink == 0 {
			break
		}

		path, err = os.Readlink(path)
		if err != nil {
			return links, fmt.Errorf("Readlink error: %v", err)
		}
	}

	return links, nil
}
<commit_msg>Fix windows MountSensitive error<commit_after>// +build windows

/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mount

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"k8s.io/klog"
	utilexec "k8s.io/utils/exec"
	"k8s.io/utils/keymutex"
	utilpath "k8s.io/utils/path"
)

// Mounter provides the default implementation of mount.Interface
// for the windows platform.  This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type Mounter struct {
	mounterPath string
}

// New returns a mount.Interface for the current system.
// It provides options to override the default mounter behavior.
// mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface {
	return &Mounter{
		mounterPath: mounterPath,
	}
}

// acquire lock for smb mount
var getSMBMountMutex = keymutex.NewHashed(0)

// Mount : mounts source to target with given options.
// currently only supports cifs(smb), bind mount(for disk)
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	return mounter.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
}

// MountSensitive is the same as Mount() but this method allows
// sensitiveOptions to be passed in a separate parameter from the normal
// mount options and ensures the sensitiveOptions are never logged. This
// method should be used by callers that pass sensitive material (like
// passwords) as mount options.
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
	target = NormalizeWindowsPath(target)
	sanitizedOptionsForLogging := sanitizedOptionsForLogging(options, sensitiveOptions)

	if source == "tmpfs" {
		klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, sanitizedOptionsForLogging)
		return os.MkdirAll(target, 0755)
	}

	parentDir := filepath.Dir(target)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return err
	}

	klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
		sanitizedOptionsForLogging, source, target, fstype)
	bindSource := source

	if bind, _, _, _ := MakeBindOptsSensitive(options, sensitiveOptions); bind {
		bindSource = NormalizeWindowsPath(source)
	} else {
		allOptions := []string{}
		allOptions = append(allOptions, options...)
		allOptions = append(allOptions, sensitiveOptions...)
		if len(allOptions) < 2 {
			klog.Warningf("mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
				sanitizedOptionsForLogging, len(allOptions), source, target)
			return nil
		}

		// currently only cifs mount is supported
		if strings.ToLower(fstype) != "cifs" {
			return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, sanitizedOptionsForLogging)
		}

		// lock smb mount for the same source
		getSMBMountMutex.LockKey(source)
		defer getSMBMountMutex.UnlockKey(source)

		if output, err := newSMBMapping(allOptions[0], allOptions[1], source); err != nil {
			if isSMBMappingExist(source) {
				klog.V(2).Infof("SMB Mapping(%s) already exists, now begin to remove and remount", source)
				if output, err := removeSMBMapping(source); err != nil {
					return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
				}
				if output, err := newSMBMapping(allOptions[0], allOptions[1], source); err != nil {
					return fmt.Errorf("New-SmbGlobalMapping remount failed: %v, output: %q", err, output)
				}
			} else {
				return fmt.Errorf("New-SmbGlobalMapping failed: %v, output: %q", err, output)
			}
		}
	}

	if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
		klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
		return err
	}

	return nil
}

// do the SMB mount with username, password, remotepath
// return (output, error)
func newSMBMapping(username, password, remotepath string) (string, error) {
	if username == "" || password == "" || remotepath == "" {
		return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, sensitiveOptionsRemoved, remotepath)
	}

	// use PowerShell Environment Variables to store user input string to prevent command line injection
	// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
	cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
		`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
		`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
	cmd := exec.Command("powershell", "/c", cmdLine)
	cmd.Env = append(os.Environ(),
		fmt.Sprintf("smbuser=%s", username),
		fmt.Sprintf("smbpassword=%s", password),
		fmt.Sprintf("smbremotepath=%s", remotepath))

	output, err := cmd.CombinedOutput()
	return string(output), err
}

// check whether remotepath is already mounted
func isSMBMappingExist(remotepath string) bool {
	cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
	cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
	_, err := cmd.CombinedOutput()
	return err == nil
}

// remove SMB mapping
func removeSMBMapping(remotepath string) (string, error) {
	cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
	cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
	output, err := cmd.CombinedOutput()
	return string(output), err
}

// Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error {
	klog.V(4).Infof("azureMount: Unmount target (%q)", target)
	target = NormalizeWindowsPath(target)
	if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
		klog.Errorf("rmdir failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// List returns a list of all mounted filesystems. todo
func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	stat, err := os.Lstat(file)
	if err != nil {
		return true, err
	}
	// If current file is a symlink, then it is a mountpoint.
	if stat.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(file)
		if err != nil {
			return true, fmt.Errorf("readlink error: %v", err)
		}
		exists, err := utilpath.Exists(utilpath.CheckFollowSymlink, target)
		if err != nil {
			return true, err
		}
		return !exists, nil
	}

	return true, nil
}

// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
	windowsPath := NormalizeWindowsPath(pathname)
	pathExists, pathErr := PathExists(windowsPath)
	if !pathExists {
		return []string{}, nil
	} else if IsCorruptedMnt(pathErr) {
		klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
		return []string{}, nil
	} else if pathErr != nil {
		return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
	}
	return []string{pathname}, nil
}

func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
	// Try to mount the disk
	klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)

	if err := ValidateDiskNumber(source); err != nil {
		klog.Errorf("diskMount: formatAndMount failed, err: %v", err)
		return err
	}

	if len(fstype) == 0 {
		// Use 'NTFS' as the default
		fstype = "NTFS"
	}

	// format disk if it is unformatted(raw)
	cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+
		" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
	if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil {
		return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
	}
	klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)

	driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
	if err != nil {
		return err
	}
	driverPath := driveLetter + ":"
	target = NormalizeWindowsPath(target)
	klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
	if output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput(); err != nil {
		klog.Errorf("mklink failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// Get drive letter according to windows disk number
func getDriveLetterByDiskNumber(diskNum string, exec utilexec.Interface) (string, error) {
	cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
	output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
	}
	if len(string(output)) < 1 {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
	}
	return string(output)[:1], nil
}

// getAllParentLinks walks all symbolic links and return all the parent targets recursively
func getAllParentLinks(path string) ([]string, error) {
	const maxIter = 255
	links := []string{}
	for {
		links = append(links, path)
		if len(links) > maxIter {
			return links, fmt.Errorf("unexpected length of parent links: %v", links)
		}

		fi, err := os.Lstat(path)
		if err != nil {
			return links, fmt.Errorf("Lstat: %v", err)
		}
		if fi.Mode()&os.ModeSymlink == 0 {
			break
		}

		path, err = os.Readlink(path)
		if err != nil {
			return links, fmt.Errorf("Readlink error: %v", err)
		}
	}

	return links, nil
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"errors"
	_ "github.com/alexbrainman/odbc"
	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/ziutek/mymysql/godrv"
	"runtime"
	"strings"
)

type Database struct {
	inst       *sql.DB
	connString string
	driver     string
}

func (db *Database) Open(driver string, connString string) error {
	db.driver = driver
	db.connString = connString
	runtime.SetFinalizer(db, func(f interface{}) {
		f.(*Database).Close()
	})
	return db.executeOpen()
}

func (db *Database) executeOpen() error {
	var err error
	db.inst, err = sql.Open(db.driver, db.connString)
	if err != nil {
		return err
	}
	return nil
}

func (db *Database) Close() error {
	if db.inst != nil {
		return db.inst.Close()
	}
	return nil
}

type Rows struct {
	inst    *sql.Rows
	isFirst bool
	isNil   bool
	Cols    []string
}

func (db *Database) prepare(queryStr string, retCount int) (*sql.Stmt, error) {
	stmt, err := db.inst.Prepare(queryStr)
	if err != nil {
		db.Close()
		db.executeOpen()
		if retCount > 0 {
			return db.prepare(queryStr, retCount-1)
		}
		return nil, err
	}
	return stmt, err
}

func (db *Database) Query(queryStr string) (*Rows, error) {
	stmt, err := db.prepare(queryStr, 1)
	if stmt != nil {
		defer stmt.Close()
	}
	if err != nil {
		db.Close()
		db.executeOpen()
		return db.TempQuery(queryStr)
	}

	rows := &Rows{nil, true, false, make([]string, 0, 100)}
	rows.inst, err = stmt.Query()

	if err != nil {
		db.Close()
		db.executeOpen()
		return db.TempQuery(queryStr)
	}

	rows.Cols, err = rows.inst.Columns()

	if !rows.inst.Next() {
		rows.Close()
	}

	QUERYSTR := strings.ToUpper(queryStr)
	if strings.HasPrefix(QUERYSTR, "INSERT") && strings.Contains(QUERYSTR, "OUTPUT") && strings.Contains(QUERYSTR, "INSERTED.") {
		if rows.IsNil() {
			return nil, errors.New("insert.fail")
		}
	}

	runtime.SetFinalizer(rows, func(f interface{}) {
		f.(*Rows).Close()
	})

	return rows, nil
}

func (db *Database) TempQuery(queryStr string) (*Rows, error) {
	stmt, err := db.prepare(queryStr, 1)
	if stmt != nil {
		defer stmt.Close()
	}
	if err != nil {
		println("P1 : ", err.Error())
		return nil, err
	}

	rows := &Rows{nil, true, false, make([]string, 0, 100)}
	rows.inst, err = stmt.Query()

	if err != nil {
		if err.Error() != "Stmt did not create a result set" {
			println("P2 : ", err.Error(), "\n", queryStr)
			return nil, err
		} else {
			runtime.SetFinalizer(rows, func(f interface{}) {
				f.(*Rows).Close()
			})
			return rows, nil
		}
	}

	rows.Cols, err = rows.inst.Columns()

	if !rows.inst.Next() {
		rows.Close()
	} else {
		runtime.SetFinalizer(rows, func(f interface{}) {
			f.(*Rows).Close()
		})
	}

	QUERYSTR := strings.ToUpper(queryStr)
	if strings.HasPrefix(QUERYSTR, "INSERT") && strings.Contains(QUERYSTR, "OUTPUT") && strings.Contains(QUERYSTR, "INSERTED.") {
		if rows.IsNil() {
			return nil, errors.New("insert.fail")
		}
	}

	return rows, nil
}

func (rows *Rows) Next() bool {
	if !rows.isNil && rows.isFirst {
		rows.isFirst = false
		return true
	}
	if !rows.inst.Next() {
		rows.Close()
	}
	return !rows.isNil
}

func (rows *Rows) FetchArray() []interface{} {
	if rows.isNil {
		return nil
	}
	cols, err := rows.inst.Columns()
	if err != nil {
		return nil
	}

	rawResult := make([]*interface{}, len(cols))
	result := make([]interface{}, len(cols))

	dest := make([]interface{}, len(cols))
	for i, _ := range rawResult {
		dest[i] = &rawResult[i]
	}
	rows.inst.Scan(dest...)
	for i, raw := range rawResult {
		if raw != nil {
			result[i] = (*raw)
		} else {
			result[i] = nil
		}
	}
	return result
}

func (rows *Rows) FetchHash() map[string]interface{} {
	if rows.isNil {
		return nil
	}
	cols, err := rows.inst.Columns()
	if err != nil {
		return nil
	}

	rawResult := make([]*interface{}, len(cols))
	result := make(map[string]interface{}, len(cols))

	dest := make([]interface{}, len(cols))
	for i, _ := range rawResult {
		dest[i] = &rawResult[i]
	}
	rows.inst.Scan(dest...)
	for i, raw := range rawResult {
		if raw != nil {
			result[cols[i]] = (*raw)
			result[strings.ToUpper(cols[i])] = (*raw)
			result[strings.ToLower(cols[i])] = (*raw)
		} else {
			result[cols[i]] = nil
			result[strings.ToUpper(cols[i])] = nil
			result[strings.ToLower(cols[i])] = nil
		}
	}
	return result
}

func (rows *Rows) Close() error {
	if rows != nil && rows.inst != nil {
		rows.isNil = true
		return rows.inst.Close()
	}
	return nil
}

func (rows *Rows) IsNil() bool {
	return rows.isNil
}
<commit_msg>change<commit_after>package database

import (
	"database/sql"
	"errors"
	_ "github.com/alexbrainman/odbc"
	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/ziutek/mymysql/godrv"
	"runtime"
	"strings"
)

type Database struct {
	inst       *sql.DB
	connString string
	driver     string
}

func (db *Database) Open(driver string, connString string) error {
	db.driver = driver
	db.connString = connString
	runtime.SetFinalizer(db, func(f interface{}) {
		f.(*Database).Close()
	})
	return db.executeOpen()
}

func (db *Database) executeOpen() error {
	var err error
	db.inst, err = sql.Open(db.driver, db.connString)
	if err != nil {
		return err
	}
	return nil
}

func (db *Database) Close() error {
	if db.inst != nil {
		return db.inst.Close()
	}
	return nil
}

type Rows struct {
	inst    *sql.Rows
	isFirst bool
	isNil   bool
	Cols    []string
}

func (db *Database) prepare(queryStr string, retCount int) (*sql.Stmt, error) {
	stmt, err := db.inst.Prepare(queryStr)
	if err != nil {
		db.Close()
		db.executeOpen()
		if retCount > 0 {
			return db.prepare(queryStr, retCount-1)
		}
		return nil, err
	}
	return stmt, err
}

func (db *Database) Query(queryStr string) (*Rows, error) {
	stmt, err := db.prepare(queryStr, 1)
	if stmt != nil {
		defer stmt.Close()
	}
	if err != nil {
		db.Close()
		db.executeOpen()
		return db.TempQuery(queryStr)
	}

	rows := &Rows{nil, true, false, make([]string, 0, 100)}
	rows.inst, err = stmt.Query()

	if err != nil {
		if err.Error() != "Stmt did not create a result set" {
			db.Close()
			db.executeOpen()
			return db.TempQuery(queryStr)
		} else {
			runtime.SetFinalizer(rows, func(f interface{}) {
				f.(*Rows).Close()
			})
			return rows, nil
		}
	}

	rows.Cols, err = rows.inst.Columns()

	if !rows.inst.Next() {
		rows.Close()
	} else {
		runtime.SetFinalizer(rows, func(f interface{}) {
			f.(*Rows).Close()
		})
	}

	QUERYSTR := strings.ToUpper(queryStr)
	if strings.HasPrefix(QUERYSTR, "INSERT") && strings.Contains(QUERYSTR, "OUTPUT") && strings.Contains(QUERYSTR, "INSERTED.") {
		if rows.IsNil() {
			return nil, errors.New("insert.fail")
		}
	}

	return rows, nil
}

func (db *Database) TempQuery(queryStr string) (*Rows, error) {
	stmt, err := db.prepare(queryStr, 1)
	if stmt != nil {
		defer stmt.Close()
	}
	if err != nil {
		println("P1 : ", err.Error())
		return nil, err
	}

	rows := &Rows{nil, true, false, make([]string, 0, 100)}
	rows.inst, err = stmt.Query()

	if err != nil {
		if err.Error() != "Stmt did not create a result set" {
			println("P2 : ", err.Error(), "\n", queryStr)
			return nil, err
		} else {
			runtime.SetFinalizer(rows, func(f interface{}) {
				f.(*Rows).Close()
			})
			return rows, nil
		}
	}

	rows.Cols, err = rows.inst.Columns()

	if !rows.inst.Next() {
		rows.Close()
	} else {
		runtime.SetFinalizer(rows, func(f interface{}) {
			f.(*Rows).Close()
		})
	}

	QUERYSTR := strings.ToUpper(queryStr)
	if strings.HasPrefix(QUERYSTR, "INSERT") && strings.Contains(QUERYSTR, "OUTPUT") && strings.Contains(QUERYSTR, "INSERTED.") {
		if rows.IsNil() {
			return nil, errors.New("insert.fail")
		}
	}

	return rows, nil
}

func (rows *Rows) Next() bool {
	if !rows.isNil && rows.isFirst {
		rows.isFirst = false
		return true
	}
	if !rows.inst.Next() {
		rows.Close()
	}
	return !rows.isNil
}

func (rows *Rows) FetchArray() []interface{} {
	if rows.isNil {
		return nil
	}
	cols, err := rows.inst.Columns()
	if err != nil {
		return nil
	}

	rawResult := make([]*interface{}, len(cols))
	result := make([]interface{}, len(cols))

	dest := make([]interface{}, len(cols))
	for i, _ := range rawResult {
		dest[i] = &rawResult[i]
	}
	rows.inst.Scan(dest...)
	for i, raw := range rawResult {
		if raw != nil {
			result[i] = (*raw)
		} else {
			result[i] = nil
		}
	}
	return result
}

func (rows *Rows) FetchHash() map[string]interface{} {
	if rows.isNil {
		return nil
	}
	cols, err := rows.inst.Columns()
	if err != nil {
		return nil
	}

	rawResult := make([]*interface{}, len(cols))
	result := make(map[string]interface{}, len(cols))

	dest := make([]interface{}, len(cols))
	for i, _ := range rawResult {
		dest[i] = &rawResult[i]
	}
	rows.inst.Scan(dest...)
	for i, raw := range rawResult {
		if raw != nil {
			result[cols[i]] = (*raw)
			result[strings.ToUpper(cols[i])] = (*raw)
			result[strings.ToLower(cols[i])] = (*raw)
		} else {
			result[cols[i]] = nil
			result[strings.ToUpper(cols[i])] = nil
			result[strings.ToLower(cols[i])] = nil
		}
	}
	return result
}

func (rows *Rows) Close() error {
	if rows != nil && rows.inst != nil {
		rows.isNil = true
		return rows.inst.Close()
	}
	return nil
}

func (rows *Rows) IsNil() bool {
	return rows.isNil
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"crypto/rsa"
	"errors"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	spb "github.com/PomeloCloud/BFTRaft4go/proto/server"
	"github.com/PomeloCloud/BFTRaft4go/utils"
	"github.com/patrickmn/go-cache"
	"log"
)

type BFTRaftClient struct {
	Id          uint64
	PrivateKey  *rsa.PrivateKey
	AlphaRPCs   AlphaRPCsCache
	GroupHosts  *cache.Cache
	GroupLeader *cache.Cache
	CmdResChan  map[uint64]map[uint64]chan []byte
	Counter     int64
	Lock        sync.RWMutex
}

type ClientOptions struct {
	PrivateKey []byte
}

// bootstraps is a list of server address believed to be the member of the network
// the list does not need to contain alpha nodes since all of the nodes on the network will get informed
func NewClient(bootstraps []string, opts ClientOptions) (*BFTRaftClient, error) {
	privateKey, err := utils.ParsePrivateKey(opts.PrivateKey)
	if err != nil {
		return nil, err
	}
	publicKey := utils.PublicKeyFromPrivate(privateKey)
	bftclient := &BFTRaftClient{
		Id:          utils.HashPublicKey(publicKey),
		PrivateKey:  privateKey,
		Lock:        sync.RWMutex{},
		AlphaRPCs:   NewAlphaRPCsCache(bootstraps),
		GroupHosts:  cache.New(1*time.Minute, 1*time.Minute),
		GroupLeader: cache.New(1*time.Minute, 1*time.Minute),
		CmdResChan:  map[uint64]map[uint64]chan []byte{},
		Counter:     0,
	}
	return bftclient, nil
}

func (brc *BFTRaftClient) GetGroupHosts(groupId uint64) *[]*spb.Host {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupHosts.Get(cacheKey); found {
		return cached.(*[]*spb.Host)
	}
	res := utils.MajorityResponse(brc.AlphaRPCs.Get(), func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GroupHosts(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			return &res.Nodes, utils.NodesSignData(res.Nodes)
		} else {
			log.Println("error on getting group host:", err)
			return nil, []byte{}
		}
	})
	var hosts *[]*spb.Host = nil
	if res != nil {
		hosts = res.(*[]*spb.Host)
	}
	if hosts != nil {
		brc.GroupHosts.Set(cacheKey, hosts, cache.DefaultExpiration)
	}
	return hosts
}

func (brc *BFTRaftClient) GetGroupLeader(groupId uint64) spb.BFTRaftClient {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupLeader.Get(cacheKey); found {
		return cached.(spb.BFTRaftClient)
	}
	res := utils.MajorityResponse(brc.AlphaRPCs.Get(), func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GetGroupLeader(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			// TODO: verify signature
			return res.Node, []byte(res.Node.ServerAddr)
		} else {
			log.Println("cannot get group leader on alpha peer:", err)
			return nil, []byte{}
		}
	})
	var leaderHost *spb.Host = nil
	if res != nil {
		leaderHost = res.(*spb.Host)
	}
	if leaderHost != nil {
		if leader, err := utils.GetClusterRPC(leaderHost.ServerAddr); err == nil {
			brc.GroupLeader.Set(cacheKey, leader, cache.DefaultExpiration)
			return leader
		}
	} else {
		log.Println(brc.Id, ", group", groupId, "has no leader")
	}
	return nil
}

func (brc *BFTRaftClient) ExecCommand(groupId uint64, funcId uint64, arg []byte) (*[]byte, error) {
	leader := brc.GetGroupLeader(groupId)
	if leader == nil {
		return nil, errors.New("cannot found leader")
	}
	reqId := uint64(atomic.AddInt64(&brc.Counter, 1))
	cmdReq := &spb.CommandRequest{
		Group:     groupId,
		ClientId:  brc.Id,
		RequestId: reqId,
		FuncId:    funcId,
		Arg:       arg,
	}
	signData := utils.ExecCommandSignData(cmdReq)
	cmdReq.Signature = utils.Sign(brc.PrivateKey, signData)
	if _, found := brc.CmdResChan[groupId]; !found {
		brc.CmdResChan[groupId] = map[uint64]chan []byte{}
	}
	brc.CmdResChan[groupId][reqId] = make(chan []byte)
	defer func() {
		close(brc.CmdResChan[groupId][reqId])
		delete(brc.CmdResChan[groupId], reqId)
	}()
	go func() {
		if cmdRes, err := leader.ExecCommand(context.Background(), cmdReq); err == nil {
			// TODO: verify signature
			// TODO: update leader if needed
			// TODO: verify response matches request
			brc.CmdResChan[groupId][reqId] <- cmdRes.Result

		} else {
			log.Println("cannot exec on leader:", err)
		}
	}()
	hosts := brc.GetGroupHosts(groupId)
	if hosts == nil {
		return nil, errors.New("cannot get group hosts")
	}
	expectedResponse := len(*hosts) / 2
	if expectedResponse == 0 {
		expectedResponse = 1
	}
	responseReceived := map[uint64][]byte{}
	responseHashes := []uint64{}
	replicationCompleted := make(chan bool, 1)
	wg := sync.WaitGroup{}
	wg.Add(expectedResponse)
	go func() {
		for i := 0; i < expectedResponse; i++ {
			res := <-brc.CmdResChan[groupId][reqId]
			hash := utils.HashData(res)
			responseReceived[hash] = res
			responseHashes = append(responseHashes, hash)
			wg.Done()
		}
	}()
	go func() {
		wg.Wait()
		replicationCompleted <- true
	}()
	select {
	case <-replicationCompleted:
		majorityHash := utils.PickMajority(responseHashes)
		majorityData := responseReceived[majorityHash]
		return &majorityData, nil
	case <-time.After(10 * time.Second):
		return nil, errors.New("does not receive enough response")
	}
}
<commit_msg>check group exists<commit_after>package client

import (
	"context"
	"crypto/rsa"
	"errors"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	spb "github.com/PomeloCloud/BFTRaft4go/proto/server"
	"github.com/PomeloCloud/BFTRaft4go/utils"
	"github.com/patrickmn/go-cache"
	"log"
)

type BFTRaftClient struct {
	Id          uint64
	PrivateKey  *rsa.PrivateKey
	AlphaRPCs   AlphaRPCsCache
	GroupHosts  *cache.Cache
	GroupLeader *cache.Cache
	CmdResChan  map[uint64]map[uint64]chan []byte
	Counter     int64
	Lock        sync.RWMutex
}

type ClientOptions struct {
	PrivateKey []byte
}

// bootstraps is a list of server address believed to be the member of the network
// the list does not need to contain alpha nodes since all of the nodes on the network will get informed
func NewClient(bootstraps []string, opts ClientOptions) (*BFTRaftClient, error) {
	privateKey, err := utils.ParsePrivateKey(opts.PrivateKey)
	if err != nil {
		return nil, err
	}
	publicKey := utils.PublicKeyFromPrivate(privateKey)
	bftclient := &BFTRaftClient{
		Id:          utils.HashPublicKey(publicKey),
		PrivateKey:  privateKey,
		Lock:        sync.RWMutex{},
		AlphaRPCs:   NewAlphaRPCsCache(bootstraps),
		GroupHosts:  cache.New(1*time.Minute, 1*time.Minute),
		GroupLeader: cache.New(1*time.Minute, 1*time.Minute),
		CmdResChan:  map[uint64]map[uint64]chan []byte{},
		Counter:     0,
	}
	return bftclient, nil
}

func (brc *BFTRaftClient) GetGroupHosts(groupId uint64) *[]*spb.Host {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupHosts.Get(cacheKey); found {
		return cached.(*[]*spb.Host)
	}
	res := utils.MajorityResponse(brc.AlphaRPCs.Get(), func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GroupHosts(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			return &res.Nodes, utils.NodesSignData(res.Nodes)
		} else {
			log.Println("error on getting group host:", err)
			return nil, []byte{}
		}
	})
	var hosts *[]*spb.Host = nil
	if res != nil {
		hosts = res.(*[]*spb.Host)
	}
	if hosts != nil {
		brc.GroupHosts.Set(cacheKey, hosts, cache.DefaultExpiration)
	}
	return hosts
}

func (brc *BFTRaftClient) GetGroupLeader(groupId uint64) spb.BFTRaftClient {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupLeader.Get(cacheKey); found {
		return cached.(spb.BFTRaftClient)
	}
	res := utils.MajorityResponse(brc.AlphaRPCs.Get(), func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GetGroupLeader(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			// TODO: verify signature
			return res.Node, []byte(res.Node.ServerAddr)
		} else {
			log.Println("cannot get group leader on alpha peer:", err)
			return nil, []byte{}
		}
	})
	var leaderHost *spb.Host = nil
	if res != nil {
		leaderHost = res.(*spb.Host)
	}
	if leaderHost != nil {
		if leader, err := utils.GetClusterRPC(leaderHost.ServerAddr); err == nil {
			brc.GroupLeader.Set(cacheKey, leader, cache.DefaultExpiration)
			return leader
		}
	} else {
		log.Println(brc.Id, ", group", groupId, "has no leader")
	}
	return nil
}

func (brc *BFTRaftClient) GroupExists(groupId uint64) bool {
	res := utils.MajorityResponse(brc.AlphaRPCs.Get(), func(client spb.BFTRaftClient) (interface{}, []byte) {
		if _, err := client.GetGroupContent(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			// TODO: verify signature
			return true, []byte{1}
		} else {
			return false, []byte{0}
		}
	})
	return res.(bool)
}

func (brc *BFTRaftClient) ExecCommand(groupId uint64, funcId uint64, arg []byte) (*[]byte, error) {
	leader := brc.GetGroupLeader(groupId)
	if leader == nil {
		return nil, errors.New("cannot found leader")
	}
	reqId := uint64(atomic.AddInt64(&brc.Counter, 1))
	cmdReq := &spb.CommandRequest{
		Group:     groupId,
		ClientId:  brc.Id,
		RequestId: reqId,
		FuncId:    funcId,
		Arg:       arg,
	}
	signData := utils.ExecCommandSignData(cmdReq)
	cmdReq.Signature = utils.Sign(brc.PrivateKey, signData)
	if _, found := brc.CmdResChan[groupId]; !found {
		brc.CmdResChan[groupId] = map[uint64]chan []byte{}
	}
	brc.CmdResChan[groupId][reqId] = make(chan []byte)
	defer func() {
		close(brc.CmdResChan[groupId][reqId])
		delete(brc.CmdResChan[groupId], reqId)
	}()
	go func() {
		if cmdRes, err := leader.ExecCommand(context.Background(), cmdReq); err == nil {
			// TODO: verify signature
			// TODO: update leader if needed
			// TODO: verify response matches request
			brc.CmdResChan[groupId][reqId] <- cmdRes.Result

		} else {
			log.Println("cannot exec on leader:", err)
		}
	}()
	hosts := brc.GetGroupHosts(groupId)
	if hosts == nil {
		return nil, errors.New("cannot get group hosts")
	}
	expectedResponse := len(*hosts) / 2
	if expectedResponse == 0 {
		expectedResponse = 1
	}
	responseReceived := map[uint64][]byte{}
	responseHashes := []uint64{}
	replicationCompleted := make(chan bool, 1)
	wg := sync.WaitGroup{}
	wg.Add(expectedResponse)
	go func() {
		for i := 0; i < expectedResponse; i++ {
			res := <-brc.CmdResChan[groupId][reqId]
			hash := utils.HashData(res)
			responseReceived[hash] = res
			responseHashes = append(responseHashes, hash)
			wg.Done()
		}
	}()
	go func() {
		wg.Wait()
		replicationCompleted <- true
	}()
	select {
	case <-replicationCompleted:
		majorityHash := utils.PickMajority(responseHashes)
		majorityData := responseReceived[majorityHash]
		return &majorityData, nil
	case <-time.After(10 * time.Second):
		return nil, errors.New("does not receive enough response")
	}
}
<|endoftext|>


<commit_before>package groups

import (
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/moves/interfaces"
)

//Repeat returns a MoveProgressionGroup that repeats the provided group the
//number of times count is looking for, in serial. It is conceptually
//equivalent to duplicating a given group within a parent groups.Serial count
//times.
func Repeat(count interfaces.ValidCounter, group interfaces.MoveProgressionGroup) interfaces.MoveProgressionGroup {
	return repeat{
		count,
		group,
	}
}

type repeat struct {
	Count interfaces.ValidCounter
	Child interfaces.MoveProgressionGroup
}

func (r repeat) MoveConfigs() []boardgame.MoveConfig {
	return r.Child.MoveConfigs()
}

func (r repeat) Satisfied(tape *interfaces.MoveGroupHistoryItem) (error, *interfaces.MoveGroupHistoryItem) {

	tapeHead := tape

	count := 0

	for {

		if tapeHead == nil {
			return nil, nil
		}

		if err := r.Count(count, 1); err == nil {
			break
		}

		err, rest := r.Child.Satisfied(tapeHead)

		if err != nil {
			return err, nil
		}

		count++
		tapeHead = rest

	}

	return nil, tapeHead

}
<commit_msg>Add group.Optional(), convenience method for group.Repeat. Part of #627.<commit_after>package groups

import (
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/moves/count"
	"github.com/jkomoros/boardgame/moves/interfaces"
)

//Optional returns a MoveProgressionGroup that matches the provided group
//either 0 or 1 times. Equivalent to Repeat() with a count of Between(0, 1).
func Optional(group interfaces.MoveProgressionGroup) interfaces.MoveProgressionGroup {
	return Repeat(count.Between(0, 1), group)
}

//Repeat returns a MoveProgressionGroup that repeats the provided group the
//number of times count is looking for, in serial. It is conceptually
//equivalent to duplicating a given group within a parent groups.Serial count
//times.
func Repeat(count interfaces.ValidCounter, group interfaces.MoveProgressionGroup) interfaces.MoveProgressionGroup {
	return repeat{
		count,
		group,
	}
}

type repeat struct {
	Count interfaces.ValidCounter
	Child interfaces.MoveProgressionGroup
}

func (r repeat) MoveConfigs() []boardgame.MoveConfig {
	return r.Child.MoveConfigs()
}

func (r repeat) Satisfied(tape *interfaces.MoveGroupHistoryItem) (error, *interfaces.MoveGroupHistoryItem) {

	tapeHead := tape

	count := 0

	for {

		if tapeHead == nil {
			return nil, nil
		}

		if err := r.Count(count, 1); err == nil {
			break
		}

		err, rest := r.Child.Satisfied(tapeHead)

		if err != nil {
			return err, nil
		}

		count++
		tapeHead = rest

	}

	return nil, tapeHead

}
<|endoftext|>


<commit_before>package notifier

import (
	"fmt"
	"net/http"
	"os"
	"sync"
	"testing"
	"time"

	"github.com/oinume/lekcije/server/emailer"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/stretchr/testify/require"
)

var helper = model.NewTestHelper()
var _ = fmt.Print

type mockTransport struct {
	sync.Mutex
	called int
}

func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.Lock()
	t.called++
	t.Unlock()
	resp := &http.Response{
		Header:     make(http.Header),
		Request:    req,
		StatusCode: http.StatusOK,
		Status:     "200 OK",
	}
	resp.Header.Set("Content-Type", "text/html; charset=UTF-8")

	// TODO: file location
	file, err := os.Open("../fetcher/testdata/5982.html")
	if err != nil {
		return nil, err
	}
	resp.Body = file // Close() will be called by client
	fmt.Printf("RoundTrip()\n")
	return resp, nil
}

func TestMain(m *testing.M) {
	db := helper.DB()
	defer db.Close()
	helper.TruncateAllTables(db)
	os.Exit(m.Run())
}

func TestSendNotification(t *testing.T) {
	//a := assert.New(t)
	r := require.New(t)
	db := helper.DB()
	logger.InitializeAppLogger(os.Stdout)

	client := &http.Client{
		Transport: &mockTransport{},
		Timeout:   5 * time.Second,
	}
	fetcher := fetcher.NewTeacherLessonFetcher(client, 1, false, helper.LoadMCountries(), nil)

	user := helper.CreateUser("oinume", "oinume@gmail.com")
	teacher := helper.CreateRandomTeacher()
	helper.CreateFollowingTeacher(user.ID, teacher)

	//sender := emailer.NewSendGridSender(http.DefaultClient)
	sender := &emailer.NoSender{}
	n := NewNotifier(db, fetcher, true, sender)
	err := n.SendNotification(user)
	r.Nil(err)
}
<commit_msg>mock transport for SendGridSender<commit_after>package notifier

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/oinume/lekcije/server/emailer"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/stretchr/testify/require"
)

var helper = model.NewTestHelper()
var _ = fmt.Print

type mockFetcherTransport struct {
	sync.Mutex
	called int
}

func (t *mockFetcherTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.Lock()
	t.called++
	t.Unlock()
	resp := &http.Response{
		Header:     make(http.Header),
		Request:    req,
		StatusCode: http.StatusOK,
		Status:     "200 OK",
	}
	resp.Header.Set("Content-Type", "text/html; charset=UTF-8")

	// TODO: file location
	file, err := os.Open("../fetcher/testdata/5982.html")
	if err != nil {
		return nil, err
	}
	resp.Body = file // Close() will be called by client
	return resp, nil
}

type mockSenderTransport struct {
	sync.Mutex
	called int
}

func (t *mockSenderTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.Lock()
	t.called++
	t.Unlock()
	resp := &http.Response{
		Header:     make(http.Header),
		Request:    req,
		StatusCode: http.StatusAccepted,
		Status:     "202 Accepted",
	}
	//resp.Header.Set("Content-Type", "text/html; charset=UTF-8")
	resp.Body = ioutil.NopCloser(strings.NewReader(""))
	return resp, nil
}

func TestMain(m *testing.M) {
	db := helper.DB()
	defer db.Close()
	helper.TruncateAllTables(db)
	os.Exit(m.Run())
}

func TestSendNotification(t *testing.T) {
	//a := assert.New(t)
	r := require.New(t)
	db := helper.DB()
	logger.InitializeAppLogger(os.Stdout)

	fetcherHTTPClient := &http.Client{
		Transport: &mockFetcherTransport{},
		Timeout:   5 * time.Second,
	}
	fetcher := fetcher.NewTeacherLessonFetcher(fetcherHTTPClient, 1, false, helper.LoadMCountries(), nil)

	user := helper.CreateUser("oinume", "oinume@gmail.com")
	teacher := helper.CreateRandomTeacher()
	helper.CreateFollowingTeacher(user.ID, teacher)

	senderHTTPClient := &http.Client{
		Transport: &mockSenderTransport{},
		Timeout:   5 * time.Second,
	}
	sender := emailer.NewSendGridSender(senderHTTPClient)
	//sender := &emailer.NoSender{}
	n := NewNotifier(db, fetcher, true, sender)
	err := n.SendNotification(user)
	r.Nil(err)
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/cluster"
)

var flynnHostLogs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin"},
	{os.Args[0], "version"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
}

func init() {
	Register("collect-debug-info", runCollectDebugInfo, `
usage: flynn-host collect-debug-info [options]

Options:
  --tarball      Create a tarball instead of uploading to a gist
  --include-env  Include sensitive environment variables

Collect debug information into an anonymous gist or tarball`)
}

func runCollectDebugInfo(args *docopt.Args) error {
	log := log15.New()
	if args.Bool["--tarball"] {
		log.Info("creating a tarball containing logs and debug information")
	} else {
		log.Info("uploading logs and debug information to a private, anonymous gist")
	}
	log.Info("this may take a while depending on the size of your logs")

	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	log.Info("getting flynn-host logs")
	for name, filepath := range flynnHostLogs {
		if err := gist.AddLocalFile(name, filepath); err != nil && !os.IsNotExist(err) {
			log.Error(fmt.Sprintf("error getting flynn-host log %q", filepath), "err", err)
		}
	}

	log.Info("getting job logs")
	if err := captureJobs(gist, args.Bool["--include-env"]); err != nil {
		log.Error("error getting job logs", "err", err)
	}

	log.Info("getting system information")
	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Error(fmt.Sprintf("error capturing output of %q", strings.Join(cmd, " ")), "err", err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if args.Bool["--tarball"] {
		path, err := gist.CreateTarball()
		if err != nil {
			log.Error("error creating tarball", "err", err)
			return err
		}
		log.Info(fmt.Sprintf("created tarball containing debug information at %s", path))
		return nil
	}

	if err := gist.Upload(log); err != nil {
		return err
	}

	log.Info(fmt.Sprintf("debug information uploaded to: %s", gist.URL))
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist, env bool) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content, env)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, true, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<commit_msg>host/cli: Collect iptables rules as part of debug info<commit_after>package cli

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/cluster"
)

var flynnHostLogs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin"},
	{os.Args[0], "version"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
	{"iptables", "-L", "-v", "-n", "--line-numbers"},
}

func init() {
	Register("collect-debug-info", runCollectDebugInfo, `
usage: flynn-host collect-debug-info [options]

Options:
  --tarball      Create a tarball instead of uploading to a gist
  --include-env  Include sensitive environment variables

Collect debug information into an anonymous gist or tarball`)
}

func runCollectDebugInfo(args *docopt.Args) error {
	log := log15.New()
	if args.Bool["--tarball"] {
		log.Info("creating a tarball containing logs and debug information")
	} else {
		log.Info("uploading logs and debug information to a private, anonymous gist")
	}
	log.Info("this may take a while depending on the size of your logs")

	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	log.Info("getting flynn-host logs")
	for name, filepath := range flynnHostLogs {
		if err := gist.AddLocalFile(name, filepath); err != nil && !os.IsNotExist(err) {
			log.Error(fmt.Sprintf("error getting flynn-host log %q", filepath), "err", err)
		}
	}

	log.Info("getting job logs")
	if err := captureJobs(gist, args.Bool["--include-env"]); err != nil {
		log.Error("error getting job logs", "err", err)
	}

	log.Info("getting system information")
	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Error(fmt.Sprintf("error capturing output of %q", strings.Join(cmd, " ")), "err", err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if args.Bool["--tarball"] {
		path, err := gist.CreateTarball()
		if err != nil {
			log.Error("error creating tarball", "err", err)
			return err
		}
		log.Info(fmt.Sprintf("created tarball containing debug information at %s", path))
		return nil
	}

	if err := gist.Upload(log); err != nil {
		return err
	}

	log.Info(fmt.Sprintf("debug information uploaded to: %s", gist.URL))
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist, env bool) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content, env)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, true, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<|endoftext|>


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

package caddycmd

import (
	"flag"
	"regexp"
)

// Command represents a subcommand. Name, Func,
// and Short are required.
type Command struct {
	// The name of the subcommand. Must conform to the
	// format described by the RegisterCommand() godoc.
	// Required.
	Name string

	// Run is a function that executes a subcommand using
	// the parsed flags. It returns an exit code and any
	// associated error.
	// Required.
	Func CommandFunc

	// Usage is a brief message describing the syntax of
	// the subcommand's flags and args. Use [] to indicate
	// optional parameters and <> to enclose literal values
	// intended to be replaced by the user. Do not prefix
	// the string with "caddy" or the name of the command
	// since these will be prepended for you; only include
	// the actual parameters for this command.
	Usage string

	// Short is a one-line message explaining what the
	// command does. Should not end with punctuation.
	// Required.
	Short string

	// Long is the full help text shown to the user.
	// Will be trimmed of whitespace on both ends before
	// being printed.
	Long string

	// Flags is the flagset for command.
	Flags *flag.FlagSet
}

// CommandFunc is a command's function. It runs the
// command and returns the proper exit code along with
// any error that occurred.
type CommandFunc func(Flags) (int, error)

var commands = make(map[string]Command)

func init() {
	RegisterCommand(Command{
		Name:  "help",
		Func:  cmdHelp,
		Usage: "<command>",
		Short: "Shows help for a Caddy subcommand",
	})

	RegisterCommand(Command{
		Name:  "start",
		Func:  cmdStart,
		Usage: "[--config <path> [--adapter <name>]] [--watch]",
		Short: "Starts the Caddy process in the background and then returns",
		Long: `
Starts the Caddy process, optionally bootstrapped with an initial config file.
This command unblocks after the server starts running or fails to run.

On Windows, the spawned child process will remain attached to the terminal, so
closing the window will forcefully stop Caddy; to avoid forgetting this, try
using 'caddy run' instead to keep it in the foreground.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("start", flag.ExitOnError)
			fs.String("config", "", "Configuration file")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.Bool("watch", false, "Reload changed config file automatically")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "run",
		Func:  cmdRun,
		Usage: "[--config <path> [--adapter <name>]] [--environ] [--watch]",
		Short: `Starts the Caddy process and blocks indefinitely`,
		Long: `
Starts the Caddy process, optionally bootstrapped with an initial config file,
and blocks indefinitely until the server is stopped; i.e. runs Caddy in
"daemon" mode (foreground).

If a config file is specified, it will be applied immediately after the process
is running. If the config file is not in Caddy's native JSON format, you can
specify an adapter with --adapter to adapt the given config file to
Caddy's native format. The config adapter must be a registered module. Any
warnings will be printed to the log, but beware that any adaptation without
errors will immediately be used. If you want to review the results of the
adaptation first, use the 'adapt' subcommand.

As a special case, if the current working directory has a file called
"Caddyfile" and the caddyfile config adapter is plugged in (default), then
that file will be loaded and used to configure Caddy, even without any command
line flags.

If --environ is specified, the environment as seen by the Caddy process will
be printed before starting. This is the same as the environ command but does
not quit after printing, and can be useful for troubleshooting.

The --resume flag will override the --config flag if there is a config auto-
save file. It is not an error if --resume is used and no autosave file exists.

If --watch is specified, the config file will be loaded automatically after
changes. ⚠ This is dangerous in production! Only use this option in a local
development environment.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("run", flag.ExitOnError)
			fs.String("config", "", "Configuration file")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.Bool("environ", false, "Print environment")
			fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
			fs.Bool("watch", false, "Watch config file for changes and reload it automatically")
			fs.String("pingback", "", "Echo confirmation bytes to this address on success")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "stop",
		Func:  cmdStop,
		Short: "Gracefully stops a started Caddy process",
		Long: `
Stops the background Caddy process as gracefully as possible.

It requires that the admin API is enabled and accessible, since it will
use the API's /stop endpoint. The address of this request can be
customized using the --address flag if it is not the default.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("stop", flag.ExitOnError)
			fs.String("address", "", "The address to use to reach the admin API endpoint, if not the default")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "reload",
		Func:  cmdReload,
		Usage: "--config <path> [--adapter <name>] [--address <interface>]",
		Short: "Changes the config of the running Caddy instance",
		Long: `
Gives the running Caddy instance a new configuration. This has the same effect
as POSTing a document to the /load API endpoint, but is convenient for simple
workflows revolving around config files.

Since the admin endpoint is configurable, the endpoint configuration is loaded
from the --address flag if specified; otherwise it is loaded from the given
config file; otherwise the default is assumed.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("reload", flag.ExitOnError)
			fs.String("config", "", "Configuration file (required)")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.String("address", "", "Address of the administration listener, if different from config")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "version",
		Func:  cmdVersion,
		Short: "Prints the version",
	})

	RegisterCommand(Command{
		Name:  "list-modules",
		Func:  cmdListModules,
		Usage: "[--versions]",
		Short: "Lists the installed Caddy modules",
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
			fs.Bool("versions", false, "Print version information")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "build-info",
		Func:  cmdBuildInfo,
		Short: "Prints information about this build",
	})

	RegisterCommand(Command{
		Name:  "environ",
		Func:  cmdEnviron,
		Short: "Prints the environment",
	})

	RegisterCommand(Command{
		Name:  "adapt",
		Func:  cmdAdaptConfig,
		Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
		Short: "Adapts a configuration to Caddy's native JSON",
		Long: `
Adapts a configuration to Caddy's native JSON format and writes the
output to stdout, along with any warnings to stderr.

If --pretty is specified, the output will be formatted with indentation
for human readability.

If --validate is used, the adapted config will be checked for validity.
If the config is invalid, an error will be printed to stderr and a non-
zero exit status will be returned.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("adapt", flag.ExitOnError)
			fs.String("config", "", "Configuration file to adapt (required)")
			fs.String("adapter", "caddyfile", "Name of config adapter")
			fs.Bool("pretty", false, "Format the output for human readability")
			fs.Bool("validate", false, "Validate the output")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "validate",
		Func:  cmdValidateConfig,
		Usage: "--config <path> [--adapter <name>]",
		Short: "Tests whether a configuration file is valid",
		Long: `
Loads and provisions the provided config, but does not start running it.
This reveals any errors with the configuration through the loading and
provisioning stages.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("load", flag.ExitOnError)
			fs.String("config", "", "Input configuration file")
			fs.String("adapter", "", "Name of config adapter")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "fmt",
		Func:  cmdFmt,
		Usage: "[--overwrite] [<path>]",
		Short: "Formats a Caddyfile",
		Long: `
Formats the Caddyfile by adding proper indentation and spaces to improve
human readability. It prints the result to stdout.

If --write is specified, the output will be written to the config file
directly instead of printing it.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("format", flag.ExitOnError)
			fs.Bool("overwrite", false, "Overwrite the input file with the results")
			return fs
		}(),
	})

}

// RegisterCommand registers the command cmd.
// cmd.Name must be unique and conform to the
// following format:
//
//    - lowercase
//    - alphanumeric and hyphen characters only
//    - cannot start or end with a hyphen
//    - hyphen cannot be adjacent to another hyphen
//
// This function panics if the name is already registered,
// if the name does not meet the described format, or if
// any of the fields are missing from cmd.
//
// This function should be used in init().
func RegisterCommand(cmd Command) {
	if cmd.Name == "" {
		panic("command name is required")
	}
	if cmd.Func == nil {
		panic("command function missing")
	}
	if cmd.Short == "" {
		panic("command short string is required")
	}
	if _, exists := commands[cmd.Name]; exists {
		panic("command already registered: " + cmd.Name)
	}
	if !commandNameRegex.MatchString(cmd.Name) {
		panic("invalid command name")
	}
	commands[cmd.Name] = cmd
}

var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
<commit_msg>docs: Fix command.Func documentation (#3371)<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddycmd

import (
	"flag"
	"regexp"
)

// Command represents a subcommand. Name, Func,
// and Short are required.
type Command struct {
	// The name of the subcommand. Must conform to the
	// format described by the RegisterCommand() godoc.
	// Required.
	Name string

	// Func is a function that executes a subcommand using
	// the parsed flags. It returns an exit code and any
	// associated error.
	// Required.
	Func CommandFunc

	// Usage is a brief message describing the syntax of
	// the subcommand's flags and args. Use [] to indicate
	// optional parameters and <> to enclose literal values
	// intended to be replaced by the user. Do not prefix
	// the string with "caddy" or the name of the command
	// since these will be prepended for you; only include
	// the actual parameters for this command.
	Usage string

	// Short is a one-line message explaining what the
	// command does. Should not end with punctuation.
	// Required.
	Short string

	// Long is the full help text shown to the user.
	// Will be trimmed of whitespace on both ends before
	// being printed.
	Long string

	// Flags is the flagset for command.
	Flags *flag.FlagSet
}

// CommandFunc is a command's function. It runs the
// command and returns the proper exit code along with
// any error that occurred.
type CommandFunc func(Flags) (int, error)

var commands = make(map[string]Command)

func init() {
	RegisterCommand(Command{
		Name:  "help",
		Func:  cmdHelp,
		Usage: "<command>",
		Short: "Shows help for a Caddy subcommand",
	})

	RegisterCommand(Command{
		Name:  "start",
		Func:  cmdStart,
		Usage: "[--config <path> [--adapter <name>]] [--watch]",
		Short: "Starts the Caddy process in the background and then returns",
		Long: `
Starts the Caddy process, optionally bootstrapped with an initial config file.
This command unblocks after the server starts running or fails to run.

On Windows, the spawned child process will remain attached to the terminal, so
closing the window will forcefully stop Caddy; to avoid forgetting this, try
using 'caddy run' instead to keep it in the foreground.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("start", flag.ExitOnError)
			fs.String("config", "", "Configuration file")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.Bool("watch", false, "Reload changed config file automatically")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "run",
		Func:  cmdRun,
		Usage: "[--config <path> [--adapter <name>]] [--environ] [--watch]",
		Short: `Starts the Caddy process and blocks indefinitely`,
		Long: `
Starts the Caddy process, optionally bootstrapped with an initial config file,
and blocks indefinitely until the server is stopped; i.e. runs Caddy in
"daemon" mode (foreground).

If a config file is specified, it will be applied immediately after the process
is running. If the config file is not in Caddy's native JSON format, you can
specify an adapter with --adapter to adapt the given config file to
Caddy's native format. The config adapter must be a registered module. Any
warnings will be printed to the log, but beware that any adaptation without
errors will immediately be used. If you want to review the results of the
adaptation first, use the 'adapt' subcommand.

As a special case, if the current working directory has a file called
"Caddyfile" and the caddyfile config adapter is plugged in (default), then
that file will be loaded and used to configure Caddy, even without any command
line flags.

If --environ is specified, the environment as seen by the Caddy process will
be printed before starting. This is the same as the environ command but does
not quit after printing, and can be useful for troubleshooting.

The --resume flag will override the --config flag if there is a config auto-
save file. It is not an error if --resume is used and no autosave file exists.

If --watch is specified, the config file will be loaded automatically after
changes. ⚠ This is dangerous in production! Only use this option in a local
development environment.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("run", flag.ExitOnError)
			fs.String("config", "", "Configuration file")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.Bool("environ", false, "Print environment")
			fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
			fs.Bool("watch", false, "Watch config file for changes and reload it automatically")
			fs.String("pingback", "", "Echo confirmation bytes to this address on success")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "stop",
		Func:  cmdStop,
		Short: "Gracefully stops a started Caddy process",
		Long: `
Stops the background Caddy process as gracefully as possible.

It requires that the admin API is enabled and accessible, since it will
use the API's /stop endpoint. The address of this request can be
customized using the --address flag if it is not the default.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("stop", flag.ExitOnError)
			fs.String("address", "", "The address to use to reach the admin API endpoint, if not the default")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "reload",
		Func:  cmdReload,
		Usage: "--config <path> [--adapter <name>] [--address <interface>]",
		Short: "Changes the config of the running Caddy instance",
		Long: `
Gives the running Caddy instance a new configuration. This has the same effect
as POSTing a document to the /load API endpoint, but is convenient for simple
workflows revolving around config files.

Since the admin endpoint is configurable, the endpoint configuration is loaded
from the --address flag if specified; otherwise it is loaded from the given
config file; otherwise the default is assumed.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("reload", flag.ExitOnError)
			fs.String("config", "", "Configuration file (required)")
			fs.String("adapter", "", "Name of config adapter to apply")
			fs.String("address", "", "Address of the administration listener, if different from config")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "version",
		Func:  cmdVersion,
		Short: "Prints the version",
	})

	RegisterCommand(Command{
		Name:  "list-modules",
		Func:  cmdListModules,
		Usage: "[--versions]",
		Short: "Lists the installed Caddy modules",
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
			fs.Bool("versions", false, "Print version information")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "build-info",
		Func:  cmdBuildInfo,
		Short: "Prints information about this build",
	})

	RegisterCommand(Command{
		Name:  "environ",
		Func:  cmdEnviron,
		Short: "Prints the environment",
	})

	RegisterCommand(Command{
		Name:  "adapt",
		Func:  cmdAdaptConfig,
		Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
		Short: "Adapts a configuration to Caddy's native JSON",
		Long: `
Adapts a configuration to Caddy's native JSON format and writes the
output to stdout, along with any warnings to stderr.

If --pretty is specified, the output will be formatted with indentation
for human readability.

If --validate is used, the adapted config will be checked for validity.
If the config is invalid, an error will be printed to stderr and a non-
zero exit status will be returned.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("adapt", flag.ExitOnError)
			fs.String("config", "", "Configuration file to adapt (required)")
			fs.String("adapter", "caddyfile", "Name of config adapter")
			fs.Bool("pretty", false, "Format the output for human readability")
			fs.Bool("validate", false, "Validate the output")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "validate",
		Func:  cmdValidateConfig,
		Usage: "--config <path> [--adapter <name>]",
		Short: "Tests whether a configuration file is valid",
		Long: `
Loads and provisions the provided config, but does not start running it.
This reveals any errors with the configuration through the loading and
provisioning stages.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("load", flag.ExitOnError)
			fs.String("config", "", "Input configuration file")
			fs.String("adapter", "", "Name of config adapter")
			return fs
		}(),
	})

	RegisterCommand(Command{
		Name:  "fmt",
		Func:  cmdFmt,
		Usage: "[--overwrite] [<path>]",
		Short: "Formats a Caddyfile",
		Long: `
Formats the Caddyfile by adding proper indentation and spaces to improve
human readability. It prints the result to stdout.

If --write is specified, the output will be written to the config file
directly instead of printing it.`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("format", flag.ExitOnError)
			fs.Bool("overwrite", false, "Overwrite the input file with the results")
			return fs
		}(),
	})

}

// RegisterCommand registers the command cmd.
// cmd.Name must be unique and conform to the
// following format:
//
//    - lowercase
//    - alphanumeric and hyphen characters only
//    - cannot start or end with a hyphen
//    - hyphen cannot be adjacent to another hyphen
//
// This function panics if the name is already registered,
// if the name does not meet the described format, or if
// any of the fields are missing from cmd.
//
// This function should be used in init().
func RegisterCommand(cmd Command) {
	if cmd.Name == "" {
		panic("command name is required")
	}
	if cmd.Func == nil {
		panic("command function missing")
	}
	if cmd.Short == "" {
		panic("command short string is required")
	}
	if _, exists := commands[cmd.Name]; exists {
		panic("command already registered: " + cmd.Name)
	}
	if !commandNameRegex.MatchString(cmd.Name) {
		panic("invalid command name")
	}
	commands[cmd.Name] = cmd
}

var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
<|endoftext|>


<commit_before>package db

import (
	"github.com/jinzhu/gorm"
	"time"

	"github.com/Nekitosss/authorization/utils"

	"github.com/satori/go.uuid"
)

const (
	insertSessionSQL = "INSERT INTO" + sessionTable + "(" + sessionColumns + ") VALUES ($1, $2, $3, $4)"

	selectSessionSQL = "SELECT" + sessionColumns + "FROM" + sessionTable+  "WHERE userid = $1"

	sessionColumns = " identifier, userid, logintime, lastseentime "

	sessionTable = " auth.session "

	validateSessionSQL = "SELECT userid FROM " + sessionTable + " WHERE session = $1"
)

func GetSession(database  *gorm.DB, userid uuid.UUID) (Session, error) {

	var session = Session{}

	err := database.Where("user_id = $1", userid).Find(&session).Error

	if err != gorm.ErrRecordNotFound {
		return session, err

	} else if err == gorm.ErrRecordNotFound {
		return createSession(database, userid)

	} else {
		return session, nil
	}

}

func ValidateSession(database *gorm.DB, sessionID uuid.UUID) uuid.UUID {

	userID := uuid.Nil

	var session = Session{}
	session.SessionID = uuid.Nil

	err := database.First(&session, sessionID).Error

	if err != gorm.ErrRecordNotFound {
		utils.CheckError(err)
	}

	return userID
}

func createSession(database *gorm.DB, userid uuid.UUID) (Session, error) {

	var session = Session{}
	session.SessionID = uuid.NewV4()
	session.UserModelID = userid
	session.LoginTime = time.Now()
	session.LastSeenTime = session.LoginTime

	err := database.Create(&session).Error

	return session, err
}<commit_msg>fix again<commit_after>package db

import (
	"github.com/jinzhu/gorm"
	"time"

	"github.com/Nekitosss/authorization/utils"

	"github.com/satori/go.uuid"
)

const (
	insertSessionSQL = "INSERT INTO" + sessionTable + "(" + sessionColumns + ") VALUES ($1, $2, $3, $4)"

	selectSessionSQL = "SELECT" + sessionColumns + "FROM" + sessionTable+  "WHERE userid = $1"

	sessionColumns = " identifier, userid, logintime, lastseentime "

	sessionTable = " auth.session "

	validateSessionSQL = "SELECT userid FROM " + sessionTable + " WHERE session = $1"
)

func GetSession(database  *gorm.DB, userid uuid.UUID) (Session, error) {

	var session = Session{}

	err := database.Where("user_model_id = $1", userid).Find(&session).Error

	if err != gorm.ErrRecordNotFound {
		return session, err

	} else if err == gorm.ErrRecordNotFound {
		return createSession(database, userid)

	} else {
		return session, nil
	}

}

func ValidateSession(database *gorm.DB, sessionID uuid.UUID) uuid.UUID {

	userID := uuid.Nil

	var session = Session{}
	session.SessionID = uuid.Nil

	err := database.First(&session, sessionID).Error

	if err != gorm.ErrRecordNotFound {
		utils.CheckError(err)
	}

	return userID
}

func createSession(database *gorm.DB, userid uuid.UUID) (Session, error) {

	var session = Session{}
	session.SessionID = uuid.NewV4()
	session.UserModelID = userid
	session.LoginTime = time.Now()
	session.LastSeenTime = session.LoginTime

	err := database.Create(&session).Error

	return session, err
}<|endoftext|>


<commit_before>// Copyright 2016 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.

//go:generate ./mkdoc.sh

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/tabwriter"

	"github.com/golang/dep"
)

var (
	successExitCode = 0
	errorExitCode   = 1
)

type command interface {
	Name() string           // "foobar"
	Args() string           // "<baz> [quux...]"
	ShortHelp() string      // "Foo the first bar"
	LongHelp() string       // "Foo the first bar meeting the following conditions..."
	Register(*flag.FlagSet) // command-specific flags
	Hidden() bool           // indicates whether the command should be hidden from help output
	Run(*dep.Ctx, []string) error
}

func main() {
	wd, err := os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, "failed to get working directory", err)
		os.Exit(1)
	}
	c := &Config{
		Args:       os.Args,
		Stdout:     os.Stdout,
		Stderr:     os.Stderr,
		WorkingDir: wd,
		Env:        os.Environ(),
	}
	os.Exit(c.Run())
}

// A Config specifies a full configuration for a dep execution.
type Config struct {
	WorkingDir     string    // Where to execute
	Args           []string  // Command-line arguments, starting with the program name.
	Env            []string  // Environment variables
	Stdout, Stderr io.Writer // Log output
}

// Run executes a configuration and returns an exit code.
func (c *Config) Run() int {
	// Build the list of available commands.
	commands := [...]command{
		&initCommand{},
		&statusCommand{},
		&ensureCommand{},
		&hashinCommand{},
		&pruneCommand{},
		&versionCommand{},
	}

	examples := [...][2]string{
		{
			"dep init",
			"set up a new project",
		},
		{
			"dep ensure",
			"install the project's dependencies",
		},
		{
			"dep ensure -update",
			"update the locked versions of all dependencies",
		},
		{
			"dep ensure -add github.com/pkg/errors",
			"add a dependency to the project",
		},
	}

	usage := func(w io.Writer) {
		fmt.Fprintln(w, "Dep is a tool for managing dependencies for Go projects")
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Usage: \"dep [command]\"")
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Commands:")
		fmt.Fprintln(w)
		tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
		for _, cmd := range commands {
			if !cmd.Hidden() {
				fmt.Fprintf(tw, "\t%s\t%s\n", cmd.Name(), cmd.ShortHelp())
			}
		}
		tw.Flush()
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Examples:")
		for _, example := range examples {
			fmt.Fprintf(tw, "\t%s\t%s\n", example[0], example[1])
		}
		tw.Flush()
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Use \"dep help [command]\" for more information about a command.")
	}

	cmdName, printCommandHelp, exit := parseArgs(c.Args)
	if exit {
		usage(c.Stderr)
		return errorExitCode
	}

	// 'dep help documentation' generates doc.go.
	if printCommandHelp && cmdName == "documentation" {
		fmt.Println("// Copyright 2017 The Go Authors. All rights reserved.")
		fmt.Println("// Use of this source code is governed by a BSD-style")
		fmt.Println("// license that can be found in the LICENSE file.")
		fmt.Println()
		fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkdoc.sh.")
		fmt.Println("// Edit the documentation in other files and rerun mkdoc.sh to generate this one.")
		fmt.Println()

		var cw io.Writer = &commentWriter{W: c.Stdout}
		usage(cw)
		for _, cmd := range commands {
			if !cmd.Hidden() {
				fmt.Fprintln(cw)
				short := cmd.ShortHelp()
				fmt.Fprintln(cw, short)
				fmt.Fprintln(cw)
				fmt.Fprintln(cw, "Usage:")
				fmt.Fprintln(cw)
				fmt.Fprintln(cw, "", cmd.Name(), cmd.Args())
				if long := cmd.LongHelp(); long != short {
					fmt.Fprintln(cw, long)
				}
			}
		}

		fmt.Println("//")
		fmt.Println("package main")
		return successExitCode
	}

	outLogger := log.New(c.Stdout, "", 0)
	errLogger := log.New(c.Stderr, "", 0)

	for _, cmd := range commands {
		if cmd.Name() == cmdName {
			// Build flag set with global flags in there.
			fs := flag.NewFlagSet(cmdName, flag.ContinueOnError)
			fs.SetOutput(c.Stderr)
			verbose := fs.Bool("v", false, "enable verbose logging")

			// Register the subcommand flags in there, too.
			cmd.Register(fs)

			// Override the usage text to something nicer.
			resetUsage(errLogger, fs, cmdName, cmd.Args(), cmd.LongHelp())

			if printCommandHelp {
				fs.Usage()
				return errorExitCode
			}

			// Parse the flags the user gave us.
			// flag package automatically prints usage and error message in err != nil
			// or if '-h' flag provided
			if err := fs.Parse(c.Args[2:]); err != nil {
				return errorExitCode
			}

			// Set up dep context.
			ctx := &dep.Ctx{
				Out:            outLogger,
				Err:            errLogger,
				Verbose:        *verbose,
				DisableLocking: getEnv(c.Env, "DEPNOLOCK") != "",
			}

			GOPATHS := filepath.SplitList(getEnv(c.Env, "GOPATH"))
			ctx.SetPaths(c.WorkingDir, GOPATHS...)

			// Run the command with the post-flag-processing args.
			if err := cmd.Run(ctx, fs.Args()); err != nil {
				errLogger.Printf("%v\n", err)
				return errorExitCode
			}

			// Easy peasy livin' breezy.
			return successExitCode
		}
	}

	errLogger.Printf("dep: %s: no such command\n", cmdName)
	usage(c.Stderr)
	return errorExitCode
}

func resetUsage(logger *log.Logger, fs *flag.FlagSet, name, args, longHelp string) {
	var (
		hasFlags   bool
		flagBlock  bytes.Buffer
		flagWriter = tabwriter.NewWriter(&flagBlock, 0, 4, 2, ' ', 0)
	)
	fs.VisitAll(func(f *flag.Flag) {
		hasFlags = true
		// Default-empty string vars should read "(default: <none>)"
		// rather than the comparatively ugly "(default: )".
		defValue := f.DefValue
		if defValue == "" {
			defValue = "<none>"
		}
		fmt.Fprintf(flagWriter, "\t-%s\t%s (default: %s)\n", f.Name, f.Usage, defValue)
	})
	flagWriter.Flush()
	fs.Usage = func() {
		logger.Printf("Usage: dep %s %s\n", name, args)
		logger.Println()
		logger.Println(strings.TrimSpace(longHelp))
		logger.Println()
		if hasFlags {
			logger.Println("Flags:")
			logger.Println()
			logger.Println(flagBlock.String())
		}
	}
}

// parseArgs determines the name of the dep command and whether the user asked for
// help to be printed.
func parseArgs(args []string) (cmdName string, printCmdUsage bool, exit bool) {
	isHelpArg := func() bool {
		return strings.Contains(strings.ToLower(args[1]), "help") || strings.ToLower(args[1]) == "-h"
	}

	switch len(args) {
	case 0, 1:
		exit = true
	case 2:
		if isHelpArg() {
			exit = true
		} else {
			cmdName = args[1]
		}
	default:
		if isHelpArg() {
			cmdName = args[2]
			printCmdUsage = true
		} else {
			cmdName = args[1]
		}
	}
	return cmdName, printCmdUsage, exit
}

// getEnv returns the last instance of an environment variable.
func getEnv(env []string, key string) string {
	for i := len(env) - 1; i >= 0; i-- {
		v := env[i]
		kv := strings.SplitN(v, "=", 2)
		if kv[0] == key {
			if len(kv) > 1 {
				return kv[1]
			}
			return ""
		}
	}
	return ""
}

// commentWriter writes a Go comment to the underlying io.Writer,
// using line comment form (//).
//
// Copied from cmd/go/internal/help/help.go.
type commentWriter struct {
	W            io.Writer
	wroteSlashes bool // Wrote "//" at the beginning of the current line.
}

func (c *commentWriter) Write(p []byte) (int, error) {
	var n int
	for i, b := range p {
		if !c.wroteSlashes {
			s := "//"
			if b != '\n' {
				s = "// "
			}
			if _, err := io.WriteString(c.W, s); err != nil {
				return n, err
			}
			c.wroteSlashes = true
		}
		n0, err := c.W.Write(p[i : i+1])
		n += n0
		if err != nil {
			return n, err
		}
		if b == '\n' {
			c.wroteSlashes = false
		}
	}
	return len(p), nil
}
<commit_msg>Add profiling flags<commit_after>// Copyright 2016 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.

//go:generate ./mkdoc.sh

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"text/tabwriter"

	"github.com/golang/dep"
)

var (
	successExitCode = 0
	errorExitCode   = 1
)

type command interface {
	Name() string           // "foobar"
	Args() string           // "<baz> [quux...]"
	ShortHelp() string      // "Foo the first bar"
	LongHelp() string       // "Foo the first bar meeting the following conditions..."
	Register(*flag.FlagSet) // command-specific flags
	Hidden() bool           // indicates whether the command should be hidden from help output
	Run(*dep.Ctx, []string) error
}

func main() {
	p := &profile{}
	flag.StringVar(&p.cpuProfile, "cpuprofile", "", "Writes a CPU profile to the specified file before exiting.")
	flag.StringVar(&p.memProfile, "memprofile", "", "Writes a memory profile to the specified file before exiting.")
	flag.IntVar(&p.memProfileRate, "memprofilerate", 0, "Enable more precise memory profiles by setting runtime.MemProfileRate.")
	flag.StringVar(&p.mutexProfile, "mutexprofile", "", "Writes a mutex profile to the specified file before exiting.")
	flag.IntVar(&p.mutexProfileFraction, "mutexprofilefraction", 0, "Enable more precise mutex profiles by runtime.SetMutexProfileFraction.")
	flag.Parse()

	wd, err := os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, "failed to get working directory", err)
		os.Exit(1)
	}

	args := append([]string{os.Args[0]}, flag.Args()...)
	c := &Config{
		Args:       args,
		Stdout:     os.Stdout,
		Stderr:     os.Stderr,
		WorkingDir: wd,
		Env:        os.Environ(),
	}

	if err := p.start(); err != nil {
		fmt.Fprintf(os.Stderr, "failed to profile: %v\n", err)
		os.Exit(1)
	}
	exit := c.Run()
	if err := p.finish(); err != nil {
		fmt.Fprintf(os.Stderr, "failed to finish the profile: %v\n", err)
		os.Exit(1)
	}
	os.Exit(exit)
}

// A Config specifies a full configuration for a dep execution.
type Config struct {
	WorkingDir     string    // Where to execute
	Args           []string  // Command-line arguments, starting with the program name.
	Env            []string  // Environment variables
	Stdout, Stderr io.Writer // Log output
}

// Run executes a configuration and returns an exit code.
func (c *Config) Run() int {
	// Build the list of available commands.
	commands := [...]command{
		&initCommand{},
		&statusCommand{},
		&ensureCommand{},
		&hashinCommand{},
		&pruneCommand{},
		&versionCommand{},
	}

	examples := [...][2]string{
		{
			"dep init",
			"set up a new project",
		},
		{
			"dep ensure",
			"install the project's dependencies",
		},
		{
			"dep ensure -update",
			"update the locked versions of all dependencies",
		},
		{
			"dep ensure -add github.com/pkg/errors",
			"add a dependency to the project",
		},
	}

	usage := func(w io.Writer) {
		fmt.Fprintln(w, "Dep is a tool for managing dependencies for Go projects")
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Usage: \"dep [command]\"")
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Commands:")
		fmt.Fprintln(w)
		tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
		for _, cmd := range commands {
			if !cmd.Hidden() {
				fmt.Fprintf(tw, "\t%s\t%s\n", cmd.Name(), cmd.ShortHelp())
			}
		}
		tw.Flush()
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Examples:")
		for _, example := range examples {
			fmt.Fprintf(tw, "\t%s\t%s\n", example[0], example[1])
		}
		tw.Flush()
		fmt.Fprintln(w)
		fmt.Fprintln(w, "Use \"dep help [command]\" for more information about a command.")
	}

	cmdName, printCommandHelp, exit := parseArgs(c.Args)
	if exit {
		usage(c.Stderr)
		return errorExitCode
	}

	// 'dep help documentation' generates doc.go.
	if printCommandHelp && cmdName == "documentation" {
		fmt.Println("// Copyright 2017 The Go Authors. All rights reserved.")
		fmt.Println("// Use of this source code is governed by a BSD-style")
		fmt.Println("// license that can be found in the LICENSE file.")
		fmt.Println()
		fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkdoc.sh.")
		fmt.Println("// Edit the documentation in other files and rerun mkdoc.sh to generate this one.")
		fmt.Println()

		var cw io.Writer = &commentWriter{W: c.Stdout}
		usage(cw)
		for _, cmd := range commands {
			if !cmd.Hidden() {
				fmt.Fprintln(cw)
				short := cmd.ShortHelp()
				fmt.Fprintln(cw, short)
				fmt.Fprintln(cw)
				fmt.Fprintln(cw, "Usage:")
				fmt.Fprintln(cw)
				fmt.Fprintln(cw, "", cmd.Name(), cmd.Args())
				if long := cmd.LongHelp(); long != short {
					fmt.Fprintln(cw, long)
				}
			}
		}

		fmt.Println("//")
		fmt.Println("package main")
		return successExitCode
	}

	outLogger := log.New(c.Stdout, "", 0)
	errLogger := log.New(c.Stderr, "", 0)

	for _, cmd := range commands {
		if cmd.Name() == cmdName {
			// Build flag set with global flags in there.
			fs := flag.NewFlagSet(cmdName, flag.ContinueOnError)
			fs.SetOutput(c.Stderr)
			verbose := fs.Bool("v", false, "enable verbose logging")

			// Register the subcommand flags in there, too.
			cmd.Register(fs)

			// Override the usage text to something nicer.
			resetUsage(errLogger, fs, cmdName, cmd.Args(), cmd.LongHelp())

			if printCommandHelp {
				fs.Usage()
				return errorExitCode
			}

			// Parse the flags the user gave us.
			// flag package automatically prints usage and error message in err != nil
			// or if '-h' flag provided
			if err := fs.Parse(c.Args[2:]); err != nil {
				return errorExitCode
			}

			// Set up dep context.
			ctx := &dep.Ctx{
				Out:            outLogger,
				Err:            errLogger,
				Verbose:        *verbose,
				DisableLocking: getEnv(c.Env, "DEPNOLOCK") != "",
			}

			GOPATHS := filepath.SplitList(getEnv(c.Env, "GOPATH"))
			ctx.SetPaths(c.WorkingDir, GOPATHS...)

			// Run the command with the post-flag-processing args.
			if err := cmd.Run(ctx, fs.Args()); err != nil {
				errLogger.Printf("%v\n", err)
				return errorExitCode
			}

			// Easy peasy livin' breezy.
			return successExitCode
		}
	}

	errLogger.Printf("dep: %s: no such command\n", cmdName)
	usage(c.Stderr)
	return errorExitCode
}

func resetUsage(logger *log.Logger, fs *flag.FlagSet, name, args, longHelp string) {
	var (
		hasFlags   bool
		flagBlock  bytes.Buffer
		flagWriter = tabwriter.NewWriter(&flagBlock, 0, 4, 2, ' ', 0)
	)
	fs.VisitAll(func(f *flag.Flag) {
		hasFlags = true
		// Default-empty string vars should read "(default: <none>)"
		// rather than the comparatively ugly "(default: )".
		defValue := f.DefValue
		if defValue == "" {
			defValue = "<none>"
		}
		fmt.Fprintf(flagWriter, "\t-%s\t%s (default: %s)\n", f.Name, f.Usage, defValue)
	})
	flagWriter.Flush()
	fs.Usage = func() {
		logger.Printf("Usage: dep %s %s\n", name, args)
		logger.Println()
		logger.Println(strings.TrimSpace(longHelp))
		logger.Println()
		if hasFlags {
			logger.Println("Flags:")
			logger.Println()
			logger.Println(flagBlock.String())
		}
	}
}

// parseArgs determines the name of the dep command and whether the user asked for
// help to be printed.
func parseArgs(args []string) (cmdName string, printCmdUsage bool, exit bool) {
	isHelpArg := func() bool {
		return strings.Contains(strings.ToLower(args[1]), "help") || strings.ToLower(args[1]) == "-h"
	}

	switch len(args) {
	case 0, 1:
		exit = true
	case 2:
		if isHelpArg() {
			exit = true
		} else {
			cmdName = args[1]
		}
	default:
		if isHelpArg() {
			cmdName = args[2]
			printCmdUsage = true
		} else {
			cmdName = args[1]
		}
	}
	return cmdName, printCmdUsage, exit
}

// getEnv returns the last instance of an environment variable.
func getEnv(env []string, key string) string {
	for i := len(env) - 1; i >= 0; i-- {
		v := env[i]
		kv := strings.SplitN(v, "=", 2)
		if kv[0] == key {
			if len(kv) > 1 {
				return kv[1]
			}
			return ""
		}
	}
	return ""
}

// commentWriter writes a Go comment to the underlying io.Writer,
// using line comment form (//).
//
// Copied from cmd/go/internal/help/help.go.
type commentWriter struct {
	W            io.Writer
	wroteSlashes bool // Wrote "//" at the beginning of the current line.
}

func (c *commentWriter) Write(p []byte) (int, error) {
	var n int
	for i, b := range p {
		if !c.wroteSlashes {
			s := "//"
			if b != '\n' {
				s = "// "
			}
			if _, err := io.WriteString(c.W, s); err != nil {
				return n, err
			}
			c.wroteSlashes = true
		}
		n0, err := c.W.Write(p[i : i+1])
		n += n0
		if err != nil {
			return n, err
		}
		if b == '\n' {
			c.wroteSlashes = false
		}
	}
	return len(p), nil
}

type profile struct {
	cpuProfile string

	memProfile     string
	memProfileRate int

	mutexProfile         string
	mutexProfileFraction int

	// TODO(jbd): Add block profile and -trace.

	f *os.File // file to write the profiling output to
}

func (p *profile) start() error {
	switch {
	case p.cpuProfile != "":
		if err := p.createOutput(p.cpuProfile); err != nil {
			return err
		}
		return pprof.StartCPUProfile(p.f)
	case p.memProfile != "":
		if p.memProfileRate > 0 {
			runtime.MemProfileRate = p.memProfileRate
		}
		return p.createOutput(p.memProfile)
	case p.mutexProfile != "":
		if p.mutexProfileFraction > 0 {
			runtime.SetMutexProfileFraction(p.mutexProfileFraction)
		}
		return p.createOutput(p.mutexProfile)
	}
	return nil
}

func (p *profile) finish() error {
	if p.f == nil {
		return nil
	}
	switch {
	case p.cpuProfile != "":
		pprof.StopCPUProfile()
	case p.memProfile != "":
		if err := pprof.WriteHeapProfile(p.f); err != nil {
			return err
		}
	case p.mutexProfile != "":
		if err := pprof.Lookup("mutex").WriteTo(p.f, 2); err != nil {
			return err
		}
	}
	return p.f.Close()
}

func (p *profile) createOutput(name string) error {
	f, err := os.Create(name)
	if err != nil {
		return err
	}
	p.f = f
	return nil
}
<|endoftext|>