<commit_before>package builder

import (
	"github.com/appc/spec/schema/types"
	"github.com/blablacar/cnt/dist"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/spec"
	"github.com/blablacar/cnt/utils"
	"io/ioutil"
	"os"
	"strings"
)

const BATS_ACI = "aci.blablacar.com/aci-bats:1"
const PATH_RESULT = "/result"
const TEST_INIT_SCRIPT = `#!/bin/bash
set -x

%%COMMAND%% &

waitsuccess="0"
if [ -f "/tests/wait.sh" ]; then
	chmod +x /tests/wait.sh
	i="0"
	while [ $i -lt 60 ]; do
	  /tests/wait.sh
	  if [ $? == 0 ]; then
	  	waitsuccess="1"
	  	break;
	  fi
	  i=$[$i+1]
	  sleep 1
	done
fi

if [ wait_success == "0" ]; then
	echo "1\n" > /result/wait.sh
	echo "WAIT FAILED"
	exit 1
fi

/test.sh
`

func (cnt *Img) Test() {
	cnt.Install()
	log.Get().Info("Testing " + cnt.manifest.NameAndVersion)
	cnt.importAciBats()

	testAci, err := cnt.prepareTestAci()
	if err != nil {
		log.Get().Panic(err)
	}
	testAci.Build()

	os.MkdirAll(cnt.target+PATH_TEST+PATH_TARGET+PATH_RESULT, 0777)

	if err := utils.ExecCmd("rkt",
		"--insecure-skip-verify=true",
		"run",
		"--local",
		"--volume=result,kind=host,source="+cnt.target+PATH_TEST+PATH_TARGET+PATH_RESULT,
		cnt.target+PATH_TEST+PATH_TARGET+"/image.aci"); err != nil {
		// rkt+systemd cannot exit with fail status yet
		log.Get().Panic(err)
	}

	cnt.checkResult()
}

func (cnt *Img) checkResult() {
	files, err := ioutil.ReadDir(cnt.target + PATH_TEST + PATH_TARGET + PATH_RESULT)
	if err != nil {
		log.Get().Panic("Cannot read test result directory", err)
	}
	for _, f := range files {
		content, err := ioutil.ReadFile(cnt.target + PATH_TEST + PATH_TARGET + PATH_RESULT + "/" + f.Name())
		if err != nil {
			log.Get().Panic("Cannot read result file", f.Name(), err)
		}
		if string(content) != "0\n" {
			log.Get().Error("Failed test file : ", f.Name())
			os.Exit(2)
		}
	}
}

func (cnt *Img) importAciBats() {
	if err := utils.ExecCmd("bash", "-c", "rkt image list --fields name --no-legend | grep "+BATS_ACI); err != nil {
		content, _ := dist.Asset("dist/bindata/aci-bats.aci")
		if err := ioutil.WriteFile("/tmp/aci-bats.aci", content, 0644); err != nil {
			log.Get().Panic(err)
		}
		utils.ExecCmd("rkt", "--insecure-skip-verify=true", "fetch", "/tmp/aci-bats.aci")
		os.Remove("/tmp/aci-bats.aci")
	}
}

func (cnt *Img) prepareTestAci() (*Img, error) {
	files, err := ioutil.ReadDir(cnt.path + PATH_TEST)
	if err != nil {
		return nil, err
	}

	utils.CopyDir(cnt.path+PATH_TEST+PATH_FILES, cnt.target+PATH_TEST+PATH_FILES)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_ATTRIBUTES, cnt.target+PATH_TEST+PATH_ATTRIBUTES)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_CONFD, cnt.target+PATH_TEST+PATH_CONFD)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_RUNLEVELS, cnt.target+PATH_TEST+PATH_RUNLEVELS)

	os.MkdirAll(cnt.target+PATH_TEST+PATH_FILES+PATH_TEST, 0777)
	for _, f := range files {
		if !f.IsDir() {
			if err := utils.CopyFile(cnt.path+PATH_TEST+"/"+f.Name(), cnt.target+PATH_TEST+PATH_FILES+PATH_TEST+"/"+f.Name()); err != nil {
				log.Get().Panic(err)
			}
		}
	}

	ExecScript := strings.Replace(TEST_INIT_SCRIPT, "%%COMMAND%%", "'"+strings.Join(cnt.manifest.Aci.App.Exec, "' '")+"'", 1)

	ioutil.WriteFile(cnt.target+PATH_TEST+PATH_FILES+"/init.sh", []byte(ExecScript), 0777)

	fullname, err := spec.NewACFullName(cnt.manifest.NameAndVersion.Name() + "_test:" + cnt.manifest.NameAndVersion.Version())
	if err != nil {
		log.Get().Panic(err)
	}

	resultMountName, _ := types.NewACName("result")
	testAci, err := NewAciWithManifest(cnt.target+PATH_TEST, cnt.args, spec.AciManifest{
		Aci: spec.AciDefinition{
			App: &spec.CntApp{
				Exec:        []string{"/init.sh"},
				MountPoints: []types.MountPoint{{Path: PATH_RESULT, Name: *resultMountName}},
			},
			Dependencies: []spec.ACFullname{BATS_ACI, cnt.manifest.NameAndVersion},
		},
		NameAndVersion: *fullname,
	})
	if err != nil {
		log.Get().Panic("Cannot build test aci", err)
	}
	return testAci, nil
}
<commit_msg>do not print grep for test aci<commit_after>package builder

import (
	"github.com/appc/spec/schema/types"
	"github.com/blablacar/cnt/dist"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/spec"
	"github.com/blablacar/cnt/utils"
	"io/ioutil"
	"os"
	"strings"
)

const BATS_ACI = "aci.blablacar.com/aci-bats:1"
const PATH_RESULT = "/result"
const TEST_INIT_SCRIPT = `#!/bin/bash
set -x

%%COMMAND%% &

waitsuccess="0"
if [ -f "/tests/wait.sh" ]; then
	chmod +x /tests/wait.sh
	i="0"
	while [ $i -lt 60 ]; do
	  /tests/wait.sh
	  if [ $? == 0 ]; then
	  	waitsuccess="1"
	  	break;
	  fi
	  i=$[$i+1]
	  sleep 1
	done
fi

if [ wait_success == "0" ]; then
	echo "1\n" > /result/wait.sh
	echo "WAIT FAILED"
	exit 1
fi

/test.sh
`

func (cnt *Img) Test() {
	cnt.Install()
	log.Get().Info("Testing " + cnt.manifest.NameAndVersion)
	cnt.importAciBats()

	testAci, err := cnt.prepareTestAci()
	if err != nil {
		log.Get().Panic(err)
	}
	testAci.Build()

	os.MkdirAll(cnt.target+PATH_TEST+PATH_TARGET+PATH_RESULT, 0777)

	if err := utils.ExecCmd("rkt",
		"--insecure-skip-verify=true",
		"run",
		"--local",
		"--volume=result,kind=host,source="+cnt.target+PATH_TEST+PATH_TARGET+PATH_RESULT,
		cnt.target+PATH_TEST+PATH_TARGET+"/image.aci"); err != nil {
		// rkt+systemd cannot exit with fail status yet
		log.Get().Panic(err)
	}

	cnt.checkResult()
}

func (cnt *Img) checkResult() {
	files, err := ioutil.ReadDir(cnt.target + PATH_TEST + PATH_TARGET + PATH_RESULT)
	if err != nil {
		log.Get().Panic("Cannot read test result directory", err)
	}
	for _, f := range files {
		content, err := ioutil.ReadFile(cnt.target + PATH_TEST + PATH_TARGET + PATH_RESULT + "/" + f.Name())
		if err != nil {
			log.Get().Panic("Cannot read result file", f.Name(), err)
		}
		if string(content) != "0\n" {
			log.Get().Error("Failed test file : ", f.Name())
			os.Exit(2)
		}
	}
}

func (cnt *Img) importAciBats() {
	if err := utils.ExecCmd("bash", "-c", "rkt image list --fields name --no-legend | grep -q "+BATS_ACI); err != nil {
		content, _ := dist.Asset("dist/bindata/aci-bats.aci")
		if err := ioutil.WriteFile("/tmp/aci-bats.aci", content, 0644); err != nil {
			log.Get().Panic(err)
		}
		utils.ExecCmd("rkt", "--insecure-skip-verify=true", "fetch", "/tmp/aci-bats.aci")
		os.Remove("/tmp/aci-bats.aci")
	}
}

func (cnt *Img) prepareTestAci() (*Img, error) {
	files, err := ioutil.ReadDir(cnt.path + PATH_TEST)
	if err != nil {
		return nil, err
	}

	utils.CopyDir(cnt.path+PATH_TEST+PATH_FILES, cnt.target+PATH_TEST+PATH_FILES)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_ATTRIBUTES, cnt.target+PATH_TEST+PATH_ATTRIBUTES)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_CONFD, cnt.target+PATH_TEST+PATH_CONFD)
	utils.CopyDir(cnt.path+PATH_TEST+PATH_RUNLEVELS, cnt.target+PATH_TEST+PATH_RUNLEVELS)

	os.MkdirAll(cnt.target+PATH_TEST+PATH_FILES+PATH_TEST, 0777)
	for _, f := range files {
		if !f.IsDir() {
			if err := utils.CopyFile(cnt.path+PATH_TEST+"/"+f.Name(), cnt.target+PATH_TEST+PATH_FILES+PATH_TEST+"/"+f.Name()); err != nil {
				log.Get().Panic(err)
			}
		}
	}

	ExecScript := strings.Replace(TEST_INIT_SCRIPT, "%%COMMAND%%", "'"+strings.Join(cnt.manifest.Aci.App.Exec, "' '")+"'", 1)

	ioutil.WriteFile(cnt.target+PATH_TEST+PATH_FILES+"/init.sh", []byte(ExecScript), 0777)

	fullname, err := spec.NewACFullName(cnt.manifest.NameAndVersion.Name() + "_test:" + cnt.manifest.NameAndVersion.Version())
	if err != nil {
		log.Get().Panic(err)
	}

	resultMountName, _ := types.NewACName("result")
	testAci, err := NewAciWithManifest(cnt.target+PATH_TEST, cnt.args, spec.AciManifest{
		Aci: spec.AciDefinition{
			App: &spec.CntApp{
				Exec:        []string{"/init.sh"},
				MountPoints: []types.MountPoint{{Path: PATH_RESULT, Name: *resultMountName}},
			},
			Dependencies: []spec.ACFullname{BATS_ACI, cnt.manifest.NameAndVersion},
		},
		NameAndVersion: *fullname,
	})
	if err != nil {
		log.Get().Panic("Cannot build test aci", err)
	}
	return testAci, nil
}
<|endoftext|>


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

package buildkite

import "fmt"

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string           `json:"id,omitempty"`
	URL         *string           `json:"url,omitempty"`
	Number      *int              `json:"number,omitempty"`
	State       *string           `json:"state,omitempty"`
	Message     *string           `json:"message,omitempty"`
	Commit      *string           `json:"commit,omitempty"`
	Branch      *string           `json:"branch,omitempty"`
	Env         map[string]string `json:"env,omitempty"`
	CreatedAt   *Timestamp        `json:"created_at,omitempty"`
	ScheduledAt *Timestamp        `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp        `json:"started_at,omitempty"`
	FinishedAt  *Timestamp        `json:"finished_at,omitempty"`
	MetaData    interface{}       `json:"meta_data,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the project this build is associated with
	Project *Project `json:"project,omitempty"`
}

// Job represents a job run during a build in buildkite
type Job struct {
	ID            *string    `json:"id,omitempty"`
	Type          *string    `json:"type,omitempty"`
	Name          *string    `json:"name,omitempty"`
	State         *string    `json:"state,omitempty"`
	LogsURL       *string    `json:"logs_url,omitempty"`
	RawLogsURL    *string    `json:"raw_log_url,omitempty"`
	Command       *string    `json:"command,omitempty"`
	ExitStatus    *int       `json:"exit_status,omitempty"`
	ArtifactPaths *string    `json:"artifact_paths,omitempty"`
	CreatedAt     *Timestamp `json:"created_at,omitempty"`
	ScheduledAt   *Timestamp `json:"scheduled_at,omitempty"`
	StartedAt     *Timestamp `json:"started_at,omitempty"`
	FinishedAt    *Timestamp `json:"finished_at,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State string `url:"state,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	ListOptions
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (as *BuildsService) Get(org string, project string, id string) (*Build, *Response, error) {

	u := fmt.Sprintf("v1/organizations/%s/projects/%s/builds/%s", org, project, id)

	req, err := as.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/organizations#list-organizations
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByProject lists the builds for a project within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-project
func (bs *BuildsService) ListByProject(org string, project string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/organizations/%s/projects/%s/builds", org, project)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}
<commit_msg>Add missing WebURL field to Build struct<commit_after>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import "fmt"

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string           `json:"id,omitempty"`
	URL         *string           `json:"url,omitempty"`
	WebURL      *string           `json:"web_url,omitempty"`
	Number      *int              `json:"number,omitempty"`
	State       *string           `json:"state,omitempty"`
	Message     *string           `json:"message,omitempty"`
	Commit      *string           `json:"commit,omitempty"`
	Branch      *string           `json:"branch,omitempty"`
	Env         map[string]string `json:"env,omitempty"`
	CreatedAt   *Timestamp        `json:"created_at,omitempty"`
	ScheduledAt *Timestamp        `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp        `json:"started_at,omitempty"`
	FinishedAt  *Timestamp        `json:"finished_at,omitempty"`
	MetaData    interface{}       `json:"meta_data,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the project this build is associated with
	Project *Project `json:"project,omitempty"`
}

// Job represents a job run during a build in buildkite
type Job struct {
	ID            *string    `json:"id,omitempty"`
	Type          *string    `json:"type,omitempty"`
	Name          *string    `json:"name,omitempty"`
	State         *string    `json:"state,omitempty"`
	LogsURL       *string    `json:"logs_url,omitempty"`
	RawLogsURL    *string    `json:"raw_log_url,omitempty"`
	Command       *string    `json:"command,omitempty"`
	ExitStatus    *int       `json:"exit_status,omitempty"`
	ArtifactPaths *string    `json:"artifact_paths,omitempty"`
	CreatedAt     *Timestamp `json:"created_at,omitempty"`
	ScheduledAt   *Timestamp `json:"scheduled_at,omitempty"`
	StartedAt     *Timestamp `json:"started_at,omitempty"`
	FinishedAt    *Timestamp `json:"finished_at,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State string `url:"state,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	ListOptions
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (as *BuildsService) Get(org string, project string, id string) (*Build, *Response, error) {

	u := fmt.Sprintf("v1/organizations/%s/projects/%s/builds/%s", org, project, id)

	req, err := as.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/organizations#list-organizations
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByProject lists the builds for a project within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-project
func (bs *BuildsService) ListByProject(org string, project string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v1/organizations/%s/projects/%s/builds", org, project)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}
<|endoftext|>


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

package log

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"

	"golang.org/x/sync/syncmap"
)

var (
	loggers []*Logger
	// GitLogger logger for git
	GitLogger *Logger
)

// NewLogger create a logger
func NewLogger(bufLen int64, mode, config string) {
	logger := newLogger(bufLen)

	isExist := false
	for i, l := range loggers {
		if l.adapter == mode {
			isExist = true
			loggers[i] = logger
		}
	}
	if !isExist {
		loggers = append(loggers, logger)
	}
	if err := logger.SetLogger(mode, config); err != nil {
		Fatal(2, "Failed to set logger (%s): %v", mode, err)
	}
}

// DelLogger removes loggers that are for the given mode
func DelLogger(mode string) error {
	for _, l := range loggers {
		if _, ok := l.outputs.Load(mode); ok {
			return l.DelLogger(mode)
		}
	}

	Trace("Log adapter %s not found, no need to delete", mode)
	return nil
}

// NewGitLogger create a logger for git
// FIXME: use same log level as other loggers.
func NewGitLogger(logPath string) {
	path := path.Dir(logPath)

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		Fatal(4, "Failed to create dir %s: %v", path, err)
	}

	GitLogger = newLogger(0)
	GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath))
}

// Trace records trace log
func Trace(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Trace(format, v...)
	}
}

// Debug records debug log
func Debug(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Debug(format, v...)
	}
}

// Info records info log
func Info(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Info(format, v...)
	}
}

// Warn records warning log
func Warn(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Warn(format, v...)
	}
}

// Error records error log
func Error(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Error(skip, format, v...)
	}
}

// Critical records critical log
func Critical(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Critical(skip, format, v...)
	}
}

// Fatal records error log and exit process
func Fatal(skip int, format string, v ...interface{}) {
	Error(skip, format, v...)
	for _, l := range loggers {
		l.Close()
	}
	os.Exit(1)
}

// Close closes all the loggers
func Close() {
	for _, l := range loggers {
		l.Close()
	}
}

// .___        __                 _____
// |   | _____/  |_  ____________/ ____\____    ____  ____
// |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
// |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
// |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
//          \/          \/                  \/     \/    \/

// LogLevel level type for log
//type LogLevel int

// log levels
const (
	TRACE = iota
	DEBUG
	INFO
	WARN
	ERROR
	CRITICAL
	FATAL
)

// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
	Init(config string) error
	WriteMsg(msg string, skip, level int) error
	Destroy()
	Flush()
}

type loggerType func() LoggerInterface

var adapters = make(map[string]loggerType)

// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
	if log == nil {
		panic("log: register provider is nil")
	}
	if _, dup := adapters[name]; dup {
		panic("log: register called twice for provider \"" + name + "\"")
	}
	adapters[name] = log
}

type logMsg struct {
	skip, level int
	msg         string
}

// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
	adapter string
	lock    sync.Mutex
	level   int
	msg     chan *logMsg
	outputs syncmap.Map
	quit    chan bool
}

// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
	l := &Logger{
		msg:  make(chan *logMsg, buffer),
		quit: make(chan bool),
	}
	go l.StartLogger()
	return l
}

// SetLogger sets new logger instance with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
	if log, ok := adapters[adapter]; ok {
		lg := log()
		if err := lg.Init(config); err != nil {
			return err
		}
		l.outputs.Store(adapter, lg)
		l.adapter = adapter
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
	if lg, ok := l.outputs.Load(adapter); ok {
		lg.(LoggerInterface).Destroy()
		l.outputs.Delete(adapter)
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

func (l *Logger) writerMsg(skip, level int, msg string) error {
	if l.level > level {
		return nil
	}
	lm := &logMsg{
		skip:  skip,
		level: level,
	}

	// Only error information needs locate position for debugging.
	if lm.level >= ERROR {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			fileName := file
			if len(fileName) > 20 {
				fileName = "..." + fileName[len(fileName)-20:]
			}
			lm.msg = fmt.Sprintf("[%s:%d %s] %s", fileName, line, fnName, msg)
		} else {
			lm.msg = msg
		}
	} else {
		lm.msg = msg
	}
	l.msg <- lm
	return nil
}

// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
	for {
		select {
		case bm := <-l.msg:
			l.outputs.Range(func(k, v interface{}) bool {
				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
				return true
			})
		case <-l.quit:
			return
		}
	}
}

// Flush flushes all chan data.
func (l *Logger) Flush() {
	l.outputs.Range(func(k, v interface{}) bool {
		v.(LoggerInterface).Flush()
		return true
	})
}

// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
	l.quit <- true
	for {
		if len(l.msg) > 0 {
			bm := <-l.msg
			l.outputs.Range(func(k, v interface{}) bool {
				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
				return true
			})
		} else {
			break
		}
	}
	l.outputs.Range(func(k, v interface{}) bool {
		v.(LoggerInterface).Flush()
		v.(LoggerInterface).Destroy()
		return true
	})
}

// Trace records trace log
func (l *Logger) Trace(format string, v ...interface{}) {
	msg := fmt.Sprintf("[T] "+format, v...)
	l.writerMsg(0, TRACE, msg)
}

// Debug records debug log
func (l *Logger) Debug(format string, v ...interface{}) {
	msg := fmt.Sprintf("[D] "+format, v...)
	l.writerMsg(0, DEBUG, msg)
}

// Info records information log
func (l *Logger) Info(format string, v ...interface{}) {
	msg := fmt.Sprintf("[I] "+format, v...)
	l.writerMsg(0, INFO, msg)
}

// Warn records warning log
func (l *Logger) Warn(format string, v ...interface{}) {
	msg := fmt.Sprintf("[W] "+format, v...)
	l.writerMsg(0, WARN, msg)
}

// Error records error log
func (l *Logger) Error(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[E] "+format, v...)
	l.writerMsg(skip, ERROR, msg)
}

// Critical records critical log
func (l *Logger) Critical(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[C] "+format, v...)
	l.writerMsg(skip, CRITICAL, msg)
}

// Fatal records error log and exit the process
func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[F] "+format, v...)
	l.writerMsg(skip, FATAL, msg)
	l.Close()
	os.Exit(1)
}
<commit_msg>Remove unused mutex field (#1440)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package log

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

	"golang.org/x/sync/syncmap"
)

var (
	loggers []*Logger
	// GitLogger logger for git
	GitLogger *Logger
)

// NewLogger create a logger
func NewLogger(bufLen int64, mode, config string) {
	logger := newLogger(bufLen)

	isExist := false
	for i, l := range loggers {
		if l.adapter == mode {
			isExist = true
			loggers[i] = logger
		}
	}
	if !isExist {
		loggers = append(loggers, logger)
	}
	if err := logger.SetLogger(mode, config); err != nil {
		Fatal(2, "Failed to set logger (%s): %v", mode, err)
	}
}

// DelLogger removes loggers that are for the given mode
func DelLogger(mode string) error {
	for _, l := range loggers {
		if _, ok := l.outputs.Load(mode); ok {
			return l.DelLogger(mode)
		}
	}

	Trace("Log adapter %s not found, no need to delete", mode)
	return nil
}

// NewGitLogger create a logger for git
// FIXME: use same log level as other loggers.
func NewGitLogger(logPath string) {
	path := path.Dir(logPath)

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		Fatal(4, "Failed to create dir %s: %v", path, err)
	}

	GitLogger = newLogger(0)
	GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath))
}

// Trace records trace log
func Trace(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Trace(format, v...)
	}
}

// Debug records debug log
func Debug(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Debug(format, v...)
	}
}

// Info records info log
func Info(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Info(format, v...)
	}
}

// Warn records warning log
func Warn(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Warn(format, v...)
	}
}

// Error records error log
func Error(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Error(skip, format, v...)
	}
}

// Critical records critical log
func Critical(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Critical(skip, format, v...)
	}
}

// Fatal records error log and exit process
func Fatal(skip int, format string, v ...interface{}) {
	Error(skip, format, v...)
	for _, l := range loggers {
		l.Close()
	}
	os.Exit(1)
}

// Close closes all the loggers
func Close() {
	for _, l := range loggers {
		l.Close()
	}
}

// .___        __                 _____
// |   | _____/  |_  ____________/ ____\____    ____  ____
// |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
// |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
// |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
//          \/          \/                  \/     \/    \/

// LogLevel level type for log
//type LogLevel int

// log levels
const (
	TRACE = iota
	DEBUG
	INFO
	WARN
	ERROR
	CRITICAL
	FATAL
)

// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
	Init(config string) error
	WriteMsg(msg string, skip, level int) error
	Destroy()
	Flush()
}

type loggerType func() LoggerInterface

var adapters = make(map[string]loggerType)

// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
	if log == nil {
		panic("log: register provider is nil")
	}
	if _, dup := adapters[name]; dup {
		panic("log: register called twice for provider \"" + name + "\"")
	}
	adapters[name] = log
}

type logMsg struct {
	skip, level int
	msg         string
}

// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
	adapter string
	level   int
	msg     chan *logMsg
	outputs syncmap.Map
	quit    chan bool
}

// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
	l := &Logger{
		msg:  make(chan *logMsg, buffer),
		quit: make(chan bool),
	}
	go l.StartLogger()
	return l
}

// SetLogger sets new logger instance with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
	if log, ok := adapters[adapter]; ok {
		lg := log()
		if err := lg.Init(config); err != nil {
			return err
		}
		l.outputs.Store(adapter, lg)
		l.adapter = adapter
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
	if lg, ok := l.outputs.Load(adapter); ok {
		lg.(LoggerInterface).Destroy()
		l.outputs.Delete(adapter)
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

func (l *Logger) writerMsg(skip, level int, msg string) error {
	if l.level > level {
		return nil
	}
	lm := &logMsg{
		skip:  skip,
		level: level,
	}

	// Only error information needs locate position for debugging.
	if lm.level >= ERROR {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			fileName := file
			if len(fileName) > 20 {
				fileName = "..." + fileName[len(fileName)-20:]
			}
			lm.msg = fmt.Sprintf("[%s:%d %s] %s", fileName, line, fnName, msg)
		} else {
			lm.msg = msg
		}
	} else {
		lm.msg = msg
	}
	l.msg <- lm
	return nil
}

// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
	for {
		select {
		case bm := <-l.msg:
			l.outputs.Range(func(k, v interface{}) bool {
				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
				return true
			})
		case <-l.quit:
			return
		}
	}
}

// Flush flushes all chan data.
func (l *Logger) Flush() {
	l.outputs.Range(func(k, v interface{}) bool {
		v.(LoggerInterface).Flush()
		return true
	})
}

// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
	l.quit <- true
	for {
		if len(l.msg) > 0 {
			bm := <-l.msg
			l.outputs.Range(func(k, v interface{}) bool {
				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
				return true
			})
		} else {
			break
		}
	}
	l.outputs.Range(func(k, v interface{}) bool {
		v.(LoggerInterface).Flush()
		v.(LoggerInterface).Destroy()
		return true
	})
}

// Trace records trace log
func (l *Logger) Trace(format string, v ...interface{}) {
	msg := fmt.Sprintf("[T] "+format, v...)
	l.writerMsg(0, TRACE, msg)
}

// Debug records debug log
func (l *Logger) Debug(format string, v ...interface{}) {
	msg := fmt.Sprintf("[D] "+format, v...)
	l.writerMsg(0, DEBUG, msg)
}

// Info records information log
func (l *Logger) Info(format string, v ...interface{}) {
	msg := fmt.Sprintf("[I] "+format, v...)
	l.writerMsg(0, INFO, msg)
}

// Warn records warning log
func (l *Logger) Warn(format string, v ...interface{}) {
	msg := fmt.Sprintf("[W] "+format, v...)
	l.writerMsg(0, WARN, msg)
}

// Error records error log
func (l *Logger) Error(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[E] "+format, v...)
	l.writerMsg(skip, ERROR, msg)
}

// Critical records critical log
func (l *Logger) Critical(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[C] "+format, v...)
	l.writerMsg(skip, CRITICAL, msg)
}

// Fatal records error log and exit the process
func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[F] "+format, v...)
	l.writerMsg(skip, FATAL, msg)
	l.Close()
	os.Exit(1)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package bootstrap_test

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"
	goyaml "gopkg.in/yaml.v1"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/bootstrap"
	"github.com/juju/juju/environs/storage"
	envtesting "github.com/juju/juju/environs/testing"
	"github.com/juju/juju/instance"
	coretesting "github.com/juju/juju/testing"
)

type StateSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&StateSuite{})

func (suite *StateSuite) newStorageWithDataDir(c *gc.C) (storage.Storage, string) {
	closer, stor, dataDir := envtesting.CreateLocalTestStorage(c)
	suite.AddCleanup(func(*gc.C) { closer.Close() })
	envtesting.UploadFakeTools(c, stor)
	return stor, dataDir
}

func (suite *StateSuite) newStorage(c *gc.C) storage.Storage {
	stor, _ := suite.newStorageWithDataDir(c)
	return stor
}

// testingHTTPSServer creates a tempdir backed https server with internal
// self-signed certs that will not be accepted as valid.
func (suite *StateSuite) testingHTTPSServer(c *gc.C) (string, string) {
	dataDir := c.MkDir()
	mux := http.NewServeMux()
	mux.Handle("/", http.FileServer(http.Dir(dataDir)))
	server := httptest.NewTLSServer(mux)
	suite.AddCleanup(func(*gc.C) { server.Close() })
	return server.URL, dataDir
}

func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) {
	stor := suite.newStorage(c)

	url, err := bootstrap.CreateStateFile(stor)
	c.Assert(err, gc.IsNil)

	reader, err := storage.Get(stor, bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	data, err := ioutil.ReadAll(reader)
	c.Assert(err, gc.IsNil)
	c.Check(string(data), gc.Equals, "")
	c.Assert(url, gc.NotNil)
	expectedURL, err := stor.URL(bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	c.Check(url, gc.Equals, expectedURL)
}

func (suite *StateSuite) TestDeleteStateFile(c *gc.C) {
	closer, stor, dataDir := envtesting.CreateLocalTestStorage(c)
	defer closer.Close()

	err := bootstrap.DeleteStateFile(stor)
	c.Assert(err, gc.IsNil) // doesn't exist, juju don't care

	_, err = bootstrap.CreateStateFile(stor)
	c.Assert(err, gc.IsNil)
	_, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile))
	c.Assert(err, gc.IsNil)

	err = bootstrap.DeleteStateFile(stor)
	c.Assert(err, gc.IsNil)
	_, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) {
	stor := suite.newStorage(c)
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	marshaledState, err := goyaml.Marshal(state)
	c.Assert(err, gc.IsNil)

	err = bootstrap.SaveState(stor, &state)
	c.Assert(err, gc.IsNil)

	loadedState, err := storage.Get(stor, bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	content, err := ioutil.ReadAll(loadedState)
	c.Assert(err, gc.IsNil)
	c.Check(content, gc.DeepEquals, marshaledState)
}

func (suite *StateSuite) setUpSavedState(c *gc.C, dataDir string) bootstrap.BootstrapState {
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	content, err := goyaml.Marshal(state)
	c.Assert(err, gc.IsNil)
	err = ioutil.WriteFile(filepath.Join(dataDir, bootstrap.StateFile), []byte(content), 0644)
	c.Assert(err, gc.IsNil)
	return state
}

func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) {
	storage, dataDir := suite.newStorageWithDataDir(c)
	state := suite.setUpSavedState(c, dataDir)
	storedState, err := bootstrap.LoadState(storage)
	c.Assert(err, gc.IsNil)
	c.Check(*storedState, gc.DeepEquals, state)
}

func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) {
	stor := suite.newStorage(c)
	_, err := bootstrap.LoadState(stor)
	c.Check(err, gc.Equals, environs.ErrNotBootstrapped)
}

func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) {
	storage := suite.newStorage(c)
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	err := bootstrap.SaveState(storage, &state)
	c.Assert(err, gc.IsNil)
	storedState, err := bootstrap.LoadState(storage)
	c.Assert(err, gc.IsNil)

	c.Check(*storedState, gc.DeepEquals, state)
}
<commit_msg>go fmt<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package bootstrap_test

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"

	jc "github.com/juju/testing/checkers"
	goyaml "gopkg.in/yaml.v1"
	gc "launchpad.net/gocheck"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/bootstrap"
	"github.com/juju/juju/environs/storage"
	envtesting "github.com/juju/juju/environs/testing"
	"github.com/juju/juju/instance"
	coretesting "github.com/juju/juju/testing"
)

type StateSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&StateSuite{})

func (suite *StateSuite) newStorageWithDataDir(c *gc.C) (storage.Storage, string) {
	closer, stor, dataDir := envtesting.CreateLocalTestStorage(c)
	suite.AddCleanup(func(*gc.C) { closer.Close() })
	envtesting.UploadFakeTools(c, stor)
	return stor, dataDir
}

func (suite *StateSuite) newStorage(c *gc.C) storage.Storage {
	stor, _ := suite.newStorageWithDataDir(c)
	return stor
}

// testingHTTPSServer creates a tempdir backed https server with internal
// self-signed certs that will not be accepted as valid.
func (suite *StateSuite) testingHTTPSServer(c *gc.C) (string, string) {
	dataDir := c.MkDir()
	mux := http.NewServeMux()
	mux.Handle("/", http.FileServer(http.Dir(dataDir)))
	server := httptest.NewTLSServer(mux)
	suite.AddCleanup(func(*gc.C) { server.Close() })
	return server.URL, dataDir
}

func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) {
	stor := suite.newStorage(c)

	url, err := bootstrap.CreateStateFile(stor)
	c.Assert(err, gc.IsNil)

	reader, err := storage.Get(stor, bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	data, err := ioutil.ReadAll(reader)
	c.Assert(err, gc.IsNil)
	c.Check(string(data), gc.Equals, "")
	c.Assert(url, gc.NotNil)
	expectedURL, err := stor.URL(bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	c.Check(url, gc.Equals, expectedURL)
}

func (suite *StateSuite) TestDeleteStateFile(c *gc.C) {
	closer, stor, dataDir := envtesting.CreateLocalTestStorage(c)
	defer closer.Close()

	err := bootstrap.DeleteStateFile(stor)
	c.Assert(err, gc.IsNil) // doesn't exist, juju don't care

	_, err = bootstrap.CreateStateFile(stor)
	c.Assert(err, gc.IsNil)
	_, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile))
	c.Assert(err, gc.IsNil)

	err = bootstrap.DeleteStateFile(stor)
	c.Assert(err, gc.IsNil)
	_, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) {
	stor := suite.newStorage(c)
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	marshaledState, err := goyaml.Marshal(state)
	c.Assert(err, gc.IsNil)

	err = bootstrap.SaveState(stor, &state)
	c.Assert(err, gc.IsNil)

	loadedState, err := storage.Get(stor, bootstrap.StateFile)
	c.Assert(err, gc.IsNil)
	content, err := ioutil.ReadAll(loadedState)
	c.Assert(err, gc.IsNil)
	c.Check(content, gc.DeepEquals, marshaledState)
}

func (suite *StateSuite) setUpSavedState(c *gc.C, dataDir string) bootstrap.BootstrapState {
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	content, err := goyaml.Marshal(state)
	c.Assert(err, gc.IsNil)
	err = ioutil.WriteFile(filepath.Join(dataDir, bootstrap.StateFile), []byte(content), 0644)
	c.Assert(err, gc.IsNil)
	return state
}

func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) {
	storage, dataDir := suite.newStorageWithDataDir(c)
	state := suite.setUpSavedState(c, dataDir)
	storedState, err := bootstrap.LoadState(storage)
	c.Assert(err, gc.IsNil)
	c.Check(*storedState, gc.DeepEquals, state)
}

func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) {
	stor := suite.newStorage(c)
	_, err := bootstrap.LoadState(stor)
	c.Check(err, gc.Equals, environs.ErrNotBootstrapped)
}

func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) {
	storage := suite.newStorage(c)
	state := bootstrap.BootstrapState{
		StateInstances: []instance.Id{instance.Id("an-instance-id")},
	}
	err := bootstrap.SaveState(storage, &state)
	c.Assert(err, gc.IsNil)
	storedState, err := bootstrap.LoadState(storage)
	c.Assert(err, gc.IsNil)

	c.Check(*storedState, gc.DeepEquals, state)
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 command

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

	v3 "github.com/coreos/etcd/clientv3"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	spb "github.com/coreos/etcd/storage/storagepb"
	"github.com/dustin/go-humanize"
	"github.com/olekukonko/tablewriter"
)

type printer interface {
	Del(v3.DeleteResponse)
	Get(v3.GetResponse)
	Put(v3.PutResponse)
	Txn(v3.TxnResponse)
	Watch(v3.WatchResponse)

	MemberList(v3.MemberListResponse)

	MemberStatus([]statusInfo)

	Alarm(v3.AlarmResponse)
	DBStatus(dbstatus)
}

func NewPrinter(printerType string, isHex bool) printer {
	switch printerType {
	case "simple":
		return &simplePrinter{isHex: isHex}
	case "json":
		return &jsonPrinter{}
	case "protobuf":
		return &pbPrinter{}
	}
	return nil
}

type simplePrinter struct {
	isHex bool
}

func (s *simplePrinter) Del(resp v3.DeleteResponse) {
	fmt.Println(resp.Deleted)
}

func (s *simplePrinter) Get(resp v3.GetResponse) {
	for _, kv := range resp.Kvs {
		printKV(s.isHex, kv)
	}
}

func (s *simplePrinter) Put(r v3.PutResponse) { fmt.Println("OK") }

func (s *simplePrinter) Txn(resp v3.TxnResponse) {
	if resp.Succeeded {
		fmt.Println("SUCCESS")
	} else {
		fmt.Println("FAILURE")
	}

	for _, r := range resp.Responses {
		fmt.Println("")
		switch v := r.Response.(type) {
		case *pb.ResponseUnion_ResponseDeleteRange:
			s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
		case *pb.ResponseUnion_ResponsePut:
			s.Put((v3.PutResponse)(*v.ResponsePut))
		case *pb.ResponseUnion_ResponseRange:
			s.Get(((v3.GetResponse)(*v.ResponseRange)))
		default:
			fmt.Printf("unexpected response %+v\n", r)
		}
	}
}

func (s *simplePrinter) Watch(resp v3.WatchResponse) {
	for _, e := range resp.Events {
		fmt.Println(e.Type)
		printKV(s.isHex, e.Kv)
	}
}

func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
	for _, e := range resp.Alarms {
		fmt.Printf("%+v\n", e)
	}
}

func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs", "Is Leader"})

	for _, m := range resp.Members {
		status := "started"
		if len(m.Name) == 0 {
			status = "unstarted"
		}

		table.Append([]string{
			fmt.Sprintf("%x", m.ID),
			status,
			m.Name,
			strings.Join(m.PeerURLs, ","),
			strings.Join(m.ClientURLs, ","),
			fmt.Sprint(m.IsLeader),
		})
	}

	table.Render()
}

func (s *simplePrinter) MemberStatus(statusList []statusInfo) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"endpoint", "ID", "version"})

	for _, status := range statusList {
		table.Append([]string{
			fmt.Sprint(status.ep),
			fmt.Sprintf("%x", status.resp.Header.MemberId),
			fmt.Sprint(status.resp.Version),
		})
	}

	table.Render()
}

func (s *simplePrinter) DBStatus(ds dbstatus) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"hash", "revision", "total keys", "total size"})

	table.Append([]string{
		fmt.Sprintf("%x", ds.hash),
		fmt.Sprint(ds.revision),
		fmt.Sprint(ds.totalKey),
		humanize.Bytes(uint64(ds.totalSize)),
	})

	table.Render()
}

type jsonPrinter struct{}

func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
func (p *jsonPrinter) Get(r v3.GetResponse) {
	for _, kv := range r.Kvs {
		printJSON(kv)
	}
}
func (p *jsonPrinter) Put(r v3.PutResponse)               { printJSON(r) }
func (p *jsonPrinter) Txn(r v3.TxnResponse)               { printJSON(r) }
func (p *jsonPrinter) Watch(r v3.WatchResponse)           { printJSON(r) }
func (p *jsonPrinter) Alarm(r v3.AlarmResponse)           { printJSON(r) }
func (p *jsonPrinter) MemberList(r v3.MemberListResponse) { printJSON(r) }
func (p *jsonPrinter) MemberStatus(r []statusInfo)        { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus)                { printJSON(r) }

func printJSON(v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	fmt.Println(string(b))
}

type pbPrinter struct{}

type pbMarshal interface {
	Marshal() ([]byte, error)
}

func (p *pbPrinter) Del(r v3.DeleteResponse) {
	printPB((*pb.DeleteRangeResponse)(&r))
}

func (p *pbPrinter) Get(r v3.GetResponse) {
	printPB((*pb.RangeResponse)(&r))
}

func (p *pbPrinter) Put(r v3.PutResponse) {
	printPB((*pb.PutResponse)(&r))
}

func (p *pbPrinter) Txn(r v3.TxnResponse) {
	printPB((*pb.TxnResponse)(&r))
}

func (p *pbPrinter) Watch(r v3.WatchResponse) {
	for _, ev := range r.Events {
		printPB((*spb.Event)(ev))
	}
}

func (p *pbPrinter) Alarm(r v3.AlarmResponse) {
	printPB((*pb.AlarmResponse)(&r))
}

func (pb *pbPrinter) MemberList(r v3.MemberListResponse) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func (pb *pbPrinter) MemberStatus(r []statusInfo) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func (pb *pbPrinter) DBStatus(r dbstatus) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func printPB(m pbMarshal) {
	b, err := m.Marshal()
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	fmt.Printf(string(b))
}
<commit_msg>etcdctlv3: print db size and raft status in 'etcdctl status' command<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 command

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

	v3 "github.com/coreos/etcd/clientv3"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	spb "github.com/coreos/etcd/storage/storagepb"
	"github.com/dustin/go-humanize"
	"github.com/olekukonko/tablewriter"
)

type printer interface {
	Del(v3.DeleteResponse)
	Get(v3.GetResponse)
	Put(v3.PutResponse)
	Txn(v3.TxnResponse)
	Watch(v3.WatchResponse)

	MemberList(v3.MemberListResponse)

	MemberStatus([]statusInfo)

	Alarm(v3.AlarmResponse)
	DBStatus(dbstatus)
}

func NewPrinter(printerType string, isHex bool) printer {
	switch printerType {
	case "simple":
		return &simplePrinter{isHex: isHex}
	case "json":
		return &jsonPrinter{}
	case "protobuf":
		return &pbPrinter{}
	}
	return nil
}

type simplePrinter struct {
	isHex bool
}

func (s *simplePrinter) Del(resp v3.DeleteResponse) {
	fmt.Println(resp.Deleted)
}

func (s *simplePrinter) Get(resp v3.GetResponse) {
	for _, kv := range resp.Kvs {
		printKV(s.isHex, kv)
	}
}

func (s *simplePrinter) Put(r v3.PutResponse) { fmt.Println("OK") }

func (s *simplePrinter) Txn(resp v3.TxnResponse) {
	if resp.Succeeded {
		fmt.Println("SUCCESS")
	} else {
		fmt.Println("FAILURE")
	}

	for _, r := range resp.Responses {
		fmt.Println("")
		switch v := r.Response.(type) {
		case *pb.ResponseUnion_ResponseDeleteRange:
			s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
		case *pb.ResponseUnion_ResponsePut:
			s.Put((v3.PutResponse)(*v.ResponsePut))
		case *pb.ResponseUnion_ResponseRange:
			s.Get(((v3.GetResponse)(*v.ResponseRange)))
		default:
			fmt.Printf("unexpected response %+v\n", r)
		}
	}
}

func (s *simplePrinter) Watch(resp v3.WatchResponse) {
	for _, e := range resp.Events {
		fmt.Println(e.Type)
		printKV(s.isHex, e.Kv)
	}
}

func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
	for _, e := range resp.Alarms {
		fmt.Printf("%+v\n", e)
	}
}

func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs", "Is Leader"})

	for _, m := range resp.Members {
		status := "started"
		if len(m.Name) == 0 {
			status = "unstarted"
		}

		table.Append([]string{
			fmt.Sprintf("%x", m.ID),
			status,
			m.Name,
			strings.Join(m.PeerURLs, ","),
			strings.Join(m.ClientURLs, ","),
			fmt.Sprint(m.IsLeader),
		})
	}

	table.Render()
}

func (s *simplePrinter) MemberStatus(statusList []statusInfo) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index"})

	for _, status := range statusList {
		table.Append([]string{
			fmt.Sprint(status.ep),
			fmt.Sprintf("%x", status.resp.Header.MemberId),
			fmt.Sprint(status.resp.Version),
			fmt.Sprint(humanize.Bytes(uint64(status.resp.DbSize))),
			fmt.Sprint(status.resp.Leader == status.resp.Header.MemberId),
			fmt.Sprint(status.resp.RaftTerm),
			fmt.Sprint(status.resp.RaftIndex),
		})
	}

	table.Render()
}

func (s *simplePrinter) DBStatus(ds dbstatus) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"hash", "revision", "total keys", "total size"})

	table.Append([]string{
		fmt.Sprintf("%x", ds.hash),
		fmt.Sprint(ds.revision),
		fmt.Sprint(ds.totalKey),
		humanize.Bytes(uint64(ds.totalSize)),
	})

	table.Render()
}

type jsonPrinter struct{}

func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
func (p *jsonPrinter) Get(r v3.GetResponse) {
	for _, kv := range r.Kvs {
		printJSON(kv)
	}
}
func (p *jsonPrinter) Put(r v3.PutResponse)               { printJSON(r) }
func (p *jsonPrinter) Txn(r v3.TxnResponse)               { printJSON(r) }
func (p *jsonPrinter) Watch(r v3.WatchResponse)           { printJSON(r) }
func (p *jsonPrinter) Alarm(r v3.AlarmResponse)           { printJSON(r) }
func (p *jsonPrinter) MemberList(r v3.MemberListResponse) { printJSON(r) }
func (p *jsonPrinter) MemberStatus(r []statusInfo)        { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus)                { printJSON(r) }

func printJSON(v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	fmt.Println(string(b))
}

type pbPrinter struct{}

type pbMarshal interface {
	Marshal() ([]byte, error)
}

func (p *pbPrinter) Del(r v3.DeleteResponse) {
	printPB((*pb.DeleteRangeResponse)(&r))
}

func (p *pbPrinter) Get(r v3.GetResponse) {
	printPB((*pb.RangeResponse)(&r))
}

func (p *pbPrinter) Put(r v3.PutResponse) {
	printPB((*pb.PutResponse)(&r))
}

func (p *pbPrinter) Txn(r v3.TxnResponse) {
	printPB((*pb.TxnResponse)(&r))
}

func (p *pbPrinter) Watch(r v3.WatchResponse) {
	for _, ev := range r.Events {
		printPB((*spb.Event)(ev))
	}
}

func (p *pbPrinter) Alarm(r v3.AlarmResponse) {
	printPB((*pb.AlarmResponse)(&r))
}

func (pb *pbPrinter) MemberList(r v3.MemberListResponse) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func (pb *pbPrinter) MemberStatus(r []statusInfo) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func (pb *pbPrinter) DBStatus(r dbstatus) {
	ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}

func printPB(m pbMarshal) {
	b, err := m.Marshal()
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	fmt.Printf(string(b))
}
<|endoftext|>


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

// +build windows

package schannel

import (
	"syscall"
)

const (
	__SCHANNEL_CRED_VERSION = 4

	_SP_PROT_PCT1_SERVER      = 0x00000001
	_SP_PROT_PCT1_CLIENT      = 0x00000002
	_SP_PROT_PCT1             = _SP_PROT_PCT1_SERVER | _SP_PROT_PCT1_CLIENT
	_SP_PROT_SSL2_SERVER      = 0x00000004
	_SP_PROT_SSL2_CLIENT      = 0x00000008
	_SP_PROT_SSL2             = _SP_PROT_SSL2_SERVER | _SP_PROT_SSL2_CLIENT
	_SP_PROT_SSL3_SERVER      = 0x00000010
	_SP_PROT_SSL3_CLIENT      = 0x00000020
	_SP_PROT_SSL3             = _SP_PROT_SSL3_SERVER | _SP_PROT_SSL3_CLIENT
	_SP_PROT_TLS1_SERVER      = 0x00000040
	_SP_PROT_TLS1_CLIENT      = 0x00000080
	_SP_PROT_TLS1             = _SP_PROT_TLS1_SERVER | _SP_PROT_TLS1_CLIENT
	_SP_PROT_SSL3TLS1_CLIENTS = _SP_PROT_TLS1_CLIENT | _SP_PROT_SSL3_CLIENT
	_SP_PROT_SSL3TLS1_SERVERS = _SP_PROT_TLS1_SERVER | _SP_PROT_SSL3_SERVER
	_SP_PROT_SSL3TLS1         = _SP_PROT_SSL3 | _SP_PROT_TLS1
)

type __SCHANNEL_CRED struct {
	Version               uint32
	CredCount             uint32
	Creds                 *syscall.CertContext
	RootStore             syscall.Handle // TODO: make sure this field is syscall.Handle
	cMappers              uint32
	aphMappers            uintptr
	SupportedAlgCount     uint32
	SupportedAlgs         *uint32
	EnabledProtocols      uint32
	MinimumCipherStrength uint32
	MaximumCipherStrength uint32
	SessionLifespan       uint32
	Flags                 uint32
	CredFormat            uint32
}

const (
	_SECPKG_ATTR_SIZES            = 0
	_SECPKG_ATTR_NAMES            = 1
	_SECPKG_ATTR_LIFESPAN         = 2
	_SECPKG_ATTR_DCE_INFO         = 3
	_SECPKG_ATTR_STREAM_SIZES     = 4
	_SECPKG_ATTR_KEY_INFO         = 5
	_SECPKG_ATTR_AUTHORITY        = 6
	_SECPKG_ATTR_PROTO_INFO       = 7
	_SECPKG_ATTR_PASSWORD_EXPIRY  = 8
	_SECPKG_ATTR_SESSION_KEY      = 9
	_SECPKG_ATTR_PACKAGE_INFO     = 10
	_SECPKG_ATTR_USER_FLAGS       = 11
	_SECPKG_ATTR_NEGOTIATION_INFO = 12
	_SECPKG_ATTR_NATIVE_NAMES     = 13
	_SECPKG_ATTR_FLAGS            = 14

	_SCHANNEL_RENEGOTIATE = 0
	_SCHANNEL_SHUTDOWN    = 1
	_SCHANNEL_ALERT       = 2
)

type _SecPkgContext_StreamSizes struct {
	Header         uint32
	Trailer        uint32
	MaximumMessage uint32
	Buffers        uint32
	BlockSize      uint32
}

type _SecPkgContext_ProtoInfo struct {
	ProtocolName *uint16
	MajorVersion uint32
	MinorVersion uint32
}

type _SecPkgContext_Names struct {
	UserName *uint16
}

type _SecPkgContext_Authority struct {
	AuthorityName *uint16
}

type _SecPkgContext_KeyInfo struct {
	SignatureAlgorithmName *uint16
	EncryptAlgorithmName   *uint16
	KeySize                uint32
	SignatureAlgorithm     uint32
	EncryptAlgorithm       uint32
}

// TODO: SecPkgContext_ConnectionInfo

// TODO: SECPKG_ATTR_REMOTE_CERT_CONTEXT
// TODO: SECPKG_ATTR_LOCAL_CERT_CONTEXT

// TODO: SecPkgContext_IssuerListInfoEx
<commit_msg>sspi/schannel: add TODO<commit_after>// Copyright 2015 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.

// +build windows

package schannel

import (
	"syscall"
)

// TODO: maybe put all these into a separate package, like sspi/schannel/winapi or similar

const (
	__SCHANNEL_CRED_VERSION = 4

	_SP_PROT_PCT1_SERVER      = 0x00000001
	_SP_PROT_PCT1_CLIENT      = 0x00000002
	_SP_PROT_PCT1             = _SP_PROT_PCT1_SERVER | _SP_PROT_PCT1_CLIENT
	_SP_PROT_SSL2_SERVER      = 0x00000004
	_SP_PROT_SSL2_CLIENT      = 0x00000008
	_SP_PROT_SSL2             = _SP_PROT_SSL2_SERVER | _SP_PROT_SSL2_CLIENT
	_SP_PROT_SSL3_SERVER      = 0x00000010
	_SP_PROT_SSL3_CLIENT      = 0x00000020
	_SP_PROT_SSL3             = _SP_PROT_SSL3_SERVER | _SP_PROT_SSL3_CLIENT
	_SP_PROT_TLS1_SERVER      = 0x00000040
	_SP_PROT_TLS1_CLIENT      = 0x00000080
	_SP_PROT_TLS1             = _SP_PROT_TLS1_SERVER | _SP_PROT_TLS1_CLIENT
	_SP_PROT_SSL3TLS1_CLIENTS = _SP_PROT_TLS1_CLIENT | _SP_PROT_SSL3_CLIENT
	_SP_PROT_SSL3TLS1_SERVERS = _SP_PROT_TLS1_SERVER | _SP_PROT_SSL3_SERVER
	_SP_PROT_SSL3TLS1         = _SP_PROT_SSL3 | _SP_PROT_TLS1
)

type __SCHANNEL_CRED struct {
	Version               uint32
	CredCount             uint32
	Creds                 *syscall.CertContext
	RootStore             syscall.Handle // TODO: make sure this field is syscall.Handle
	cMappers              uint32
	aphMappers            uintptr
	SupportedAlgCount     uint32
	SupportedAlgs         *uint32
	EnabledProtocols      uint32
	MinimumCipherStrength uint32
	MaximumCipherStrength uint32
	SessionLifespan       uint32
	Flags                 uint32
	CredFormat            uint32
}

const (
	_SECPKG_ATTR_SIZES            = 0
	_SECPKG_ATTR_NAMES            = 1
	_SECPKG_ATTR_LIFESPAN         = 2
	_SECPKG_ATTR_DCE_INFO         = 3
	_SECPKG_ATTR_STREAM_SIZES     = 4
	_SECPKG_ATTR_KEY_INFO         = 5
	_SECPKG_ATTR_AUTHORITY        = 6
	_SECPKG_ATTR_PROTO_INFO       = 7
	_SECPKG_ATTR_PASSWORD_EXPIRY  = 8
	_SECPKG_ATTR_SESSION_KEY      = 9
	_SECPKG_ATTR_PACKAGE_INFO     = 10
	_SECPKG_ATTR_USER_FLAGS       = 11
	_SECPKG_ATTR_NEGOTIATION_INFO = 12
	_SECPKG_ATTR_NATIVE_NAMES     = 13
	_SECPKG_ATTR_FLAGS            = 14

	_SCHANNEL_RENEGOTIATE = 0
	_SCHANNEL_SHUTDOWN    = 1
	_SCHANNEL_ALERT       = 2
)

type _SecPkgContext_StreamSizes struct {
	Header         uint32
	Trailer        uint32
	MaximumMessage uint32
	Buffers        uint32
	BlockSize      uint32
}

type _SecPkgContext_ProtoInfo struct {
	ProtocolName *uint16
	MajorVersion uint32
	MinorVersion uint32
}

type _SecPkgContext_Names struct {
	UserName *uint16
}

type _SecPkgContext_Authority struct {
	AuthorityName *uint16
}

type _SecPkgContext_KeyInfo struct {
	SignatureAlgorithmName *uint16
	EncryptAlgorithmName   *uint16
	KeySize                uint32
	SignatureAlgorithm     uint32
	EncryptAlgorithm       uint32
}

// TODO: SecPkgContext_ConnectionInfo

// TODO: SECPKG_ATTR_REMOTE_CERT_CONTEXT
// TODO: SECPKG_ATTR_LOCAL_CERT_CONTEXT

// TODO: SecPkgContext_IssuerListInfoEx
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 搜索目录管理.
// 可以添加搜索目录，按照添加的优先级进行文件检索，并在内部进行高效缓存处理。
package gspath

import (
    "sync"
    "errors"
    "strings"
    "gitee.com/johng/gf/g/os/gfile"
    "gitee.com/johng/gf/g/container/gmap"
)

// 文件目录搜索管理对象
type SPath struct {
    mu    sync.RWMutex
    paths []string              // 搜索路径，按照优先级进行排序
    cache *gmap.StringStringMap // 搜索结果缓存map
}

func New () *SPath {
    return &SPath{
        paths : make([]string, 0),
        cache : gmap.NewStringStringMap(),
    }
}

// 设置搜索路径，只保留当前设置项，其他搜索路径被清空
func (sp *SPath) Set(path string) error {
    r := gfile.RealPath(path)
    if r != "" && gfile.IsDir(r) {
        sp.mu.Lock()
        sp.paths = []string{strings.TrimRight(r, gfile.Separator)}
        sp.mu.Unlock()
        sp.cache.Clear()
        return nil
    }
    return errors.New("invalid path:" + path)
}

// 添加搜索路径
func (sp *SPath) Add(path string) error {
    r := gfile.RealPath(path)
    if r != "" && gfile.IsDir(r) {
        sp.mu.Lock()
        sp.paths = append(sp.paths, r)
        sp.mu.Unlock()
        return nil
    }
    return errors.New("invalid path:" + path)
}

// 按照优先级搜索文件，返回搜索到的文件绝对路径
func (sp *SPath) Search(name string) string {
    path := sp.cache.Get(name)
    if path == "" {
        sp.mu.RLock()
        for _, v := range sp.paths {
            path = v + gfile.Separator + name
            if gfile.Exists(path) {
                break
            }
        }
        sp.mu.RUnlock()
        if path != "" {
            sp.cache.Set(name, path)
        }
    }
    return path
}<commit_msg>修复在windows下文件参数中不同文件分隔符引起的热更新机制失效问题。<commit_after>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 搜索目录管理.
// 可以添加搜索目录，按照添加的优先级进行文件检索，并在内部进行高效缓存处理。
package gspath

import (
    "sync"
    "errors"
    "strings"
    "gitee.com/johng/gf/g/os/gfile"
    "gitee.com/johng/gf/g/container/gmap"
)

// 文件目录搜索管理对象
type SPath struct {
    mu    sync.RWMutex
    paths []string              // 搜索路径，按照优先级进行排序
    cache *gmap.StringStringMap // 搜索结果缓存map
}

func New () *SPath {
    return &SPath{
        paths : make([]string, 0),
        cache : gmap.NewStringStringMap(),
    }
}

// 设置搜索路径，只保留当前设置项，其他搜索路径被清空
func (sp *SPath) Set(path string) error {
    r := gfile.RealPath(path)
    if r != "" && gfile.IsDir(r) {
        sp.mu.Lock()
        sp.paths = []string{strings.TrimRight(r, gfile.Separator)}
        sp.mu.Unlock()
        sp.cache.Clear()
        return nil
    }
    return errors.New("invalid path:" + path)
}

// 添加搜索路径
func (sp *SPath) Add(path string) error {
    r := gfile.RealPath(path)
    if r != "" && gfile.IsDir(r) {
        sp.mu.Lock()
        sp.paths = append(sp.paths, r)
        sp.mu.Unlock()
        return nil
    }
    return errors.New("invalid path:" + path)
}

// 按照优先级搜索文件，返回搜索到的文件绝对路径
func (sp *SPath) Search(name string) string {
    path := sp.cache.Get(name)
    if path == "" {
        sp.mu.RLock()
        for _, v := range sp.paths {
            path = gfile.RealPath(v + gfile.Separator + name)
            if path != "" && gfile.Exists(path) {
                break
            }
        }
        sp.mu.RUnlock()
        if path != "" {
            sp.cache.Set(name, path)
        }
    }
    return path
}<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 类型转换.
// 内部使用了bytes作为底层转换类型，效率很高。
package gconv

import (
    "fmt"
    "time"
    "strconv"
    "encoding/json"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/util/gstr"
    "gitee.com/johng/gf/g/encoding/gbinary"
    "github.com/fatih/structs"
    "strings"
    "reflect"
)

// 将变量i转换为字符串指定的类型t
func Convert(i interface{}, t string, params...interface{}) interface{} {
    switch t {
        case "int":             return Int(i)
        case "int8":            return Int8(i)
        case "int16":           return Int16(i)
        case "int32":           return Int32(i)
        case "int64":           return Int64(i)
        case "uint":            return Uint(i)
        case "uint8":           return Uint8(i)
        case "uint16":          return Uint16(i)
        case "uint32":          return Uint32(i)
        case "uint64":          return Uint64(i)
        case "float32":         return Float32(i)
        case "float64":         return Float64(i)
        case "bool":            return Bool(i)
        case "string":          return String(i)
        case "[]byte":          return Bytes(i)
        case "time.Time":
            if len(params) > 0 {
                return Time(i, String(params[0]))
            }
            return Time(i)

        case "time.Duration":   return TimeDuration(i)
        default:
            return i
    }
}

// 将变量i转换为time.Time类型
func Time(i interface{}, format...string) time.Time {
    s := String(i)
    // 优先使用用户输入日期格式进行转换
    if len(format) > 0 {
        t, _ := gtime.StrToTimeFormat(s, format[0])
        return t
    }
    if gstr.IsNumeric(s) {
        return gtime.NewFromTimeStamp(Int64(s)).Time
    } else {
        t, _ := gtime.StrToTime(s)
        return t
    }
}

// 将变量i转换为time.Time类型
func TimeDuration(i interface{}) time.Duration {
    return time.Duration(Int64(i))
}

func Bytes(i interface{}) []byte {
    if i == nil {
        return nil
    }
    if r, ok := i.([]byte); ok {
        return r
    } else {
        return gbinary.Encode(i)
    }
}

// 基础的字符串类型转换
func String(i interface{}) string {
    if i == nil {
        return ""
    }
    switch value := i.(type) {
        case int:     return strconv.Itoa(value)
        case int8:    return strconv.Itoa(int(value))
        case int16:   return strconv.Itoa(int(value))
        case int32:   return strconv.Itoa(int(value))
        case int64:   return strconv.Itoa(int(value))
        case uint:    return strconv.FormatUint(uint64(value), 10)
        case uint8:   return strconv.FormatUint(uint64(value), 10)
        case uint16:  return strconv.FormatUint(uint64(value), 10)
        case uint32:  return strconv.FormatUint(uint64(value), 10)
        case uint64:  return strconv.FormatUint(uint64(value), 10)
        case float32: return strconv.FormatFloat(float64(value), 'f', -1, 32)
        case float64: return strconv.FormatFloat(value, 'f', -1, 64)
        case bool:    return strconv.FormatBool(value)
        case string:  return value
        case []byte:  return string(value)
        default:
            // 默认使用json进行字符串转换
            jsonContent, _ := json.Marshal(value)
            return string(jsonContent)
    }
}

func Strings(i interface{}) []string {
    if i == nil {
        return nil
    }
    if r, ok := i.([]string); ok {
        return r
    } else if r, ok := i.([]interface{}); ok {
        strs := make([]string, len(r))
        for k, v := range r {
            strs[k] = String(v)
        }
        return strs
    }
    return []string{fmt.Sprintf("%v", i)}
}

//false: "", 0, false, off
func Bool(i interface{}) bool {
    if i == nil {
        return false
    }
    if v, ok := i.(bool); ok {
        return v
    }
    if s := String(i); s != "" && s != "0" && s != "false" && s != "off" {
        return true
    }
    return false
}

func Int(i interface{}) int {
    if i == nil {
        return 0
    }
    switch value := i.(type) {
        case int:     return value
        case int8:    return int(value)
        case int16:   return int(value)
        case int32:   return int(value)
        case int64:   return int(value)
        case uint:    return int(value)
        case uint8:   return int(value)
        case uint16:  return int(value)
        case uint32:  return int(value)
        case uint64:  return int(value)
        case float32: return int(value)
        case float64: return int(value)
        case bool:
            if value {
                return 1
            }
            return 0
        default:
            v, _ := strconv.Atoi(String(value))
            return v
    }
}

func Int8(i interface{}) int8 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int8); ok {
        return v
    }
    return int8(Int(i))
}

func Int16(i interface{}) int16 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int16); ok {
        return v
    }
    return int16(Int(i))
}

func Int32(i interface{}) int32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int32); ok {
        return v
    }
    return int32(Int(i))
}

func Int64(i interface{}) int64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int64); ok {
        return v
    }
    return int64(Int(i))
}

func Uint(i interface{}) uint {
    if i == nil {
        return 0
    }
    switch value := i.(type) {
        case int:     return uint(value)
        case int8:    return uint(value)
        case int16:   return uint(value)
        case int32:   return uint(value)
        case int64:   return uint(value)
        case uint:    return value
        case uint8:   return uint(value)
        case uint16:  return uint(value)
        case uint32:  return uint(value)
        case uint64:  return uint(value)
        case float32: return uint(value)
        case float64: return uint(value)
        case bool:
            if value {
                return 1
            }
            return 0
        default:
            v, _ := strconv.ParseUint(String(value), 10, 64)
            return uint(v)
    }
}

func Uint8(i interface{}) uint8 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint8); ok {
        return v
    }
    return uint8(Uint(i))
}

func Uint16(i interface{}) uint16 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint16); ok {
        return v
    }
    return uint16(Uint(i))
}

func Uint32(i interface{}) uint32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint32); ok {
        return v
    }
    return uint32(Uint(i))
}

func Uint64(i interface{}) uint64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint64); ok {
        return v
    }
    return uint64(Uint(i))
}

func Float32 (i interface{}) float32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(float32); ok {
        return v
    }
    v, _ := strconv.ParseFloat(String(i), 64)
    return float32(v)
}

func Float64 (i interface{}) float64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(float64); ok {
        return v
    }
    v, _ := strconv.ParseFloat(String(i), 64)
    return v
}

// 将map键值对映射到对应的struct对象属性上，需要注意：
// 1、第二个参数为struct对象指针；
// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值；
// 3、map中的键名可以为小写，映射转换时会自动将键名首字母转为大写做匹配映射，如果无法匹配则忽略；
func MapToStruct(params map[string]interface{}, object interface{}) error {
    tagmap := make(map[string]string)
    fields := structs.Fields(object)
    // 将struct中定义的属性转换名称构建称tagmap
    for _, field := range fields {
        if tag := field.Tag("gconv"); tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagmap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    elem := reflect.ValueOf(object).Elem()
    // 首先匹配对象定义时绑定的属性名称
    for tagk, tagv := range tagmap {
        if v, ok := params[tagk]; ok {
            bindVarToStruct(elem, tagv, v)
        }
    }
    // 其次按照默认规则进行匹配
    for mapk, mapv := range params {
        bindVarToStruct(elem, gstr.UcFirst(mapk), mapv)
    }
    return nil
}

// 将参数值绑定到对象指定名称的属性上
func bindVarToStruct(elem reflect.Value, name string, value interface{}) {
    structFieldValue := elem.FieldByName(name)
    // 键名与对象属性匹配检测
    if !structFieldValue.IsValid() {
        return
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
}
<commit_msg>gconv.MapToStruct方法增加自定义参数属性映射关系参数，非必需<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 类型转换.
// 内部使用了bytes作为底层转换类型，效率很高。
package gconv

import (
    "fmt"
    "time"
    "strconv"
    "encoding/json"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/util/gstr"
    "gitee.com/johng/gf/g/encoding/gbinary"
    "github.com/fatih/structs"
    "strings"
    "reflect"
)

// 将变量i转换为字符串指定的类型t
func Convert(i interface{}, t string, params...interface{}) interface{} {
    switch t {
        case "int":             return Int(i)
        case "int8":            return Int8(i)
        case "int16":           return Int16(i)
        case "int32":           return Int32(i)
        case "int64":           return Int64(i)
        case "uint":            return Uint(i)
        case "uint8":           return Uint8(i)
        case "uint16":          return Uint16(i)
        case "uint32":          return Uint32(i)
        case "uint64":          return Uint64(i)
        case "float32":         return Float32(i)
        case "float64":         return Float64(i)
        case "bool":            return Bool(i)
        case "string":          return String(i)
        case "[]byte":          return Bytes(i)
        case "time.Time":
            if len(params) > 0 {
                return Time(i, String(params[0]))
            }
            return Time(i)

        case "time.Duration":   return TimeDuration(i)
        default:
            return i
    }
}

// 将变量i转换为time.Time类型
func Time(i interface{}, format...string) time.Time {
    s := String(i)
    // 优先使用用户输入日期格式进行转换
    if len(format) > 0 {
        t, _ := gtime.StrToTimeFormat(s, format[0])
        return t
    }
    if gstr.IsNumeric(s) {
        return gtime.NewFromTimeStamp(Int64(s)).Time
    } else {
        t, _ := gtime.StrToTime(s)
        return t
    }
}

// 将变量i转换为time.Time类型
func TimeDuration(i interface{}) time.Duration {
    return time.Duration(Int64(i))
}

func Bytes(i interface{}) []byte {
    if i == nil {
        return nil
    }
    if r, ok := i.([]byte); ok {
        return r
    } else {
        return gbinary.Encode(i)
    }
}

// 基础的字符串类型转换
func String(i interface{}) string {
    if i == nil {
        return ""
    }
    switch value := i.(type) {
        case int:     return strconv.Itoa(value)
        case int8:    return strconv.Itoa(int(value))
        case int16:   return strconv.Itoa(int(value))
        case int32:   return strconv.Itoa(int(value))
        case int64:   return strconv.Itoa(int(value))
        case uint:    return strconv.FormatUint(uint64(value), 10)
        case uint8:   return strconv.FormatUint(uint64(value), 10)
        case uint16:  return strconv.FormatUint(uint64(value), 10)
        case uint32:  return strconv.FormatUint(uint64(value), 10)
        case uint64:  return strconv.FormatUint(uint64(value), 10)
        case float32: return strconv.FormatFloat(float64(value), 'f', -1, 32)
        case float64: return strconv.FormatFloat(value, 'f', -1, 64)
        case bool:    return strconv.FormatBool(value)
        case string:  return value
        case []byte:  return string(value)
        default:
            // 默认使用json进行字符串转换
            jsonContent, _ := json.Marshal(value)
            return string(jsonContent)
    }
}

func Strings(i interface{}) []string {
    if i == nil {
        return nil
    }
    if r, ok := i.([]string); ok {
        return r
    } else if r, ok := i.([]interface{}); ok {
        strs := make([]string, len(r))
        for k, v := range r {
            strs[k] = String(v)
        }
        return strs
    }
    return []string{fmt.Sprintf("%v", i)}
}

//false: "", 0, false, off
func Bool(i interface{}) bool {
    if i == nil {
        return false
    }
    if v, ok := i.(bool); ok {
        return v
    }
    if s := String(i); s != "" && s != "0" && s != "false" && s != "off" {
        return true
    }
    return false
}

func Int(i interface{}) int {
    if i == nil {
        return 0
    }
    switch value := i.(type) {
        case int:     return value
        case int8:    return int(value)
        case int16:   return int(value)
        case int32:   return int(value)
        case int64:   return int(value)
        case uint:    return int(value)
        case uint8:   return int(value)
        case uint16:  return int(value)
        case uint32:  return int(value)
        case uint64:  return int(value)
        case float32: return int(value)
        case float64: return int(value)
        case bool:
            if value {
                return 1
            }
            return 0
        default:
            v, _ := strconv.Atoi(String(value))
            return v
    }
}

func Int8(i interface{}) int8 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int8); ok {
        return v
    }
    return int8(Int(i))
}

func Int16(i interface{}) int16 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int16); ok {
        return v
    }
    return int16(Int(i))
}

func Int32(i interface{}) int32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int32); ok {
        return v
    }
    return int32(Int(i))
}

func Int64(i interface{}) int64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(int64); ok {
        return v
    }
    return int64(Int(i))
}

func Uint(i interface{}) uint {
    if i == nil {
        return 0
    }
    switch value := i.(type) {
        case int:     return uint(value)
        case int8:    return uint(value)
        case int16:   return uint(value)
        case int32:   return uint(value)
        case int64:   return uint(value)
        case uint:    return value
        case uint8:   return uint(value)
        case uint16:  return uint(value)
        case uint32:  return uint(value)
        case uint64:  return uint(value)
        case float32: return uint(value)
        case float64: return uint(value)
        case bool:
            if value {
                return 1
            }
            return 0
        default:
            v, _ := strconv.ParseUint(String(value), 10, 64)
            return uint(v)
    }
}

func Uint8(i interface{}) uint8 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint8); ok {
        return v
    }
    return uint8(Uint(i))
}

func Uint16(i interface{}) uint16 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint16); ok {
        return v
    }
    return uint16(Uint(i))
}

func Uint32(i interface{}) uint32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint32); ok {
        return v
    }
    return uint32(Uint(i))
}

func Uint64(i interface{}) uint64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(uint64); ok {
        return v
    }
    return uint64(Uint(i))
}

func Float32 (i interface{}) float32 {
    if i == nil {
        return 0
    }
    if v, ok := i.(float32); ok {
        return v
    }
    v, _ := strconv.ParseFloat(String(i), 64)
    return float32(v)
}

func Float64 (i interface{}) float64 {
    if i == nil {
        return 0
    }
    if v, ok := i.(float64); ok {
        return v
    }
    v, _ := strconv.ParseFloat(String(i), 64)
    return v
}

// 将params键值对参数映射到对应的struct对象属性上，第三个参数mapping为非必需，表示自定义名称与属性名称的映射关系。
// 需要注意：
// 1、第二个参数为struct对象指针；
// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值；
// 3、map中的键名可以为小写，映射转换时会自动将键名首字母转为大写做匹配映射，如果无法匹配则忽略；
func MapToStruct(params map[string]interface{}, object interface{}, mapping...map[string]string) error {
    tagmap := make(map[string]string)
    fields := structs.Fields(object)
    // 将struct中定义的属性转换名称构建称tagmap
    for _, field := range fields {
        if tag := field.Tag("gconv"); tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagmap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    elem := reflect.ValueOf(object).Elem()
    dmap := make(map[string]bool)
    // 首先按照传递的映射关系进行匹配
    if len(mapping) > 0 {
        for mappingk, mappingv := range mapping[0] {
            if v, ok := params[mappingk]; ok {
                dmap[mappingv] = true
                bindVarToStruct(elem, mappingv, v)
            }
        }
    }
    // 其次匹配对象定义时绑定的属性名称
    for tagk, tagv := range tagmap {
        if _, ok := dmap[tagv]; ok {
            continue
        }
        if v, ok := params[tagk]; ok {
            dmap[tagv] = true
            bindVarToStruct(elem, tagv, v)
        }
    }
    // 最后按照默认规则进行匹配
    for mapk, mapv := range params {
        name := gstr.UcFirst(mapk)
        if _, ok := dmap[name]; ok {
            continue
        }
        // 后续tag逻辑中会处理的key(重复的键名)这里便不处理
        if _, ok := tagmap[mapk]; !ok {
            bindVarToStruct(elem, name, mapv)
        }
    }
    return nil
}

// 将参数值绑定到对象指定名称的属性上
func bindVarToStruct(elem reflect.Value, name string, value interface{}) {
    structFieldValue := elem.FieldByName(name)
    // 键名与对象属性匹配检测
    if !structFieldValue.IsValid() {
        return
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
}
<|endoftext|>


<commit_before>// +build darwin,!cgo

package notify

import (
	"github.com/deckarep/gosx-notifier"
)

type OSXNotifier struct{}

func NewOSXNotifier() *OSXNotifier {
	return &OSXNotifier{}
}

func (n *OSXNotifier) NotifySucceeded(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Succeeded") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Sound("Submarine")
	return note.Push()
}

func (n *OSXNotifier) NotifyFailed(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Failed") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Basso
	return note.Push()
}

func (n *OSXNotifier) NotifyFixed(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Fixed") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Sound("Submarine")
	return note.Push()
}
<commit_msg>remove cgo build constraint<commit_after>// +build darwin

package notify

import (
	"github.com/deckarep/gosx-notifier"
)

type OSXNotifier struct{}

func NewOSXNotifier() *OSXNotifier {
	return &OSXNotifier{}
}

func (n *OSXNotifier) NotifySucceeded(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Succeeded") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Sound("Submarine")
	return note.Push()
}

func (n *OSXNotifier) NotifyFailed(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Failed") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Basso
	return note.Push()
}

func (n *OSXNotifier) NotifyFixed(title, subtitle string) error {
	note := gosxnotifier.NewNotification("Build Fixed") // message body
	note.Title = title
	note.Subtitle = subtitle
	note.Sound = gosxnotifier.Sound("Submarine")
	return note.Push()
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package samples

import (
	"os"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
)

// A file system with a fixed structure that looks like this:
//
//     hello
//     dir/
//         world
//
// Each file contains the string "Hello, world!".
type HelloFS struct {
	fuseutil.NotImplementedFileSystem
	Clock timeutil.Clock
}

var _ fuse.FileSystem = &HelloFS{}

const (
	rootInode fuse.InodeID = fuse.RootInodeID + iota
	helloInode
	dirInode
	worldInode
)

type inodeInfo struct {
	attributes fuse.InodeAttributes

	// File or directory?
	dir bool

	// For directories, children.
	children []fuseutil.Dirent
}

// We have a fixed directory structure.
var gInodeInfo = map[fuse.InodeID]inodeInfo{
	// root
	rootInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			// TODO(jacobsa): Why do we get premission denied errors when this is
			// 0500?
			Mode: 0500 | os.ModeDir,
		},
		dir: true,
		children: []fuseutil.Dirent{
			fuseutil.Dirent{
				Offset: 1,
				Inode:  helloInode,
				Name:   "hello",
				Type:   fuseutil.DT_File,
			},
			fuseutil.Dirent{
				Offset: 2,
				Inode:  dirInode,
				Name:   "dir",
				Type:   fuseutil.DT_Directory,
			},
		},
	},

	// hello
	helloInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0400,
			Size: uint64(len("Hello, world!")),
		},
	},

	// dir
	dirInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0500 | os.ModeDir,
		},
		dir: true,
		children: []fuseutil.Dirent{
			fuseutil.Dirent{
				Offset: 1,
				Inode:  worldInode,
				Name:   "world",
				Type:   fuseutil.DT_File,
			},
		},
	},

	// world
	worldInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0400,
			Size: uint64(len("Hello, world!")),
		},
	},
}

func findChildInode(
	name string,
	children []fuseutil.Dirent) (inode fuse.InodeID, err error) {
	for _, e := range children {
		if e.Name == name {
			inode = e.Inode
			return
		}
	}

	err = fuse.ENOENT
	return
}

func (fs *HelloFS) patchAttributes(
	attr *fuse.InodeAttributes) {
	now := fs.Clock.Now()
	attr.Atime = now
	attr.Mtime = now
	attr.Crtime = now
}

func (fs *HelloFS) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (
	resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	// Find the info for the parent.
	parentInfo, ok := gInodeInfo[req.Parent]
	if !ok {
		err = fuse.ENOENT
		return
	}

	// Find the child within the parent.
	childInode, err := findChildInode(req.Name, parentInfo.children)
	if err != nil {
		return
	}

	// Copy over information.
	resp.Child = childInode
	resp.Attributes = gInodeInfo[childInode].attributes

	// Patch attributes.
	fs.patchAttributes(&resp.Attributes)

	return
}

func (fs *HelloFS) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	// Find the info for this inode.
	info, ok := gInodeInfo[req.Inode]
	if !ok {
		err = fuse.ENOENT
		return
	}

	// Copy over its attributes.
	resp.Attributes = info.attributes

	// Patch attributes.
	fs.patchAttributes(&resp.Attributes)

	return
}

func (fs *HelloFS) OpenDir(
	ctx context.Context,
	req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
	// We always allow opening the root directory.
	if req.Inode == rootInode {
		resp = &fuse.OpenDirResponse{}
		return
	}

	// TODO(jacobsa): Handle others.
	err = fuse.ENOSYS
	return
}

func (fs *HelloFS) ReadDir(
	ctx context.Context,
	req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
	resp = &fuse.ReadDirResponse{}

	// Find the info for this inode.
	info, ok := gInodeInfo[req.Inode]
	if !ok {
		err = fuse.ENOENT
		return
	}

	if !info.dir {
		err = fuse.EIO
		return
	}

	entries := info.children

	// Grab the range of interest.
	if req.Offset > fuse.DirOffset(len(entries)) {
		err = fuse.EIO
		return
	}

	entries = entries[req.Offset:]

	// Resume at the specified offset into the array.
	for _, e := range entries {
		resp.Data = fuseutil.AppendDirent(resp.Data, e)
		if len(resp.Data) > req.Size {
			resp.Data = resp.Data[:req.Size]
			break
		}
	}

	return
}
<commit_msg>Implemented HelloFS.Init.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package samples

import (
	"os"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
)

// A file system with a fixed structure that looks like this:
//
//     hello
//     dir/
//         world
//
// Each file contains the string "Hello, world!".
type HelloFS struct {
	fuseutil.NotImplementedFileSystem
	Clock timeutil.Clock
}

var _ fuse.FileSystem = &HelloFS{}

const (
	rootInode fuse.InodeID = fuse.RootInodeID + iota
	helloInode
	dirInode
	worldInode
)

type inodeInfo struct {
	attributes fuse.InodeAttributes

	// File or directory?
	dir bool

	// For directories, children.
	children []fuseutil.Dirent
}

// We have a fixed directory structure.
var gInodeInfo = map[fuse.InodeID]inodeInfo{
	// root
	rootInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			// TODO(jacobsa): Why do we get premission denied errors when this is
			// 0500?
			Mode: 0500 | os.ModeDir,
		},
		dir: true,
		children: []fuseutil.Dirent{
			fuseutil.Dirent{
				Offset: 1,
				Inode:  helloInode,
				Name:   "hello",
				Type:   fuseutil.DT_File,
			},
			fuseutil.Dirent{
				Offset: 2,
				Inode:  dirInode,
				Name:   "dir",
				Type:   fuseutil.DT_Directory,
			},
		},
	},

	// hello
	helloInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0400,
			Size: uint64(len("Hello, world!")),
		},
	},

	// dir
	dirInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0500 | os.ModeDir,
		},
		dir: true,
		children: []fuseutil.Dirent{
			fuseutil.Dirent{
				Offset: 1,
				Inode:  worldInode,
				Name:   "world",
				Type:   fuseutil.DT_File,
			},
		},
	},

	// world
	worldInode: inodeInfo{
		attributes: fuse.InodeAttributes{
			Mode: 0400,
			Size: uint64(len("Hello, world!")),
		},
	},
}

func findChildInode(
	name string,
	children []fuseutil.Dirent) (inode fuse.InodeID, err error) {
	for _, e := range children {
		if e.Name == name {
			inode = e.Inode
			return
		}
	}

	err = fuse.ENOENT
	return
}

func (fs *HelloFS) patchAttributes(
	attr *fuse.InodeAttributes) {
	now := fs.Clock.Now()
	attr.Atime = now
	attr.Mtime = now
	attr.Crtime = now
}

func (fs *HelloFS) Init(
	ctx context.Context,
	req *fuse.InitRequest) (
	resp *fuse.InitResponse, err error) {
	resp = &fuse.InitResponse{}
	return
}

func (fs *HelloFS) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (
	resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	// Find the info for the parent.
	parentInfo, ok := gInodeInfo[req.Parent]
	if !ok {
		err = fuse.ENOENT
		return
	}

	// Find the child within the parent.
	childInode, err := findChildInode(req.Name, parentInfo.children)
	if err != nil {
		return
	}

	// Copy over information.
	resp.Child = childInode
	resp.Attributes = gInodeInfo[childInode].attributes

	// Patch attributes.
	fs.patchAttributes(&resp.Attributes)

	return
}

func (fs *HelloFS) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	// Find the info for this inode.
	info, ok := gInodeInfo[req.Inode]
	if !ok {
		err = fuse.ENOENT
		return
	}

	// Copy over its attributes.
	resp.Attributes = info.attributes

	// Patch attributes.
	fs.patchAttributes(&resp.Attributes)

	return
}

func (fs *HelloFS) OpenDir(
	ctx context.Context,
	req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
	// We always allow opening the root directory.
	if req.Inode == rootInode {
		resp = &fuse.OpenDirResponse{}
		return
	}

	// TODO(jacobsa): Handle others.
	err = fuse.ENOSYS
	return
}

func (fs *HelloFS) ReadDir(
	ctx context.Context,
	req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
	resp = &fuse.ReadDirResponse{}

	// Find the info for this inode.
	info, ok := gInodeInfo[req.Inode]
	if !ok {
		err = fuse.ENOENT
		return
	}

	if !info.dir {
		err = fuse.EIO
		return
	}

	entries := info.children

	// Grab the range of interest.
	if req.Offset > fuse.DirOffset(len(entries)) {
		err = fuse.EIO
		return
	}

	entries = entries[req.Offset:]

	// Resume at the specified offset into the array.
	for _, e := range entries {
		resp.Data = fuseutil.AppendDirent(resp.Data, e)
		if len(resp.Data) > req.Size {
			resp.Data = resp.Data[:req.Size]
			break
		}
	}

	return
}
<|endoftext|>