<commit_before>package dashboards

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/services/dashboards"

	"github.com/grafana/grafana/pkg/bus"

	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
)

var (
	checkDiskForChangesInterval = time.Second * 3

	ErrFolderNameMissing = errors.New("Folder name missing")
)

type fileReader struct {
	Cfg              *DashboardsAsConfig
	Path             string
	log              log.Logger
	dashboardService dashboards.DashboardProvisioningService
}

func NewDashboardFileReader(cfg *DashboardsAsConfig, log log.Logger) (*fileReader, error) {
	var path string
	path, ok := cfg.Options["path"].(string)
	if !ok {
		path, ok = cfg.Options["folder"].(string)
		if !ok {
			return nil, fmt.Errorf("Failed to load dashboards. path param is not a string")
		}

		log.Warn("[Deprecated] The folder property is deprecated. Please use path instead.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Error("Cannot read directory", "error", err)
	}

	path, err := filepath.EvalSymlinks(path)
	if err != nil {
		log.Error("Failed to read content of symlinked path: %s", path)
	}

	absPath, err := filepath.Abs(path)
	if err != nil {
		log.Error("Could not create absolute path ", "path", path)
		absPath = path //if .Abs return an error we fallback to path
	}

	return &fileReader{
		Cfg:              cfg,
		Path:             absPath,
		log:              log,
		dashboardService: dashboards.NewProvisioningService(),
	}, nil
}

func (fr *fileReader) ReadAndListen(ctx context.Context) error {
	if err := fr.startWalkingDisk(); err != nil {
		fr.log.Error("failed to search for dashboards", "error", err)
	}

	ticker := time.NewTicker(checkDiskForChangesInterval)

	running := false

	for {
		select {
		case <-ticker.C:
			if !running { // avoid walking the filesystem in parallel. in-case fs is very slow.
				running = true
				go func() {
					if err := fr.startWalkingDisk(); err != nil {
						fr.log.Error("failed to search for dashboards", "error", err)
					}
					running = false
				}()
			}
		case <-ctx.Done():
			return nil
		}
	}
}

func (fr *fileReader) startWalkingDisk() error {
	if _, err := os.Stat(fr.Path); err != nil {
		if os.IsNotExist(err) {
			return err
		}
	}

	folderId, err := getOrCreateFolderId(fr.Cfg, fr.dashboardService)
	if err != nil && err != ErrFolderNameMissing {
		return err
	}

	provisionedDashboardRefs, err := getProvisionedDashboardByPath(fr.dashboardService, fr.Cfg.Name)
	if err != nil {
		return err
	}

	filesFoundOnDisk := map[string]os.FileInfo{}
	err = filepath.Walk(fr.Path, createWalkFn(filesFoundOnDisk))
	if err != nil {
		return err
	}

	fr.deleteDashboardIfFileIsMissing(provisionedDashboardRefs, filesFoundOnDisk)

	sanityChecker := newProvisioningSanityChecker(fr.Cfg.Name)

	// save dashboards based on json files
	for path, fileInfo := range filesFoundOnDisk {
		provisioningMetadata, err := fr.saveDashboard(path, folderId, fileInfo, provisionedDashboardRefs)
		sanityChecker.track(provisioningMetadata)
		if err != nil {
			fr.log.Error("failed to save dashboard", "error", err)
		}
	}
	sanityChecker.logWarnings(fr.log)

	return nil
}
func (fr *fileReader) deleteDashboardIfFileIsMissing(provisionedDashboardRefs map[string]*models.DashboardProvisioning, filesFoundOnDisk map[string]os.FileInfo) {
	if fr.Cfg.DisableDeletion {
		return
	}

	// find dashboards to delete since json file is missing
	var dashboardToDelete []int64
	for path, provisioningData := range provisionedDashboardRefs {
		_, existsOnDisk := filesFoundOnDisk[path]
		if !existsOnDisk {
			dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
		}
	}
	// delete dashboard that are missing json file
	for _, dashboardId := range dashboardToDelete {
		fr.log.Debug("deleting provisioned dashboard. missing on disk", "id", dashboardId)
		cmd := &models.DeleteDashboardCommand{OrgId: fr.Cfg.OrgId, Id: dashboardId}
		err := bus.Dispatch(cmd)
		if err != nil {
			fr.log.Error("failed to delete dashboard", "id", cmd.Id)
		}
	}
}

func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) (provisioningMetadata, error) {
	provisioningMetadata := provisioningMetadata{}
	resolvedFileInfo, err := resolveSymlink(fileInfo, path)
	if err != nil {
		return provisioningMetadata, err
	}

	provisionedData, alreadyProvisioned := provisionedDashboardRefs[path]
	upToDate := alreadyProvisioned && provisionedData.Updated == resolvedFileInfo.ModTime().Unix()

	dash, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderId)
	if err != nil {
		fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
		return provisioningMetadata, nil
	}

	// keeps track of what uid's and title's we have already provisioned
	provisioningMetadata.uid = dash.Dashboard.Uid
	provisioningMetadata.title = dash.Dashboard.Title

	if upToDate {
		return provisioningMetadata, nil
	}

	if dash.Dashboard.Id != 0 {
		dash.Dashboard.Data.Set("id", nil)
		dash.Dashboard.Id = 0
	}

	if alreadyProvisioned {
		dash.Dashboard.SetId(provisionedData.DashboardId)
	}

	fr.log.Debug("saving new dashboard", "file", path)
	dp := &models.DashboardProvisioning{ExternalId: path, Name: fr.Cfg.Name, Updated: resolvedFileInfo.ModTime().Unix()}
	_, err = fr.dashboardService.SaveProvisionedDashboard(dash, dp)
	return provisioningMetadata, err
}

func getProvisionedDashboardByPath(service dashboards.DashboardProvisioningService, name string) (map[string]*models.DashboardProvisioning, error) {
	arr, err := service.GetProvisionedDashboardData(name)
	if err != nil {
		return nil, err
	}

	byPath := map[string]*models.DashboardProvisioning{}
	for _, pd := range arr {
		byPath[pd.ExternalId] = pd
	}

	return byPath, nil
}

func getOrCreateFolderId(cfg *DashboardsAsConfig, service dashboards.DashboardProvisioningService) (int64, error) {
	if cfg.Folder == "" {
		return 0, ErrFolderNameMissing
	}

	cmd := &models.GetDashboardQuery{Slug: models.SlugifyTitle(cfg.Folder), OrgId: cfg.OrgId}
	err := bus.Dispatch(cmd)

	if err != nil && err != models.ErrDashboardNotFound {
		return 0, err
	}

	// dashboard folder not found. create one.
	if err == models.ErrDashboardNotFound {
		dash := &dashboards.SaveDashboardDTO{}
		dash.Dashboard = models.NewDashboardFolder(cfg.Folder)
		dash.Dashboard.IsFolder = true
		dash.Overwrite = true
		dash.OrgId = cfg.OrgId
		dbDash, err := service.SaveFolderForProvisionedDashboards(dash)
		if err != nil {
			return 0, err
		}

		return dbDash.Id, nil
	}

	if !cmd.Result.IsFolder {
		return 0, fmt.Errorf("got invalid response. expected folder, found dashboard")
	}

	return cmd.Result.Id, nil
}

func resolveSymlink(fileinfo os.FileInfo, path string) (os.FileInfo, error) {
	checkFilepath, err := filepath.EvalSymlinks(path)
	if path != checkFilepath {
		fi, err := os.Lstat(checkFilepath)
		if err != nil {
			return nil, err
		}

		return fi, nil
	}

	return fileinfo, err
}

func createWalkFn(filesOnDisk map[string]os.FileInfo) filepath.WalkFunc {
	return func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		isValid, err := validateWalkablePath(fileInfo)
		if !isValid {
			return err
		}

		filesOnDisk[path] = fileInfo
		return nil
	}
}

func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
	if fileInfo.IsDir() {
		if strings.HasPrefix(fileInfo.Name(), ".") {
			return false, filepath.SkipDir
		}
		return false, nil
	}

	if !strings.HasSuffix(fileInfo.Name(), ".json") {
		return false, nil
	}

	return true, nil
}

func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, folderId int64) (*dashboards.SaveDashboardDTO, error) {
	reader, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	data, err := simplejson.NewFromReader(reader)
	if err != nil {
		return nil, err
	}

	dash, err := createDashboardJson(data, lastModified, fr.Cfg, folderId)
	if err != nil {
		return nil, err
	}

	return dash, nil
}

type provisioningMetadata struct {
	uid   string
	title string
}

func newProvisioningSanityChecker(provisioningProvider string) provisioningSanityChecker {
	return provisioningSanityChecker{
		provisioningProvider: provisioningProvider,
		uidUsage:             map[string]uint8{},
		titleUsage:           map[string]uint8{}}
}

type provisioningSanityChecker struct {
	provisioningProvider string
	uidUsage             map[string]uint8
	titleUsage           map[string]uint8
}

func (checker provisioningSanityChecker) track(pm provisioningMetadata) {
	if len(pm.uid) > 0 {
		checker.uidUsage[pm.uid] += 1
	}
	if len(pm.title) > 0 {
		checker.titleUsage[pm.title] += 1
	}

}

func (checker provisioningSanityChecker) logWarnings(log log.Logger) {
	for uid, times := range checker.uidUsage {
		if times > 1 {
			log.Error("the same 'uid' is used more than once", "uid", uid, "provider", checker.provisioningProvider)
		}
	}

	for title, times := range checker.titleUsage {
		if times > 1 {
			log.Error("the same 'title' is used more than once", "title", title, "provider", checker.provisioningProvider)
		}
	}

}
<commit_msg>make path absolute before following symlink<commit_after>package dashboards

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/services/dashboards"

	"github.com/grafana/grafana/pkg/bus"

	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
)

var (
	checkDiskForChangesInterval = time.Second * 3

	ErrFolderNameMissing = errors.New("Folder name missing")
)

type fileReader struct {
	Cfg              *DashboardsAsConfig
	Path             string
	log              log.Logger
	dashboardService dashboards.DashboardProvisioningService
}

func NewDashboardFileReader(cfg *DashboardsAsConfig, log log.Logger) (*fileReader, error) {
	var path string
	path, ok := cfg.Options["path"].(string)
	if !ok {
		path, ok = cfg.Options["folder"].(string)
		if !ok {
			return nil, fmt.Errorf("Failed to load dashboards. path param is not a string")
		}

		log.Warn("[Deprecated] The folder property is deprecated. Please use path instead.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Error("Cannot read directory", "error", err)
	}

	copy := path
	path, err := filepath.Abs(path)
	if err != nil {
		log.Error("Could not create absolute path ", "path", path)
		path = copy //if .Abs return an error we fallback to path
	}

	path, err = filepath.EvalSymlinks(path)
	if err != nil {
		log.Error("Failed to read content of symlinked path: %s", path)
	}

	return &fileReader{
		Cfg:              cfg,
		Path:             path,
		log:              log,
		dashboardService: dashboards.NewProvisioningService(),
	}, nil
}

func (fr *fileReader) ReadAndListen(ctx context.Context) error {
	if err := fr.startWalkingDisk(); err != nil {
		fr.log.Error("failed to search for dashboards", "error", err)
	}

	ticker := time.NewTicker(checkDiskForChangesInterval)

	running := false

	for {
		select {
		case <-ticker.C:
			if !running { // avoid walking the filesystem in parallel. in-case fs is very slow.
				running = true
				go func() {
					if err := fr.startWalkingDisk(); err != nil {
						fr.log.Error("failed to search for dashboards", "error", err)
					}
					running = false
				}()
			}
		case <-ctx.Done():
			return nil
		}
	}
}

func (fr *fileReader) startWalkingDisk() error {
	if _, err := os.Stat(fr.Path); err != nil {
		if os.IsNotExist(err) {
			return err
		}
	}

	folderId, err := getOrCreateFolderId(fr.Cfg, fr.dashboardService)
	if err != nil && err != ErrFolderNameMissing {
		return err
	}

	provisionedDashboardRefs, err := getProvisionedDashboardByPath(fr.dashboardService, fr.Cfg.Name)
	if err != nil {
		return err
	}

	filesFoundOnDisk := map[string]os.FileInfo{}
	err = filepath.Walk(fr.Path, createWalkFn(filesFoundOnDisk))
	if err != nil {
		return err
	}

	fr.deleteDashboardIfFileIsMissing(provisionedDashboardRefs, filesFoundOnDisk)

	sanityChecker := newProvisioningSanityChecker(fr.Cfg.Name)

	// save dashboards based on json files
	for path, fileInfo := range filesFoundOnDisk {
		provisioningMetadata, err := fr.saveDashboard(path, folderId, fileInfo, provisionedDashboardRefs)
		sanityChecker.track(provisioningMetadata)
		if err != nil {
			fr.log.Error("failed to save dashboard", "error", err)
		}
	}
	sanityChecker.logWarnings(fr.log)

	return nil
}
func (fr *fileReader) deleteDashboardIfFileIsMissing(provisionedDashboardRefs map[string]*models.DashboardProvisioning, filesFoundOnDisk map[string]os.FileInfo) {
	if fr.Cfg.DisableDeletion {
		return
	}

	// find dashboards to delete since json file is missing
	var dashboardToDelete []int64
	for path, provisioningData := range provisionedDashboardRefs {
		_, existsOnDisk := filesFoundOnDisk[path]
		if !existsOnDisk {
			dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
		}
	}
	// delete dashboard that are missing json file
	for _, dashboardId := range dashboardToDelete {
		fr.log.Debug("deleting provisioned dashboard. missing on disk", "id", dashboardId)
		cmd := &models.DeleteDashboardCommand{OrgId: fr.Cfg.OrgId, Id: dashboardId}
		err := bus.Dispatch(cmd)
		if err != nil {
			fr.log.Error("failed to delete dashboard", "id", cmd.Id)
		}
	}
}

func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) (provisioningMetadata, error) {
	provisioningMetadata := provisioningMetadata{}
	resolvedFileInfo, err := resolveSymlink(fileInfo, path)
	if err != nil {
		return provisioningMetadata, err
	}

	provisionedData, alreadyProvisioned := provisionedDashboardRefs[path]
	upToDate := alreadyProvisioned && provisionedData.Updated == resolvedFileInfo.ModTime().Unix()

	dash, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderId)
	if err != nil {
		fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
		return provisioningMetadata, nil
	}

	// keeps track of what uid's and title's we have already provisioned
	provisioningMetadata.uid = dash.Dashboard.Uid
	provisioningMetadata.title = dash.Dashboard.Title

	if upToDate {
		return provisioningMetadata, nil
	}

	if dash.Dashboard.Id != 0 {
		dash.Dashboard.Data.Set("id", nil)
		dash.Dashboard.Id = 0
	}

	if alreadyProvisioned {
		dash.Dashboard.SetId(provisionedData.DashboardId)
	}

	fr.log.Debug("saving new dashboard", "file", path)
	dp := &models.DashboardProvisioning{ExternalId: path, Name: fr.Cfg.Name, Updated: resolvedFileInfo.ModTime().Unix()}
	_, err = fr.dashboardService.SaveProvisionedDashboard(dash, dp)
	return provisioningMetadata, err
}

func getProvisionedDashboardByPath(service dashboards.DashboardProvisioningService, name string) (map[string]*models.DashboardProvisioning, error) {
	arr, err := service.GetProvisionedDashboardData(name)
	if err != nil {
		return nil, err
	}

	byPath := map[string]*models.DashboardProvisioning{}
	for _, pd := range arr {
		byPath[pd.ExternalId] = pd
	}

	return byPath, nil
}

func getOrCreateFolderId(cfg *DashboardsAsConfig, service dashboards.DashboardProvisioningService) (int64, error) {
	if cfg.Folder == "" {
		return 0, ErrFolderNameMissing
	}

	cmd := &models.GetDashboardQuery{Slug: models.SlugifyTitle(cfg.Folder), OrgId: cfg.OrgId}
	err := bus.Dispatch(cmd)

	if err != nil && err != models.ErrDashboardNotFound {
		return 0, err
	}

	// dashboard folder not found. create one.
	if err == models.ErrDashboardNotFound {
		dash := &dashboards.SaveDashboardDTO{}
		dash.Dashboard = models.NewDashboardFolder(cfg.Folder)
		dash.Dashboard.IsFolder = true
		dash.Overwrite = true
		dash.OrgId = cfg.OrgId
		dbDash, err := service.SaveFolderForProvisionedDashboards(dash)
		if err != nil {
			return 0, err
		}

		return dbDash.Id, nil
	}

	if !cmd.Result.IsFolder {
		return 0, fmt.Errorf("got invalid response. expected folder, found dashboard")
	}

	return cmd.Result.Id, nil
}

func resolveSymlink(fileinfo os.FileInfo, path string) (os.FileInfo, error) {
	checkFilepath, err := filepath.EvalSymlinks(path)
	if path != checkFilepath {
		fi, err := os.Lstat(checkFilepath)
		if err != nil {
			return nil, err
		}

		return fi, nil
	}

	return fileinfo, err
}

func createWalkFn(filesOnDisk map[string]os.FileInfo) filepath.WalkFunc {
	return func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		isValid, err := validateWalkablePath(fileInfo)
		if !isValid {
			return err
		}

		filesOnDisk[path] = fileInfo
		return nil
	}
}

func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
	if fileInfo.IsDir() {
		if strings.HasPrefix(fileInfo.Name(), ".") {
			return false, filepath.SkipDir
		}
		return false, nil
	}

	if !strings.HasSuffix(fileInfo.Name(), ".json") {
		return false, nil
	}

	return true, nil
}

func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, folderId int64) (*dashboards.SaveDashboardDTO, error) {
	reader, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	data, err := simplejson.NewFromReader(reader)
	if err != nil {
		return nil, err
	}

	dash, err := createDashboardJson(data, lastModified, fr.Cfg, folderId)
	if err != nil {
		return nil, err
	}

	return dash, nil
}

type provisioningMetadata struct {
	uid   string
	title string
}

func newProvisioningSanityChecker(provisioningProvider string) provisioningSanityChecker {
	return provisioningSanityChecker{
		provisioningProvider: provisioningProvider,
		uidUsage:             map[string]uint8{},
		titleUsage:           map[string]uint8{}}
}

type provisioningSanityChecker struct {
	provisioningProvider string
	uidUsage             map[string]uint8
	titleUsage           map[string]uint8
}

func (checker provisioningSanityChecker) track(pm provisioningMetadata) {
	if len(pm.uid) > 0 {
		checker.uidUsage[pm.uid] += 1
	}
	if len(pm.title) > 0 {
		checker.titleUsage[pm.title] += 1
	}

}

func (checker provisioningSanityChecker) logWarnings(log log.Logger) {
	for uid, times := range checker.uidUsage {
		if times > 1 {
			log.Error("the same 'uid' is used more than once", "uid", uid, "provider", checker.provisioningProvider)
		}
	}

	for title, times := range checker.titleUsage {
		if times > 1 {
			log.Error("the same 'title' is used more than once", "title", title, "provider", checker.provisioningProvider)
		}
	}

}
<|endoftext|>


<commit_before>package acceptance_test

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/config"
)

const (
	BYTE     = int64(1)
	KILOBYTE = 1024 * BYTE
	MEGABYTE = 1024 * KILOBYTE
	GIGABYTE = 1024 * MEGABYTE
	TERABYTE = 1024 * GIGABYTE

	DEFAULT_MEMORY_LIMIT = "256M"
	DB_CREATE_TIMEOUT    = 30 * time.Minute
)

var (
	testConfig  config.CatsConfig
	httpClient  *http.Client
	testContext *workflowhelpers.ReproducibleTestSuiteSetup
)

func TestSuite(t *testing.T) {
	var err error
	RegisterFailHandler(Fail)

	testConfig, err = config.NewCatsConfig(os.Getenv("CONFIG"))
	Expect(err).NotTo(HaveOccurred())

	httpClient = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: testConfig.GetSkipSSLValidation()},
		},
	}

	testContext = workflowhelpers.NewTestSuiteSetup(testConfig)

	BeforeSuite(func() {
		testContext.Setup()
	})

	AfterSuite(func() {
		testContext.Teardown()
	})

	componentName := "Custom-Acceptance-Tests"
	if testConfig.GetArtifactsDirectory() != "" {
		helpers.EnableCFTrace(testConfig, componentName)
	}

	RunSpecs(t, componentName)
}

// quietCf is an equivelent of cf.Cf that doesn't send the output to
// GinkgoWriter. Used when you don't want the output, even in verbose mode (eg
// when polling the API)
func quietCf(program string, args ...string) *Session {
	command, err := Start(exec.Command(program, args...), nil, nil)
	Expect(err).NotTo(HaveOccurred())
	return command
}

func pollForServiceCreationCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service creation to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "service", dbInstanceName).Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).Should(Say("create succeeded"))
	fmt.Fprint(GinkgoWriter, "done\n")
}

func pollForServiceUpdateCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service update to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "service", dbInstanceName).Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).Should(Say("update succeeded"))
	fmt.Fprint(GinkgoWriter, "done\n")
}

func pollForServiceDeletionCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service destruction to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "services").Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).ShouldNot(Say(dbInstanceName))
	fmt.Fprint(GinkgoWriter, "done\n")
}
<commit_msg>Improve error messages from tests<commit_after>package acceptance_test

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/config"
)

const (
	BYTE     = int64(1)
	KILOBYTE = 1024 * BYTE
	MEGABYTE = 1024 * KILOBYTE
	GIGABYTE = 1024 * MEGABYTE
	TERABYTE = 1024 * GIGABYTE

	DEFAULT_MEMORY_LIMIT = "256M"
	DB_CREATE_TIMEOUT    = 30 * time.Minute
)

var (
	testConfig  config.CatsConfig
	httpClient  *http.Client
	testContext *workflowhelpers.ReproducibleTestSuiteSetup
)

func TestSuite(t *testing.T) {
	var err error
	RegisterFailHandler(Fail)

	testConfig, err = config.NewCatsConfig(os.Getenv("CONFIG"))
	Expect(err).NotTo(HaveOccurred())

	httpClient = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: testConfig.GetSkipSSLValidation()},
		},
	}

	testContext = workflowhelpers.NewTestSuiteSetup(testConfig)

	BeforeSuite(func() {
		testContext.Setup()
	})

	AfterSuite(func() {
		testContext.Teardown()
	})

	componentName := "Custom-Acceptance-Tests"
	if testConfig.GetArtifactsDirectory() != "" {
		helpers.EnableCFTrace(testConfig, componentName)
	}

	RunSpecs(t, componentName)
}

// quietCf is an equivelent of cf.Cf that doesn't send the output to
// GinkgoWriter. Used when you don't want the output, even in verbose mode (eg
// when polling the API)
func quietCf(program string, args ...string) *Session {
	command, err := Start(exec.Command(program, args...), nil, nil)
	Expect(err).NotTo(HaveOccurred())
	return command
}

func pollForServiceCreationCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service creation to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "service", dbInstanceName).Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0), fmt.Sprint("Error calling cf service creation phase: ", string(command.Out.Contents())))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).Should(Say("create succeeded"))
	fmt.Fprint(GinkgoWriter, "done\n")
}

func pollForServiceUpdateCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service update to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "service", dbInstanceName).Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0), fmt.Sprint("Error calling cf service update phase: ", string(command.Out.Contents())))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).Should(Say("update succeeded"))
	fmt.Fprint(GinkgoWriter, "done\n")
}

func pollForServiceDeletionCompletion(dbInstanceName string) {
	fmt.Fprint(GinkgoWriter, "Polling for service destruction to complete")
	Eventually(func() *Buffer {
		fmt.Fprint(GinkgoWriter, ".")
		command := quietCf("cf", "services").Wait(testConfig.DefaultTimeoutDuration())
		Expect(command).To(Exit(0), fmt.Sprint("Error calling cf services: ", string(command.Out.Contents())))
		return command.Out
	}, DB_CREATE_TIMEOUT, 15*time.Second).ShouldNot(Say(dbInstanceName))
	fmt.Fprint(GinkgoWriter, "done\n")
}
<|endoftext|>


<commit_before>package vault

import (
	"bytes"
	"reflect"
	"testing"
	"time"
)

func TestKeyring(t *testing.T) {
	k := NewKeyring()

	// Term should be 0
	if term := k.ActiveTerm(); term != 0 {
		t.Fatalf("bad: %d", term)
	}

	// Should have no key
	if key := k.ActiveKey(); key != nil {
		t.Fatalf("bad: %v", key)
	}

	// Add a key
	testKey := []byte("testing")
	key1 := &Key{Term: 1, Version: 1, Value: testKey, InstallTime: time.Now()}
	k, err := k.AddKey(key1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Term should be 1
	if term := k.ActiveTerm(); term != 1 {
		t.Fatalf("bad: %d", term)
	}

	// Should have key
	key := k.ActiveKey()
	if key == nil {
		t.Fatalf("bad: %v", key)
	}
	if !bytes.Equal(key.Value, testKey) {
		t.Fatalf("bad: %v", key)
	}
	if tKey := k.TermKey(1); tKey != key {
		t.Fatalf("bad: %v", tKey)
	}

	// Should handle idempotent set
	k, err = k.AddKey(key1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should not allow conficting set
	testConflict := []byte("nope")
	key1Conf := &Key{Term: 1, Version: 1, Value: testConflict, InstallTime: time.Now()}
	_, err = k.AddKey(key1Conf)
	if err == nil {
		t.Fatalf("err: %v", err)
	}

	// Add a new key
	testSecond := []byte("second")
	key2 := &Key{Term: 2, Version: 1, Value: testSecond, InstallTime: time.Now()}
	k, err = k.AddKey(key2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Term should be 2
	if term := k.ActiveTerm(); term != 2 {
		t.Fatalf("bad: %d", term)
	}

	// Should have key
	newKey := k.ActiveKey()
	if newKey == nil {
		t.Fatalf("bad: %v", key)
	}
	if !bytes.Equal(newKey.Value, testSecond) {
		t.Fatalf("bad: %v", key)
	}
	if tKey := k.TermKey(2); tKey != newKey {
		t.Fatalf("bad: %v", tKey)
	}

	// Read of old key should work
	if tKey := k.TermKey(1); tKey != key {
		t.Fatalf("bad: %v", tKey)
	}

	// Remove the old key
	k, err = k.RemoveKey(1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Read of old key should not work
	if tKey := k.TermKey(1); tKey != nil {
		t.Fatalf("bad: %v", tKey)
	}

	// Remove the active key should fail
	k, err = k.RemoveKey(2)
	if err == nil {
		t.Fatalf("err: %v", err)
	}
}

func TestKeyring_MasterKey(t *testing.T) {
	k := NewKeyring()
	master := []byte("test")
	master2 := []byte("test2")

	// Check no master
	out := k.MasterKey()
	if out != nil {
		t.Fatalf("bad: %v", out)
	}

	// Set master
	k = k.SetMasterKey(master)
	out = k.MasterKey()
	if !bytes.Equal(out, master) {
		t.Fatalf("bad: %v", out)
	}

	// Update master
	k = k.SetMasterKey(master2)
	out = k.MasterKey()
	if !bytes.Equal(out, master2) {
		t.Fatalf("bad: %v", out)
	}
}

func TestKeyring_Serialize(t *testing.T) {
	k := NewKeyring()
	master := []byte("test")
	k = k.SetMasterKey(master)

	testKey := []byte("testing")
	testSecond := []byte("second")
	k, _ = k.AddKey(&Key{Term: 1, Version: 1, Value: testKey, InstallTime: time.Now()})
	k, _ = k.AddKey(&Key{Term: 2, Version: 1, Value: testSecond, InstallTime: time.Now()})

	buf, err := k.Serialize()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	k2, err := DeserializeKeyring(buf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	out := k2.MasterKey()
	if !bytes.Equal(out, master) {
		t.Fatalf("bad: %v", out)
	}

	if k2.ActiveTerm() != k.ActiveTerm() {
		t.Fatalf("Term mismatch")
	}

	var i uint32
	for i = 1; i < k.ActiveTerm(); i++ {
		key1 := k2.TermKey(i)
		key2 := k.TermKey(i)
		if !reflect.DeepEqual(key1, key2) {
			t.Fatalf("bad: %v %v", key1, key2)
		}
	}
}

func TestKey_Serialize(t *testing.T) {
	k := &Key{
		Term:        10,
		Version:     1,
		Value:       []byte("foobarbaz"),
		InstallTime: time.Now(),
	}

	buf, err := k.Serialize()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	out, err := DeserializeKey(buf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(k, out) {
		t.Fatalf("bad: %#v", out)
	}
}
<commit_msg>Use the same time object in the serialization test<commit_after>package vault

import (
	"bytes"
	"reflect"
	"testing"
	"time"
)

func TestKeyring(t *testing.T) {
	k := NewKeyring()

	// Term should be 0
	if term := k.ActiveTerm(); term != 0 {
		t.Fatalf("bad: %d", term)
	}

	// Should have no key
	if key := k.ActiveKey(); key != nil {
		t.Fatalf("bad: %v", key)
	}

	// Add a key
	testKey := []byte("testing")
	key1 := &Key{Term: 1, Version: 1, Value: testKey, InstallTime: time.Now()}
	k, err := k.AddKey(key1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Term should be 1
	if term := k.ActiveTerm(); term != 1 {
		t.Fatalf("bad: %d", term)
	}

	// Should have key
	key := k.ActiveKey()
	if key == nil {
		t.Fatalf("bad: %v", key)
	}
	if !bytes.Equal(key.Value, testKey) {
		t.Fatalf("bad: %v", key)
	}
	if tKey := k.TermKey(1); tKey != key {
		t.Fatalf("bad: %v", tKey)
	}

	// Should handle idempotent set
	k, err = k.AddKey(key1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should not allow conficting set
	testConflict := []byte("nope")
	key1Conf := &Key{Term: 1, Version: 1, Value: testConflict, InstallTime: time.Now()}
	_, err = k.AddKey(key1Conf)
	if err == nil {
		t.Fatalf("err: %v", err)
	}

	// Add a new key
	testSecond := []byte("second")
	key2 := &Key{Term: 2, Version: 1, Value: testSecond, InstallTime: time.Now()}
	k, err = k.AddKey(key2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Term should be 2
	if term := k.ActiveTerm(); term != 2 {
		t.Fatalf("bad: %d", term)
	}

	// Should have key
	newKey := k.ActiveKey()
	if newKey == nil {
		t.Fatalf("bad: %v", key)
	}
	if !bytes.Equal(newKey.Value, testSecond) {
		t.Fatalf("bad: %v", key)
	}
	if tKey := k.TermKey(2); tKey != newKey {
		t.Fatalf("bad: %v", tKey)
	}

	// Read of old key should work
	if tKey := k.TermKey(1); tKey != key {
		t.Fatalf("bad: %v", tKey)
	}

	// Remove the old key
	k, err = k.RemoveKey(1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Read of old key should not work
	if tKey := k.TermKey(1); tKey != nil {
		t.Fatalf("bad: %v", tKey)
	}

	// Remove the active key should fail
	k, err = k.RemoveKey(2)
	if err == nil {
		t.Fatalf("err: %v", err)
	}
}

func TestKeyring_MasterKey(t *testing.T) {
	k := NewKeyring()
	master := []byte("test")
	master2 := []byte("test2")

	// Check no master
	out := k.MasterKey()
	if out != nil {
		t.Fatalf("bad: %v", out)
	}

	// Set master
	k = k.SetMasterKey(master)
	out = k.MasterKey()
	if !bytes.Equal(out, master) {
		t.Fatalf("bad: %v", out)
	}

	// Update master
	k = k.SetMasterKey(master2)
	out = k.MasterKey()
	if !bytes.Equal(out, master2) {
		t.Fatalf("bad: %v", out)
	}
}

func TestKeyring_Serialize(t *testing.T) {
	k := NewKeyring()
	master := []byte("test")
	k = k.SetMasterKey(master)

	now := time.Now()
	testKey := []byte("testing")
	testSecond := []byte("second")
	k, _ = k.AddKey(&Key{Term: 1, Version: 1, Value: testKey, InstallTime: now})
	k, _ = k.AddKey(&Key{Term: 2, Version: 1, Value: testSecond, InstallTime: now})

	buf, err := k.Serialize()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	k2, err := DeserializeKeyring(buf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	out := k2.MasterKey()
	if !bytes.Equal(out, master) {
		t.Fatalf("bad: %v", out)
	}

	if k2.ActiveTerm() != k.ActiveTerm() {
		t.Fatalf("Term mismatch")
	}

	var i uint32
	for i = 1; i < k.ActiveTerm(); i++ {
		key1 := k2.TermKey(i)
		key2 := k.TermKey(i)
		if !reflect.DeepEqual(key1, key2) {
			t.Fatalf("bad: key 1:\n%#v\nkey 2:\n%#v", key1, key2)
		}
	}
}

func TestKey_Serialize(t *testing.T) {
	k := &Key{
		Term:        10,
		Version:     1,
		Value:       []byte("foobarbaz"),
		InstallTime: time.Now(),
	}

	buf, err := k.Serialize()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	out, err := DeserializeKey(buf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(k, out) {
		t.Fatalf("bad: %#v", out)
	}
}
<|endoftext|>


<commit_before>package casbin

import (
	"github.com/lxmgo/config"
	"log"
	"strings"
)

// Model represents the whole access control model.
type Model map[string]AssertionMap

// AssertionMap is the collection of assertions, can be "r", "p", "g", "e", "m".
type AssertionMap map[string]*assertion

func escape(s string) string {
	return strings.Replace(s, ".", "_", -1)
}

var sectionNameMap = map[string]string{
	"r": "request_definition",
	"p": "policy_definition",
	"g": "role_definition",
	"e": "policy_effect",
	"m": "matchers",
}

func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key string) {
	ast := assertion{}
	ast.key = key
	ast.value = cfg.String(sectionNameMap[key] + "::" + key)

	if ast.value == "" {
		return
	}

	if sec == "r" || sec == "p" {
		ast.tokens = strings.Split(ast.value, ", ")
		for i := range ast.tokens {
			ast.tokens[i] = key + "_" + ast.tokens[i]
		}
	} else {
		ast.value = escape(ast.value)
	}

	_, ok := model[sec]
	if !ok {
		model[sec] = make(AssertionMap)
	}

	model[sec][key] = &ast
}

func loadModel(path string) (model Model) {
	cfg, _ := config.NewConfig(path)

	model = make(Model)

	loadAssertion(model, cfg, "r", "r")
	loadAssertion(model, cfg, "p", "p")
	loadAssertion(model, cfg, "e", "e")
	loadAssertion(model, cfg, "m", "m")

	loadAssertion(model, cfg, "g", "g")

	return model
}

func printModel(model Model) {
	log.Print("Model:")
	for k, v := range model {
		for i, j := range v {
			log.Printf("%s.%s: %s", k, i, j.value)
		}
	}
}
<commit_msg>Load all assertions of a section from the model CONF.<commit_after>package casbin

import (
	"github.com/lxmgo/config"
	"log"
	"strconv"
	"strings"
)

// Model represents the whole access control model.
type Model map[string]AssertionMap

// AssertionMap is the collection of assertions, can be "r", "p", "g", "e", "m".
type AssertionMap map[string]*assertion

func escape(s string) string {
	return strings.Replace(s, ".", "_", -1)
}

var sectionNameMap = map[string]string{
	"r": "request_definition",
	"p": "policy_definition",
	"g": "role_definition",
	"e": "policy_effect",
	"m": "matchers",
}

func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key string) bool {
	ast := assertion{}
	ast.key = key
	ast.value = cfg.String(sectionNameMap[sec] + "::" + key)

	if ast.value == "" {
		return false
	}

	if sec == "r" || sec == "p" {
		ast.tokens = strings.Split(ast.value, ", ")
		for i := range ast.tokens {
			ast.tokens[i] = key + "_" + ast.tokens[i]
		}
	} else {
		ast.value = escape(ast.value)
	}

	_, ok := model[sec]
	if !ok {
		model[sec] = make(AssertionMap)
	}

	model[sec][key] = &ast
	return true
}

func getKeySuffix(i int) string {
	if i == 1 {
		return ""
	} else {
		return strconv.Itoa(i)
	}
}

func loadSection(model Model, cfg config.ConfigInterface, sec string) {
	i := 1
	for {
		if !loadAssertion(model, cfg, sec, sec + getKeySuffix(i)) {
			break
		} else {
			i++
		}
	}
}

func loadModel(path string) (model Model) {
	cfg, _ := config.NewConfig(path)

	model = make(Model)

	loadSection(model, cfg, "r")
	loadSection(model, cfg, "p")
	loadSection(model, cfg, "e")
	loadSection(model, cfg, "m")

	loadSection(model, cfg, "g")

	return model
}

func printModel(model Model) {
	log.Print("Model:")
	for k, v := range model {
		for i, j := range v {
			log.Printf("%s.%s: %s", k, i, j.value)
		}
	}
}
<|endoftext|>


<commit_before>package sugarcane

import (
	"bufio"
	"bytes"
	"encoding/gob"
	"os"
	"io/ioutil"
	"io"
)

var Empty = io.EOF

type Database struct {
	File     *os.File
	Filename string
	Cache    *bytes.Buffer
}

// prepare encodes structs into buffers
func prepare(p interface{}) (bytes.Buffer, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(p)
	if err != nil {
		return buf, err
	}
	return buf, nil
}

// write writes data to disk
func (d Database) write(buf bytes.Buffer) error {
	w := bufio.NewWriter(d.File)
	_, err := w.Write(buf.Bytes())
	if err != nil {
		return err
	}
	w.Flush()
	return nil
}

// clanwrite is part of delete, which commits changes to disk
func (d Database) cleanwrite(data []byte) error {
	err := ioutil.WriteFile(d.Filename, data, 0600)
	if err != nil {
		return err
	}
	return nil
}

// binappend appends data to cache
func (d Database) binappend(binary bytes.Buffer) error {
	w := bufio.NewWriter(d.Cache)
	_, err := w.Write(binary.Bytes())
	if err != nil {
		return err
	}
	w.Flush()
	return nil
}

// binremove returns byte array without the binary parameter
func (d Database) binremove(binary bytes.Buffer) ([]byte, error) {
	buf, err := ioutil.ReadFile(d.Filename)
	if err != nil {
		return buf, err
	}
	return bytes.Replace(buf, binary.Bytes(), []byte(""), -1), nil
}

// replenish re-reads the table file and populates the cache
func (d Database) replenish() (*bytes.Buffer, error) {
	buf, err := ioutil.ReadFile(d.Filename)
	if err != nil {
		return bytes.NewBuffer(buf), err
	}
	return bytes.NewBuffer(buf), nil
}

// Open opens a file for writing.
func Open(filename string) (Database, error) {
	var d Database
	w, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		return d, err
	}
	d.File = w
	d.Filename = filename

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return d, err
	}

	// save buffer to cache
	d.Cache = bytes.NewBuffer(buf)
	return d, nil
}

// Insert prepares structure for disk saving.
func (d Database) Insert(p interface{}) error {
	binary, err := prepare(p)
	if err != nil {
		return err
	}
	err = d.binappend(binary)
	if err != nil {
		return err
	}
	d.write(binary)
	return nil
}

// Delete removes structure from disk
func (d Database) Delete(p interface{}) error {
	binary, err := prepare(p)
	if err != nil {
		return err
	}
	data, err := d.binremove(binary)
	if err != nil {
		return err
	}
	err = d.cleanwrite(data)
	if err != nil {
		return err
	}
	return nil
}

// Update removes structure p from disk and inserts p2 to cache and disk
func (d Database) Update(old interface{}, p interface{}) error {
	err := d.Delete(old)
	if err != nil {
		return err
	}
	err = d.Insert(p)
	if err != nil {
		return err
	}
	return nil
}

// Scan returns a first structure from byte buffer and decodes it according given structure.
func (d Database) Scan(p interface{}) (Database, error) {
	dec := gob.NewDecoder(d.Cache)
	err := dec.Decode(p)
	if err == io.EOF {
		d.Cache, err = d.replenish()
		if err != nil {
			return d, err
		}
		return d, Empty
	}
	if err != nil {
		return d, err
	}
	return d, nil
}
<commit_msg>update function documentation<commit_after>package sugarcane

import (
	"bufio"
	"bytes"
	"encoding/gob"
	"io"
	"io/ioutil"
	"os"
)

var Empty = io.EOF

type Database struct {
	File     *os.File
	Filename string
	Cache    *bytes.Buffer
}

// prepare encodes structs into buffers
func prepare(p interface{}) (bytes.Buffer, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(p)
	if err != nil {
		return buf, err
	}
	return buf, nil
}

// write writes data to disk
func (d Database) write(buf bytes.Buffer) error {
	w := bufio.NewWriter(d.File)
	_, err := w.Write(buf.Bytes())
	if err != nil {
		return err
	}
	w.Flush()
	return nil
}

// clanwrite is part of Delete(), which commits changes to disk
func (d Database) cleanwrite(data []byte) error {
	err := ioutil.WriteFile(d.Filename, data, 0600)
	if err != nil {
		return err
	}
	return nil
}

// binappend appends data to cache
func (d Database) binappend(binary bytes.Buffer) error {
	w := bufio.NewWriter(d.Cache)
	_, err := w.Write(binary.Bytes())
	if err != nil {
		return err
	}
	w.Flush()
	return nil
}

// binremove returns altered version of cache with the binary paremeter removed
func (d Database) binremove(binary bytes.Buffer) ([]byte, error) {
	buf, err := ioutil.ReadFile(d.Filename)
	if err != nil {
		return buf, err
	}
	return bytes.Replace(buf, binary.Bytes(), []byte(""), -1), nil
}

// replenish re-reads the table file and populates the cache
func (d Database) replenish() (*bytes.Buffer, error) {
	buf, err := ioutil.ReadFile(d.Filename)
	if err != nil {
		return bytes.NewBuffer(buf), err
	}
	return bytes.NewBuffer(buf), nil
}

// Open opens a database table. If it does not exist, it creates it.
func Open(filename string) (Database, error) {
	var d Database
	w, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		return d, err
	}
	d.File = w
	d.Filename = filename

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return d, err
	}

	// save buffer to cache
	d.Cache = bytes.NewBuffer(buf)
	return d, nil
}

// Insert appends p into cache and disk.
func (d Database) Insert(p interface{}) error {
	binary, err := prepare(p)
	if err != nil {
		return err
	}
	err = d.binappend(binary)
	if err != nil {
		return err
	}
	d.write(binary)
	return nil
}

// Delete removes p from disk.
func (d Database) Delete(p interface{}) error {
	binary, err := prepare(p)
	if err != nil {
		return err
	}
	data, err := d.binremove(binary)
	if err != nil {
		return err
	}
	err = d.cleanwrite(data)
	if err != nil {
		return err
	}
	return nil
}

// Update first deletes old and then appends p to cache and disk.
func (d Database) Update(old interface{}, p interface{}) error {
	err := d.Delete(old)
	if err != nil {
		return err
	}
	err = d.Insert(p)
	if err != nil {
		return err
	}
	return nil
}

// Scan decodes p with the data of d.Cache.
func (d Database) Scan(p interface{}) (Database, error) {
	dec := gob.NewDecoder(d.Cache)
	err := dec.Decode(p)
	if err == io.EOF {
		d.Cache, err = d.replenish()
		if err != nil {
			return d, err
		}
		return d, Empty
	}
	if err != nil {
		return d, err
	}
	return d, nil
}
<|endoftext|>


<commit_before>package scan

import (
	"fmt"
	"gongs/xopen"
	"os"
	"testing"
)

const (
	test_filename = "test_fq.fastq"
	test_name     = "@test"
	test_seq      = "ATCG"
	test_qual     = "@AAA"
)

// func checkFq(fq *Fastq) bool {
// 	if fq.Name != test_fq_name || string(fq.Seq) != string(test_fq_seq) || string(fq.Qual) != string(test_fq_qual) {
// 		return false
// 	}
// 	return true
// }

func create_test_fastq_file(filename string) error {
	o, err := xopen.Xcreate(filename, "w")
	if err != nil {
		return err
	}
	defer o.Close()

	for i := 0; i < 10000; i++ {
		fmt.Fprintf(o, "%s\n%s\n+\n%s\n", test_name, test_seq, test_qual)
	}
	return nil
}

func TestScannerTxt(t *testing.T) {
	if err := create_test_fastq_file(test_filename); err != nil {
		t.Error(err)
		t.Fail()
	}
	defer os.Remove(test_filename)

	file, err := xopen.Xopen(test_filename)
	if err != nil {
		t.Error("TestScanner Xopen file error:", err)
		t.Fail()
	}

	s := New(file)

	var line []byte

	lid := 0
	for s.Scan() {
		line = s.Bytes()
		lid++
		if lid != s.Lid() {
			t.Error("lid error get lid:", lid, "expect:", s.Lid())
		}
		switch lid % 4 {
		case 1:
			if string(line) != test_name {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_name)
			}
		case 2:
			if string(line) != test_seq {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_seq)
			}
		case 3:
			if string(line) != "+" {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", "+")
			}
		case 0:
			if string(line) != test_qual {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_qual)
			}
		}
	}

	if err := s.Err(); err != nil {
		t.Error("TestScanner Error:", err)
	}
}

func TestScannerGz(t *testing.T) {
	if err := create_test_fastq_file(test_filename + ".gz"); err != nil {
		t.Error(err)
		t.Fail()
	}
	//	defer os.Remove(test_filename + ".gz")

	file, err := xopen.Xopen(test_filename + ".gz")
	if err != nil {
		t.Error("TestScanner Xopen file error:", err)
		t.Fail()
	}

	s := New(file)

	var line []byte

	lid := 0
	for s.Scan() {
		line = s.Bytes()
		lid++
		if lid != s.Lid() {
			t.Error("lid error get lid:", lid, "expect:", s.Lid())
		}
		switch lid % 4 {
		case 1:
			if string(line) != test_name {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_name)
			}
		case 2:
			if string(line) != test_seq {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_seq)
			}
		case 3:
			if string(line) != "+" {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", "+")
			}
		case 0:
			if string(line) != test_qual {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_qual)
			}
		}
	}

	if err := s.Err(); err != nil {
		t.Error("TestScanner Error:", err)
	}
}
<commit_msg>update scan_test.go<commit_after>package scan

import (
	"fmt"
	"gongs/xopen"
	"os"
	"testing"
)

const (
	test_filename = "test_fq.fastq"
	test_name     = "@test"
	test_seq      = "ATCG"
	test_qual     = "@AAA"
)

func create_test_fastq_file(filename string) error {
	o, err := xopen.Xcreate(filename, "w")
	if err != nil {
		return err
	}
	defer o.Close()

	for i := 0; i < 10000; i++ {
		fmt.Fprintf(o, "%s\n%s\n+\n%s\n", test_name, test_seq, test_qual)
	}
	return nil
}

func TestScannerTxt(t *testing.T) {
	if err := create_test_fastq_file(test_filename); err != nil {
		t.Error(err)
		t.Fail()
	}
	defer os.Remove(test_filename)

	file, err := xopen.Xopen(test_filename)
	if err != nil {
		t.Error("TestScanner Xopen file error:", err)
		t.Fail()
	}

	s := New(file)
	var line []byte
	lid := 0
	for s.Scan() {
		line = s.Bytes()
		lid++
		if lid != s.Lid() {
			t.Error("lid error get lid:", lid, "expect:", s.Lid())
		}
		switch lid % 4 {
		case 1:
			if string(line) != test_name {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_name)
			}
		case 2:
			if string(line) != test_seq {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_seq)
			}
		case 3:
			if string(line) != "+" {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", "+")
			}
		case 0:
			if string(line) != test_qual {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_qual)
			}
		}
	}

	if err := s.Err(); err != nil {
		t.Error("TestScanner Error:", err)
	}
}

func TestScannerGz(t *testing.T) {
	if err := create_test_fastq_file(test_filename + ".gz"); err != nil {
		t.Error(err)
		t.Fail()
	}
	defer os.Remove(test_filename + ".gz")

	file, err := xopen.Xopen(test_filename + ".gz")
	if err != nil {
		t.Error("TestScanner Xopen file error:", err)
		t.Fail()
	}

	s := New(file)
	var line []byte
	lid := 0
	for s.Scan() {
		line = s.Bytes()
		lid++
		if lid != s.Lid() {
			t.Error("lid error get lid:", lid, "expect:", s.Lid())
		}
		switch lid % 4 {
		case 1:
			if string(line) != test_name {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_name)
			}
		case 2:
			if string(line) != test_seq {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_seq)
			}
		case 3:
			if string(line) != "+" {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", "+")
			}
		case 0:
			if string(line) != test_qual {
				t.Error("lineid:", lid, s.lid, "get line:", string(line), "expect:", test_qual)
			}
		}
	}

	if err := s.Err(); err != nil {
		t.Error("TestScanner Error:", err)
	}
}
<|endoftext|>


<commit_before>package sched

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"strings"

	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

type context struct {
	*State
	Alert *conf.Alert

	schedule *Schedule
}

func (s *Schedule) data(st *State, a *conf.Alert) interface{} {
	return &context{
		State:    st,
		Alert:    a,
		schedule: s,
	}
}

// Ack returns the acknowledge link
func (c *context) Ack() string {
	u := url.URL{
		Scheme: "http",
		Host:   c.schedule.Conf.HttpListen,
		Path:   fmt.Sprintf("/api/acknowledge/%s/%s", c.Alert.Name, c.State.Group.String()),
	}
	if strings.HasPrefix(c.schedule.Conf.HttpListen, ":") {
		h, err := os.Hostname()
		if err != nil {
			return ""
		}
		u.Host = h + u.Host
	}
	return u.String()
}

func (s *Schedule) ExecuteBody(w io.Writer, a *conf.Alert, st *State) error {
	if a.Template == nil || a.Template.Body == nil {
		return nil
	}
	return a.Template.Body.Execute(w, s.data(st, a))
}

func (s *Schedule) ExecuteSubject(w io.Writer, a *conf.Alert, st *State) error {
	if a.Template == nil || a.Template.Subject == nil {
		return nil
	}
	return a.Template.Subject.Execute(w, s.data(st, a))
}

// E executes the given expression and returns a value with corresponding tags
// to the context's tags. If no such result is found, the first result with nil
// tags is returned. If no such result is found, nil is returned. The precision
// of numbers is truncated for convienent display. Array expressions are not
// supported.
func (c *context) E(v string) (s string) {
	e, err := expr.New(v)
	if err != nil {
		return
	}
	res, err := e.Execute(c.schedule.cache, nil)
	if err != nil {
		return
	}
	for _, r := range res {
		if r.Group.Equal(c.State.Group) {
			s = truncate(r.Value)
		}
	}
	for _, r := range res {
		if r.Group == nil {
			s = truncate(r.Value)
		}
	}
	return
}

// truncate displays needed decimals for a Number.
func truncate(v expr.Value) string {
	switch t := v.(type) {
	case expr.Number:
		if t < 1 {
			return fmt.Sprintf("%.4f", t)
		} else if t < 100 {
			return fmt.Sprintf("%.1f", t)
		} else {
			return fmt.Sprintf("%.0f", t)
		}
	default:
		return ""
	}
}
<commit_msg>Function that returns a link to the hostview page (takes the hostname as the argument). Useful for linking to the hostview from alert emails.<commit_after>package sched

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"strings"

	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

type context struct {
	*State
	Alert *conf.Alert

	schedule *Schedule
}

func (s *Schedule) data(st *State, a *conf.Alert) interface{} {
	return &context{
		State:    st,
		Alert:    a,
		schedule: s,
	}
}

// Ack returns the acknowledge link
func (c *context) Ack() string {
	u := url.URL{
		Scheme: "http",
		Host:   c.schedule.Conf.HttpListen,
		Path:   fmt.Sprintf("/api/acknowledge/%s/%s", c.Alert.Name, c.State.Group.String()),
	}
	if strings.HasPrefix(c.schedule.Conf.HttpListen, ":") {
		h, err := os.Hostname()
		if err != nil {
			return ""
		}
		u.Host = h + u.Host
	}
	return u.String()
}

func (c *context) HostView(host string) string {
	u := url.URL{
		Scheme:   "http",
		Host:     c.schedule.Conf.HttpListen,
		Path:     "/host",
		RawQuery: fmt.Sprintf("time=1d-ago&host=%s", host),
	}
	if strings.HasPrefix(c.schedule.Conf.HttpListen, ":") {
		h, err := os.Hostname()
		if err != nil {
			return ""
		}
		u.Host = h + u.Host
	}
	return u.String()
}

func (s *Schedule) ExecuteBody(w io.Writer, a *conf.Alert, st *State) error {
	if a.Template == nil || a.Template.Body == nil {
		return nil
	}
	return a.Template.Body.Execute(w, s.data(st, a))
}

func (s *Schedule) ExecuteSubject(w io.Writer, a *conf.Alert, st *State) error {
	if a.Template == nil || a.Template.Subject == nil {
		return nil
	}
	return a.Template.Subject.Execute(w, s.data(st, a))
}

// E executes the given expression and returns a value with corresponding tags
// to the context's tags. If no such result is found, the first result with nil
// tags is returned. If no such result is found, nil is returned. The precision
// of numbers is truncated for convienent display. Array expressions are not
// supported.
func (c *context) E(v string) (s string) {
	e, err := expr.New(v)
	if err != nil {
		return
	}
	res, err := e.Execute(c.schedule.cache, nil)
	if err != nil {
		return
	}
	for _, r := range res {
		if r.Group.Equal(c.State.Group) {
			s = truncate(r.Value)
		}
	}
	for _, r := range res {
		if r.Group == nil {
			s = truncate(r.Value)
		}
	}
	return
}

// truncate displays needed decimals for a Number.
func truncate(v expr.Value) string {
	switch t := v.(type) {
	case expr.Number:
		if t < 1 {
			return fmt.Sprintf("%.4f", t)
		} else if t < 100 {
			return fmt.Sprintf("%.1f", t)
		} else {
			return fmt.Sprintf("%.0f", t)
		}
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package sconsify

import (
	"fmt"

	sp "github.com/op/go-libspotify/spotify"
)

type Track struct {
	URI string

	Artist     *Artist
	Name       string
	Duration   string
	Album      *Album
	fromWebApi bool
	loadRetry  int
}

func InitPartialTrack(URI string) *Track {
	return &Track{
		URI: URI,
	}
}

func InitTrack(URI string, artist *Artist, name string, duration string) *Track {
	return &Track{
		URI:        URI,
		Artist:     artist,
		Name:       name,
		Duration:   duration,
		fromWebApi: false,
	}
}

func InitWebApiTrack(URI string, artist *Artist, name string, duration string) *Track {
	return &Track{
		URI:        URI,
		Artist:     artist,
		Name:       name,
		Duration:   duration,
		fromWebApi: true,
		loadRetry:  0,
	}
}

func ToSconsifyTrack(track *sp.Track) *Track {
	spArtist := track.Artist(0)
	artist := InitArtist(spArtist.Link().String(), spArtist.Name())
	return InitTrack(track.Link().String(), artist, track.Name(), track.Duration().String())
}

func (track *Track) GetFullTitle() string {
	return fmt.Sprintf("%v - %v [%v]", track.Artist.Name, track.Name, track.Duration)
}

func (track *Track) GetTitle() string {
	return fmt.Sprintf("%v - %v", track.Artist.Name, track.Name)
}

func (track *Track) IsPartial() bool {
	return track.Artist == nil && track.Name == "" && track.Duration == ""
}

func (track *Track) IsFromWebApi() bool {
	return track.fromWebApi
}

func (track *Track) RetryLoading() int {
	track.loadRetry = track.loadRetry + 1
	return track.loadRetry
}
<commit_msg>Show song title + artist<commit_after>package sconsify

import (
	"fmt"

	sp "github.com/op/go-libspotify/spotify"
)

type Track struct {
	URI string

	Artist     *Artist
	Name       string
	Duration   string
	Album      *Album
	fromWebApi bool
	loadRetry  int
}

func InitPartialTrack(URI string) *Track {
	return &Track{
		URI: URI,
	}
}

func InitTrack(URI string, artist *Artist, name string, duration string) *Track {
	return &Track{
		URI:        URI,
		Artist:     artist,
		Name:       name,
		Duration:   duration,
		fromWebApi: false,
	}
}

func InitWebApiTrack(URI string, artist *Artist, name string, duration string) *Track {
	return &Track{
		URI:        URI,
		Artist:     artist,
		Name:       name,
		Duration:   duration,
		fromWebApi: true,
		loadRetry:  0,
	}
}

func ToSconsifyTrack(track *sp.Track) *Track {
	spArtist := track.Artist(0)
	artist := InitArtist(spArtist.Link().String(), spArtist.Name())
	return InitTrack(track.Link().String(), artist, track.Name(), track.Duration().String())
}

func (track *Track) GetFullTitle() string {
	return fmt.Sprintf("%v - %v [%v]", track.Artist.Name, track.Name, track.Duration)
}

func (track *Track) GetTitle() string {
	return fmt.Sprintf("%v - %v", track.Name, track.Artist.Name)
}

func (track *Track) IsPartial() bool {
	return track.Artist == nil && track.Name == "" && track.Duration == ""
}

func (track *Track) IsFromWebApi() bool {
	return track.fromWebApi
}

func (track *Track) RetryLoading() int {
	track.loadRetry = track.loadRetry + 1
	return track.loadRetry
}
<|endoftext|>


<commit_before>package rpcfs

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

var _ = fmt.Println

type FsServer struct {
	Root string
}

type Stamp struct {
	Ctime int64
	Mtime int64
}

// TODO - should send timestamps for returns?
type ContentRequest struct {
	Name string
}
type ContentResponse struct {
	Stamp
	Chunks [][]byte
}

type DirRequest struct {
	Name string
}

type DirResponse struct {
	Stamp
	Data     map[string]*os.FileInfo
	Symlinks map[string]string
}

func (me *FsServer) getPath(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) ReadDir(req *DirRequest, r *DirResponse) (os.Error) {
	d, e :=  ioutil.ReadDir(me.getPath(req.Name))
	log.Println("ReadDir", req)
	r.Data = make(map[string]*os.FileInfo)
	r.Symlinks = make(map[string]string)
	for _, v := range d {
		r.Data[v.Name] = v
		if v.IsSymlink() {
			// TODO - error handling.
			r.Symlinks[v.Name], _ = os.Readlink(filepath.Join(req.Name, v.Name))
		}
	}

	me.stamp(req.Name, &r.Stamp)
	return e
}

func (me *FsServer) stamp(name string, st *Stamp) os.Error {
	fi, err := os.Stat(me.getPath(name))
	if fi != nil {
		st.Ctime = fi.Ctime_ns
		st.Mtime = fi.Mtime_ns
	}
	return err
}

func ReadChunks(r io.Reader, size int) (chunks [][]byte, err os.Error) {
	for {
		chunk := make([]byte, size)
		n, err := r.Read(chunk)
		if n > 0 {
			chunk = chunk[:n]
			chunks = append(chunks, chunk)
		}
		if err == os.EOF {
			err = nil
			break
		}
		if err != nil || n == 0 {
			break
		}
	}
	return chunks, err
}

func (me *FsServer) FileContent(req *DirRequest, rep *ContentResponse) (os.Error) {
	log.Println("FileContent", req)

	f, err := os.Open(me.getPath(req.Name))
	if err != nil {
		return err
	}
	defer f.Close()

	chunksize := 128 * (1<<10)
	chunks, err := ReadChunks(f, chunksize)

	if err == nil {
		err = me.stamp(req.Name, &rep.Stamp)
		rep.Chunks = chunks
	}
	return err
}
<commit_msg>drop TODO<commit_after>package rpcfs

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

var _ = fmt.Println

type FsServer struct {
	Root string
}

type Stamp struct {
	Ctime int64
	Mtime int64
}

type ContentRequest struct {
	Name string
}
type ContentResponse struct {
	Stamp
	Chunks [][]byte
}

type DirRequest struct {
	Name string
}

type DirResponse struct {
	Stamp
	Data     map[string]*os.FileInfo
	Symlinks map[string]string
}

func (me *FsServer) getPath(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) ReadDir(req *DirRequest, r *DirResponse) (os.Error) {
	d, e :=  ioutil.ReadDir(me.getPath(req.Name))
	log.Println("ReadDir", req)
	r.Data = make(map[string]*os.FileInfo)
	r.Symlinks = make(map[string]string)
	for _, v := range d {
		r.Data[v.Name] = v
		if v.IsSymlink() {
			// TODO - error handling.
			r.Symlinks[v.Name], _ = os.Readlink(filepath.Join(req.Name, v.Name))
		}
	}

	me.stamp(req.Name, &r.Stamp)
	return e
}

func (me *FsServer) stamp(name string, st *Stamp) os.Error {
	fi, err := os.Stat(me.getPath(name))
	if fi != nil {
		st.Ctime = fi.Ctime_ns
		st.Mtime = fi.Mtime_ns
	}
	return err
}

func ReadChunks(r io.Reader, size int) (chunks [][]byte, err os.Error) {
	for {
		chunk := make([]byte, size)
		n, err := r.Read(chunk)
		if n > 0 {
			chunk = chunk[:n]
			chunks = append(chunks, chunk)
		}
		if err == os.EOF {
			err = nil
			break
		}
		if err != nil || n == 0 {
			break
		}
	}
	return chunks, err
}

func (me *FsServer) FileContent(req *DirRequest, rep *ContentResponse) (os.Error) {
	log.Println("FileContent", req)

	f, err := os.Open(me.getPath(req.Name))
	if err != nil {
		return err
	}
	defer f.Close()

	chunksize := 128 * (1<<10)
	chunks, err := ReadChunks(f, chunksize)

	if err == nil {
		err = me.stamp(req.Name, &rep.Stamp)
		rep.Chunks = chunks
	}
	return err
}
<|endoftext|>


<commit_before>// Package rundeck provides a client for interacting with a Rundeck instance
// via its HTTP API.
//
// Instantiate a Client with the NewClient function to get started.
//
// At present this package uses Rundeck API version 13.
package rundeck

import (
	"bytes"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"mime/multipart"
	"strings"
)

// ClientConfig is used with NewClient to specify initialization settings.
type ClientConfig struct {
	// The base URL of the Rundeck instance.
	BaseURL string

	// The API auth token generated from user settings in the Rundeck UI.
	AuthToken string

	// Don't fail if the server uses SSL with an un-verifiable certificate.
	// This is not recommended except during development/debugging.
	AllowUnverifiedSSL bool
}

// Client is a Rundeck API client interface.
type Client struct {
	httpClient *http.Client
	apiURL     *url.URL
	authToken  string
}

// NewClient returns a configured Rundeck client.
func NewClient(config *ClientConfig) (*Client, error) {
	t := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: config.AllowUnverifiedSSL,
		},
	}
	httpClient := &http.Client{
		Transport: t,
	}

	apiPath, _ := url.Parse("api/13/")
	baseURL, err := url.Parse(config.BaseURL)
	if err != nil {
		return nil, fmt.Errorf("invalid base URL: %s", err.Error())
	}
	apiURL := baseURL.ResolveReference(apiPath)

	return &Client{
		httpClient: httpClient,
		apiURL:     apiURL,
		authToken:  config.AuthToken,
	}, nil
}

func (c *Client) request(method string, pathParts []string, query map[string]string, reqBody interface{}, result interface{}) error {

	req := &http.Request{
		Method: method,
		Header: http.Header{},
	}
	req.Header.Add("User-Agent", "Go-Rundeck-API")
	req.Header.Add("X-Rundeck-Auth-Token", c.authToken)
	req.Header.Add("Accept", "application/xml")

	urlPath := &url.URL{
		Path: strings.Join(pathParts, "/"),
	}
	reqURL := c.apiURL.ResolveReference(urlPath)
	req.URL = reqURL

	if len(query) > 0 {
		urlQuery := url.Values{}
		for k, v := range query {
			urlQuery.Add(k, v)
		}
		reqURL.RawQuery = urlQuery.Encode()
	}

	if reqBody != nil {
		reqBodyBytes, err := xml.Marshal(reqBody)
		if err != nil {
			return err
		}

		req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBodyBytes))
		req.ContentLength = int64(len(reqBodyBytes))
		req.Header.Add("Content-Type", "application/xml")
	}

	res, err := c.httpClient.Do(req)

	if err != nil {
		return err
	}

	resBodyBytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		if strings.HasPrefix(res.Header.Get("Content-Type"), "text/xml") {
			var richErr Error
			err = xml.Unmarshal(resBodyBytes, &richErr)
			if err != nil {
				return fmt.Errorf("HTTP Error %i with error decoding XML body: %s", res.StatusCode, err.Error())
			}
			return richErr
		}

		return fmt.Errorf("HTTP Error %i", res.StatusCode)
	}

	if result != nil {
		if res.StatusCode != 200 && res.StatusCode != 201 {
			return fmt.Errorf("server did not return an XML payload")
		}
		err = xml.Unmarshal(resBodyBytes, result)
		if err != nil {
			return fmt.Errorf("error decoding response XML payload: %s", err.Error())
		}
	}

	return nil
}

func (c *Client) get(pathParts []string, query map[string]string, result interface{}) error {
	return c.request("GET", pathParts, query, nil, result)
}

func (c *Client) post(pathParts []string, query map[string]string, reqBody interface{}, result interface{}) error {
	return c.request("POST", pathParts, query, reqBody, result)
}

func (c *Client) put(pathParts []string, reqBody interface{}, result interface{}) error {
	return c.request("PUT", pathParts, nil, reqBody, result)
}

func (c *Client) delete(pathParts []string) error {
	return c.request("DELETE", pathParts, nil, nil, nil)
}

func (c *Client) postXMLBatch(pathParts []string, args map[string]string, xmlBatch interface{}, result interface{}) error {
	req := &http.Request{
		Method: "POST",
		Header: http.Header{},
	}
	req.Header.Add("User-Agent", "Go-Rundeck-API")
	req.Header.Add("X-Rundeck-Auth-Token", c.authToken)

	urlPath := &url.URL{
		Path: strings.Join(pathParts, "/"),
	}
	reqURL := c.apiURL.ResolveReference(urlPath)
	req.URL = reqURL

	buf := bytes.Buffer{}
	writer := multipart.NewWriter(&buf)
	for k, v := range args {
		err := writer.WriteField(k, v)
		if err != nil {
			return err
		}
	}
	partWriter, err := writer.CreateFormFile("xmlBatch", "batch.xml")
	if err != nil {
		return err
	}

	reqBodyBytes, err := xml.Marshal(xmlBatch)
	if err != nil {
		return err
	}

	_, err = partWriter.Write(reqBodyBytes)
	if err != nil {
		return err
	}

	writer.Close()

	reqBodyReader := bytes.NewReader(buf.Bytes())
	req.Body = ioutil.NopCloser(reqBodyReader)
	req.ContentLength = int64(buf.Len())
	req.Header.Add("Content-Type", writer.FormDataContentType())

	res, err := c.httpClient.Do(req)

	if err != nil {
		return err
	}

	resBodyBytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		if strings.HasPrefix(res.Header.Get("Content-Type"), "text/xml") {
			var richErr Error
			err = xml.Unmarshal(resBodyBytes, &richErr)
			if err != nil {
				return fmt.Errorf("HTTP Error %i with error decoding XML body: %s", res.StatusCode, err.Error())
			}
			return richErr
		}

		return fmt.Errorf("HTTP Error %i", res.StatusCode)
	}

	if result != nil {
		if res.StatusCode != 200 && res.StatusCode != 201 {
			return fmt.Errorf("server did not return an XML payload")
		}
		err = xml.Unmarshal(resBodyBytes, result)
		if err != nil {
			return fmt.Errorf("error decoding response XML payload: %s", err.Error())
		}
	}

	return nil
}
<commit_msg>Refactor request handling to make non-XML requests easier.<commit_after>// Package rundeck provides a client for interacting with a Rundeck instance
// via its HTTP API.
//
// Instantiate a Client with the NewClient function to get started.
//
// At present this package uses Rundeck API version 13.
package rundeck

import (
	"bytes"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"mime/multipart"
	"strings"
)

// ClientConfig is used with NewClient to specify initialization settings.
type ClientConfig struct {
	// The base URL of the Rundeck instance.
	BaseURL string

	// The API auth token generated from user settings in the Rundeck UI.
	AuthToken string

	// Don't fail if the server uses SSL with an un-verifiable certificate.
	// This is not recommended except during development/debugging.
	AllowUnverifiedSSL bool
}

// Client is a Rundeck API client interface.
type Client struct {
	httpClient *http.Client
	apiURL     *url.URL
	authToken  string
}

type request struct {
	Method string
	PathParts []string
	QueryArgs map[string]string
	Headers map[string]string
	BodyBytes []byte
}

// NewClient returns a configured Rundeck client.
func NewClient(config *ClientConfig) (*Client, error) {
	t := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: config.AllowUnverifiedSSL,
		},
	}
	httpClient := &http.Client{
		Transport: t,
	}

	apiPath, _ := url.Parse("api/13/")
	baseURL, err := url.Parse(config.BaseURL)
	if err != nil {
		return nil, fmt.Errorf("invalid base URL: %s", err.Error())
	}
	apiURL := baseURL.ResolveReference(apiPath)

	return &Client{
		httpClient: httpClient,
		apiURL:     apiURL,
		authToken:  config.AuthToken,
	}, nil
}

func (c *Client) rawRequest(req *request) ([]byte, error) {
	res, err := c.httpClient.Do(req.MakeHTTPRequest(c))
	if err != nil {
		return nil, err
	}

	resBodyBytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		if strings.HasPrefix(res.Header.Get("Content-Type"), "text/xml") {
			var richErr Error
			err = xml.Unmarshal(resBodyBytes, &richErr)
			if err != nil {
				return nil, fmt.Errorf("HTTP Error %i with error decoding XML body: %s", res.StatusCode, err.Error())
			}
			return nil, richErr
		}

		return nil, fmt.Errorf("HTTP Error %i", res.StatusCode)
	}

	if res.StatusCode != 200 && res.StatusCode != 201 {
		return nil, nil
	}

	return resBodyBytes, nil
}

func (c *Client) xmlRequest(method string, pathParts []string, query map[string]string, reqBody interface{}, result interface{}) error {

	var err error
	var reqBodyBytes []byte
	reqBodyBytes = nil
	if reqBody != nil {
		reqBodyBytes, err = xml.Marshal(reqBody)
		if err != nil {
			return err
		}
	}

	req := &request{
		Method: method,
		PathParts: pathParts,
		QueryArgs: query,
		BodyBytes: reqBodyBytes,
		Headers: map[string]string{
			"Accept": "application/xml",
		},
	}

	if reqBody != nil {
		req.Headers["Content-Type"] = "application/xml"
	}

	resBodyBytes, err := c.rawRequest(req)
	if err != nil {
		return err
	}

	if result != nil {
		if resBodyBytes == nil {
			return fmt.Errorf("server did not return an XML payload")
		}
		err = xml.Unmarshal(resBodyBytes, result)
		if err != nil {
			return fmt.Errorf("error decoding response XML payload: %s", err.Error())
		}
	}

	return nil
}

func (c *Client) get(pathParts []string, query map[string]string, result interface{}) error {
	return c.xmlRequest("GET", pathParts, query, nil, result)
}

func (c *Client) rawGet(pathParts []string, query map[string]string, accept string) (string, error) {
	req := &request{
		Method: "GET",
		PathParts: pathParts,
		QueryArgs: query,
		Headers: map[string]string{
			"Accept": accept,
		},
	}

	resBodyBytes, err := c.rawRequest(req)
	if err != nil {
		return "", err
	}

	return string(resBodyBytes), nil
}

func (c *Client) post(pathParts []string, query map[string]string, reqBody interface{}, result interface{}) error {
	return c.xmlRequest("POST", pathParts, query, reqBody, result)
}

func (c *Client) put(pathParts []string, reqBody interface{}, result interface{}) error {
	return c.xmlRequest("PUT", pathParts, nil, reqBody, result)
}

func (c *Client) delete(pathParts []string) error {
	return c.xmlRequest("DELETE", pathParts, nil, nil, nil)
}

func (c *Client) postXMLBatch(pathParts []string, args map[string]string, xmlBatch interface{}, result interface{}) error {
	req := &http.Request{
		Method: "POST",
		Header: http.Header{},
	}
	req.Header.Add("User-Agent", "Go-Rundeck-API")
	req.Header.Add("X-Rundeck-Auth-Token", c.authToken)

	urlPath := &url.URL{
		Path: strings.Join(pathParts, "/"),
	}
	reqURL := c.apiURL.ResolveReference(urlPath)
	req.URL = reqURL

	buf := bytes.Buffer{}
	writer := multipart.NewWriter(&buf)
	for k, v := range args {
		err := writer.WriteField(k, v)
		if err != nil {
			return err
		}
	}
	partWriter, err := writer.CreateFormFile("xmlBatch", "batch.xml")
	if err != nil {
		return err
	}

	reqBodyBytes, err := xml.Marshal(xmlBatch)
	if err != nil {
		return err
	}

	_, err = partWriter.Write(reqBodyBytes)
	if err != nil {
		return err
	}

	writer.Close()

	reqBodyReader := bytes.NewReader(buf.Bytes())
	req.Body = ioutil.NopCloser(reqBodyReader)
	req.ContentLength = int64(buf.Len())
	req.Header.Add("Content-Type", writer.FormDataContentType())

	res, err := c.httpClient.Do(req)

	if err != nil {
		return err
	}

	resBodyBytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		if strings.HasPrefix(res.Header.Get("Content-Type"), "text/xml") {
			var richErr Error
			err = xml.Unmarshal(resBodyBytes, &richErr)
			if err != nil {
				return fmt.Errorf("HTTP Error %i with error decoding XML body: %s", res.StatusCode, err.Error())
			}
			return richErr
		}

		return fmt.Errorf("HTTP Error %i", res.StatusCode)
	}

	if result != nil {
		if res.StatusCode != 200 && res.StatusCode != 201 {
			return fmt.Errorf("server did not return an XML payload")
		}
		err = xml.Unmarshal(resBodyBytes, result)
		if err != nil {
			return fmt.Errorf("error decoding response XML payload: %s", err.Error())
		}
	}

	return nil
}

func (r *request) MakeHTTPRequest(client *Client) *http.Request {
	req := &http.Request{
		Method: r.Method,
		Header: http.Header{},
	}

	// Automatic/mandatory HTTP headers first
	req.Header.Add("User-Agent", "Go-Rundeck-API")
	req.Header.Add("X-Rundeck-Auth-Token", client.authToken)

	for k, v := range r.Headers {
		req.Header.Add(k, v)
	}

	urlPath := &url.URL{
		Path: strings.Join(r.PathParts, "/"),
	}
	reqURL := client.apiURL.ResolveReference(urlPath)
	req.URL = reqURL

	if len(r.QueryArgs) > 0 {
		urlQuery := url.Values{}
		for k, v := range r.QueryArgs {
			urlQuery.Add(k, v)
		}
		reqURL.RawQuery = urlQuery.Encode()
	}

	if r.BodyBytes != nil {
		req.Body = ioutil.NopCloser(bytes.NewReader(r.BodyBytes))
		req.ContentLength = int64(len(r.BodyBytes))
	}

	return req
}
<|endoftext|>


<commit_before>package checkToken

import (
	"github.com/AitorGuerrero/UserGo/user"
	userImplementation "github.com/AitorGuerrero/UserGo/implementation/user"

	t "testing"
)

var id = "dabfe523-fae0-4a1c-8923-5e51ffeb3e91"
var userSource userImplementation.Source
var userFactory user.Factory
var com Command
var req Request
var parsedId user.Id
var passkeyEncryptor = user.PasskeyEncryptor{userImplementation.Encryptor{}}
var tokenChecker = user.TokenChecker{}

func beforeEach() {
	parsedId, _ = user.ParseId(id)
	userSource = userImplementation.Source{map[string]*user.User{}}
	userFactory = user.Factory{passkeyEncryptor}
	com = Command{tokenChecker, &userSource}
	req = Request{}
}

func TestIfTheUserDoesNotExistsShouldReturnAnError (t *t.T) {
	beforeEach()
	req.Token = "PisToken"
	err := com.Execute(req)
	if _, ok := err.(InvalidIdError); !ok {
		t.Error(err)
	}
}

func TestIfTheTokenIsInvalidShouldReturnAnError (t *t.T) {
	beforeEach()
	createUser()
	req.Token = "PisToken"
	req.UserId = id
	err := com.Execute(req)
	if _, ok := err.(IncorrectTokenError); !ok {
		t.Error(err)
	}
}

func TestIfTheUserHasNotAccessToNamespaceHouldReturnError (t *t.T) {
	beforeEach()
	u := createUser()
	req.Token = string(u.Token().Code)
	req.UserId = id
	err := com.Execute(req)
	if _, ok := err.(AccessErrorToNamespace); !ok {
		t.Error(err)
	}
}

func createUser() (u user.User) {
	u = userFactory.Make(parsedId, "passkey")
	u.GenerateToken()
	userSource.Add(&u)

	return
}
<commit_msg>Token check command, ok test<commit_after>package checkToken

import (
	"github.com/AitorGuerrero/UserGo/user"
	userImplementation "github.com/AitorGuerrero/UserGo/implementation/user"

	t "testing"
)

var id = "dabfe523-fae0-4a1c-8923-5e51ffeb3e91"
var testNameSpace = "/test/namespace"
var userSource userImplementation.Source
var userFactory user.Factory
var com Command
var req Request
var parsedId user.Id
var passkeyEncryptor = user.PasskeyEncryptor{userImplementation.Encryptor{}}
var tokenChecker = user.TokenChecker{}

func beforeEach() {
	parsedId, _ = user.ParseId(id)
	userSource = userImplementation.Source{map[string]*user.User{}}
	userFactory = user.Factory{passkeyEncryptor}
	com = Command{tokenChecker, &userSource}
	req = Request{}
}

func TestIfTheUserDoesNotExistsShouldReturnAnError (t *t.T) {
	beforeEach()
	req.Token = "PisToken"
	err := com.Execute(req)
	if _, ok := err.(InvalidIdError); !ok {
		t.Error(err)
	}
}

func TestIfTheTokenIsInvalidShouldReturnAnError (t *t.T) {
	beforeEach()
	createUser()
	req.Token = "FakeToken"
	req.UserId = id
	err := com.Execute(req)
	if _, ok := err.(IncorrectTokenError); !ok {
		t.Error(err)
	}
}

func TestIfTheUserHasNotAccessToNamespaceShouldReturnError (t *t.T) {
	beforeEach()
	u := createUser()
	req.Token = string(u.Token().Code)
	req.UserId = id
	req.Namespace = "/fake/Namespace"
	err := com.Execute(req)
	if _, ok := err.(AccessErrorToNamespace); !ok {
		t.Error(err)
	}
}

func TestIfTokenIsCorrectShouldNotResturnError(t *t.T) {
	beforeEach()
	u := createUser()
	req.Token = string(u.Token().Code)
	req.UserId = id
	req.Namespace = testNameSpace
	err := com.Execute(req)
	if nil != err {
		t.Error(err)
	}
}

func createUser() (u user.User) {
	u = userFactory.Make(parsedId, "passkey")
	u.GenerateToken()
	u.GrantAccessTo(user.Namespace(testNameSpace))
	userSource.Add(&u)

	return
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package semtech

import (
	. "github.com/thethingsnetwork/core/utils/testing"
	"testing"
)

func TestNewAdapter(t *testing.T) {
	Ok(t, "pending")
}

func TestSend(t *testing.T) {
	Ok(t, "pending")
}

func TestNextRegistration(t *testing.T) {
	Ok(t, "pending")
}

func TestNext(t *testing.T) {
	Ok(t, "pending")
}
<commit_msg>[broker] Write tests for unsupported methods<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package semtech

import (
	"github.com/thethingsnetwork/core"
	. "github.com/thethingsnetwork/core/utils/testing"
	"testing"
)

func TestNewAdapter(t *testing.T) {
	Ok(t, "pending")
}

func TestSend(t *testing.T) {
	Desc(t, "Send is not supported")
	adapter, err := NewAdapter(33000)
	if err != nil {
		panic(err)
	}
	err = adapter.Send(core.Packet{})
	checkErrors(t, ErrNotSupported, err)
}

func TestNextRegistration(t *testing.T) {
	Desc(t, "Next registration is not supported")
	adapter, err := NewAdapter(33001)
	if err != nil {
		panic(err)
	}
	_, _, err = adapter.NextRegistration()
	checkErrors(t, ErrNotSupported, err)
}

func TestNext(t *testing.T) {
	Ok(t, "pending")
}

func checkErrors(t *testing.T, want error, got error) {
	if want == got {
		Ok(t, "Check errors")
		return
	}
	Ko(t, "Expected error to be %v but got %v", want, got)
}
<|endoftext|>


<commit_before>package logger

import (
	"io"
	"os"

	"context"

	"sync"

	"cloud.google.com/go/errorreporting"
	"github.com/mgutz/logxi/v1"
	"github.com/pkg/errors"
)

// UnaLogger wraps a logxi logger
// and delegate to some of it's logging methods
type UnaLogger interface {
	Debug(msg string, args ...interface{})
	Info(msg string, args ...interface{})
	Error(msg string, err error, args ...interface{})
	Fatal(msg string, err error, args ...interface{})
	Underlying() log.Logger
}

type unaLogger struct {
	Logger log.Logger
	name   string
}

// Config contains Name and FileName for the logger
type Config struct {
	Name     string
	FileName string
}

// Keep a list of loggers that we can use in the SetLevel func
var loggers []UnaLogger

var errorClient *errorreporting.Client

// logger for internal use
var lgr UnaLogger

// ExitOnPanic makes it possible to disable exiting in ReportPanics
var ExitOnPanic = true

// InitErrorReporting will enable the errors of all calls to Error and Fatal to be sent to Google Error Reporting
// It also enables the
func InitErrorReporting(ctx context.Context, projectID, serviceName, serviceVersion string) error {
	lgr = New("unalogger")
	client, err := errorreporting.NewClient(ctx, projectID,
		errorreporting.Config{
			ServiceName:    serviceName,
			ServiceVersion: serviceVersion})
	if err != nil {
		return err
	}

	errorClient = client

	return nil
}

// ReportPanics should be defered in every new scope where you want to catch pancis and have them pass on to Stackdriver
// Error Reporting
func ReportPanics(ctx context.Context) func() {
	return func() {
		if errorClient == nil {
			panic("The errorClient was nil, initialize it with InitErrorReporting before deferring this function")
		}
		x := recover()
		if x == nil {
			return
		}
		switch e := x.(type) {
		case string:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: errors.New(e)})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		case error:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: e})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		default:
			panic(x)
		}
		// exits with a non-zero code so the app execution stops
		if ExitOnPanic {
			os.Exit(1337)
		}
	}
}

// CloseClient should be deferred right after calling InitErrorReporting to enure that the client is
// closed down gracefully
func CloseClient() {
	if errorClient == nil {
		panic("The errorClient was nil, initialize it with InitErrorReporting before deferring this function")
	}
	var _ = errorClient.Close() // Ignoring this error
}

// Deprecated: The functionality is split into InitErrorReporting, ReportPanics and CloseClient instead
// SetUpErrorReporting creates an ErrorReporting client and returns that client together with a reportPanics function.
// That function should be defered in every new scope where you want to catch pancis and have them pass on to Stackdriver
// Error Reporting
func SetUpErrorReporting(ctx context.Context, projectID, serviceName, serviceVersion string) (client *errorreporting.Client, reportPanics func()) {
	lgr := New("errorreporting")
	errClient := InitErrorReporting(ctx, projectID, serviceName, serviceVersion)
	if errClient != nil {
		lgr.Fatal("Couldn't create an errorreporting client", errClient)
	}
	return errorClient, func() {
		x := recover()
		if x == nil {
			return
		}
		switch e := x.(type) {
		case string:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: errors.New(e)})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		}
		// repanics so the app execution stops
		lgr.Info("Re-panicking ", "err", x)
		panic(x)
	}
}

func reportError(err error) {
	if errorClient != nil {
		errorClient.Report(errorreporting.Entry{
			Error: err,
		})
	}
}

var defaultsSet bool
var mutex = sync.Mutex{}

func setDefaults() {
	// These configurations are made to make the
	// log payload compatible with the LogEntry format used in Google Cloud Logging
	// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
	log.KeyMap.Level = "severity"
	log.KeyMap.Message = "message"
	log.KeyMap.Time = "timestamp"
	log.LevelMap[log.LevelFatal] = "CRITICAL"
	log.LevelMap[log.LevelError] = "ERROR"
	log.LevelMap[log.LevelInfo] = "INFO"
	log.LevelMap[log.LevelDebug] = "DEBUG"
	defaultsSet = true
}

// New creates a new logger with the given (string) name
func New(name string) UnaLogger {
	return NewLogger(Config{Name: name})
}

// NewLogger creates a new logger with the given (Config) name
func NewLogger(conf Config) UnaLogger {

	logxiLogger := log.New(conf.Name)
	if conf.FileName != "" {
		if file, err := os.Create(conf.FileName); err == nil {
			logxiLogger = log.NewLogger(file, conf.Name)
		}
	}
	unaLogger := &unaLogger{
		Logger: logxiLogger,
		name:   conf.Name,
	}

	// Add the logger to the list of loggers and set some defaults
	// Needs to use a mutex here so loggers can be created in different goroutines
	mutex.Lock()
	loggers = append(loggers, unaLogger)
	if !defaultsSet {
		setDefaults()
	}
	mutex.Unlock()

	return unaLogger
}

// SetWriter overrides the io.Writer of the underlying logxi logger
func (ul *unaLogger) SetWriter(writer io.Writer) {
	ul.Logger = log.NewLogger(writer, ul.name)
}

// Underlying returns the underlying logxi logger
func (ul unaLogger) Underlying() log.Logger {
	return ul.Logger
}

// Info logs to Stdout with an "INFO" prefix
func (ul unaLogger) Info(msg string, args ...interface{}) {
	ul.Logger.Info(msg, args...)
}

// Debug logs to Stdout with an "DEBUG" prefix if Debug level is enabled
func (ul unaLogger) Debug(msg string, args ...interface{}) {
	if ul.Logger.IsDebug() {
		ul.Logger.Debug(msg, args...)
	}
}

// Error logs to Stdout with an "Error" prefix
// It also adds an "error" key to the provided err(error) argument
func (ul unaLogger) Error(msg string, err error, args ...interface{}) {
	reportError(err)
	_ = ul.Logger.Error(msg, appendErrorToArgs(args, err)...)
}

// Fatal logs to Stdout with an "Fatal" prefix
// It also adds an "error" key to the provided err(error) argument
func (ul unaLogger) Fatal(msg string, err error, args ...interface{}) {
	if errorClient != nil {
		defer ReportPanics(context.Background())()
		// Logging at level Fatal without the panic since we have reported to Errorreporting already
		ul.Logger.Log(log.LevelFatal, msg, appendErrorToArgs(args, err))
	}
	ul.Logger.Fatal(msg, appendErrorToArgs(args, err))
}

func appendErrorToArgs(args []interface{}, err error) []interface{} {
	return append(args, "error", err)
}
<commit_msg>Inline the reportError function<commit_after>package logger

import (
	"io"
	"os"

	"context"

	"sync"

	"cloud.google.com/go/errorreporting"
	"github.com/mgutz/logxi/v1"
	"github.com/pkg/errors"
)

// UnaLogger wraps a logxi logger
// and delegate to some of it's logging methods
type UnaLogger interface {
	Debug(msg string, args ...interface{})
	Info(msg string, args ...interface{})
	Error(msg string, err error, args ...interface{})
	Fatal(msg string, err error, args ...interface{})
	Underlying() log.Logger
}

type unaLogger struct {
	Logger log.Logger
	name   string
}

// Config contains Name and FileName for the logger
type Config struct {
	Name     string
	FileName string
}

// Keep a list of loggers that we can use in the SetLevel func
var loggers []UnaLogger

var errorClient *errorreporting.Client

// logger for internal use
var lgr UnaLogger

// ExitOnPanic makes it possible to disable exiting in ReportPanics
var ExitOnPanic = true

// InitErrorReporting will enable the errors of all calls to Error and Fatal to be sent to Google Error Reporting
// It also enables the
func InitErrorReporting(ctx context.Context, projectID, serviceName, serviceVersion string) error {
	lgr = New("unalogger")
	client, err := errorreporting.NewClient(ctx, projectID,
		errorreporting.Config{
			ServiceName:    serviceName,
			ServiceVersion: serviceVersion})
	if err != nil {
		return err
	}

	errorClient = client

	return nil
}

// ReportPanics should be defered in every new scope where you want to catch pancis and have them pass on to Stackdriver
// Error Reporting
func ReportPanics(ctx context.Context) func() {
	return func() {
		if errorClient == nil {
			panic("The errorClient was nil, initialize it with InitErrorReporting before deferring this function")
		}
		x := recover()
		if x == nil {
			return
		}
		switch e := x.(type) {
		case string:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: errors.New(e)})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		case error:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: e})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		default:
			panic(x)
		}
		// exits with a non-zero code so the app execution stops
		if ExitOnPanic {
			os.Exit(1337)
		}
	}
}

// CloseClient should be deferred right after calling InitErrorReporting to enure that the client is
// closed down gracefully
func CloseClient() {
	if errorClient == nil {
		panic("The errorClient was nil, initialize it with InitErrorReporting before deferring this function")
	}
	var _ = errorClient.Close() // Ignoring this error
}

// Deprecated: The functionality is split into InitErrorReporting, ReportPanics and CloseClient instead
// SetUpErrorReporting creates an ErrorReporting client and returns that client together with a reportPanics function.
// That function should be defered in every new scope where you want to catch pancis and have them pass on to Stackdriver
// Error Reporting
func SetUpErrorReporting(ctx context.Context, projectID, serviceName, serviceVersion string) (client *errorreporting.Client, reportPanics func()) {
	lgr := New("errorreporting")
	errClient := InitErrorReporting(ctx, projectID, serviceName, serviceVersion)
	if errClient != nil {
		lgr.Fatal("Couldn't create an errorreporting client", errClient)
	}
	return errorClient, func() {
		x := recover()
		if x == nil {
			return
		}
		switch e := x.(type) {
		case string:
			err := errorClient.ReportSync(ctx, errorreporting.Entry{Error: errors.New(e)})
			if err != nil {
				lgr.Error("Couldn't do a ReportSync to Stackdriver Error Reporting", err)
			}
		}
		// repanics so the app execution stops
		lgr.Info("Re-panicking ", "err", x)
		panic(x)
	}
}

var defaultsSet bool
var mutex = sync.Mutex{}

func setDefaults() {
	// These configurations are made to make the
	// log payload compatible with the LogEntry format used in Google Cloud Logging
	// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
	log.KeyMap.Level = "severity"
	log.KeyMap.Message = "message"
	log.KeyMap.Time = "timestamp"
	log.LevelMap[log.LevelFatal] = "CRITICAL"
	log.LevelMap[log.LevelError] = "ERROR"
	log.LevelMap[log.LevelInfo] = "INFO"
	log.LevelMap[log.LevelDebug] = "DEBUG"
	defaultsSet = true
}

// New creates a new logger with the given (string) name
func New(name string) UnaLogger {
	return NewLogger(Config{Name: name})
}

// NewLogger creates a new logger with the given (Config) name
func NewLogger(conf Config) UnaLogger {

	logxiLogger := log.New(conf.Name)
	if conf.FileName != "" {
		if file, err := os.Create(conf.FileName); err == nil {
			logxiLogger = log.NewLogger(file, conf.Name)
		}
	}
	unaLogger := &unaLogger{
		Logger: logxiLogger,
		name:   conf.Name,
	}

	// Add the logger to the list of loggers and set some defaults
	// Needs to use a mutex here so loggers can be created in different goroutines
	mutex.Lock()
	loggers = append(loggers, unaLogger)
	if !defaultsSet {
		setDefaults()
	}
	mutex.Unlock()

	return unaLogger
}

// SetWriter overrides the io.Writer of the underlying logxi logger
func (ul *unaLogger) SetWriter(writer io.Writer) {
	ul.Logger = log.NewLogger(writer, ul.name)
}

// Underlying returns the underlying logxi logger
func (ul unaLogger) Underlying() log.Logger {
	return ul.Logger
}

// Info logs to Stdout with an "INFO" prefix
func (ul unaLogger) Info(msg string, args ...interface{}) {
	ul.Logger.Info(msg, args...)
}

// Debug logs to Stdout with an "DEBUG" prefix if Debug level is enabled
func (ul unaLogger) Debug(msg string, args ...interface{}) {
	ul.Logger.Debug(msg, args...)
}

// Error logs to Stdout with an "Error" prefix
// It also adds an "error" key to the provided err(error) argument
func (ul unaLogger) Error(msg string, err error, args ...interface{}) {
	if errorClient != nil {
		errorClient.Report(errorreporting.Entry{
			Error: err,
		})
	}
	_ = ul.Logger.Error(msg, appendErrorToArgs(args, err)...)
}

// Fatal logs to Stdout with an "Fatal" prefix
// It also adds an "error" key to the provided err(error) argument
func (ul unaLogger) Fatal(msg string, err error, args ...interface{}) {
	if errorClient != nil {
		defer ReportPanics(context.Background())()
		// Logging at level Fatal without the panic since we have reported to Errorreporting already
		ul.Logger.Log(log.LevelFatal, msg, appendErrorToArgs(args, err))
	}
	ul.Logger.Fatal(msg, appendErrorToArgs(args, err))
}

func appendErrorToArgs(args []interface{}, err error) []interface{} {
	return append(args, "error", err)
}
<|endoftext|>