<commit_before>package cli

import (
	"fmt"
	"log"
	//	"gopkg.in/yaml.v2"
	"os"
	//	"text/template"
	"github.com/qadium/plumber/bindata"
	"github.com/qadium/plumber/graph"
	"github.com/qadium/plumber/shell"
	"os/exec"
	"path/filepath"
	"text/template"
	// "golang.org/x/oauth2/google"
	// "golang.org/x/oauth2"
	// "google.golang.org/cloud"
	// "google.golang.org/cloud/container"
	// kubectl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
	// cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	// "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	// "os"
)

type pipelineInfo struct {
	path   string
	name   string
	commit string
}

type kubeData struct {
	BundleName     string
	ExternalFacing bool
	PipelineName   string
	PipelineCommit string
	PlumberVersion string
	PlumberCommit  string
	ImageName      string
	Args           []string
}

func bundlesToGraphs(bundles []*Bundle) []*graph.Node {
	nodes := make([]*graph.Node, len(bundles))
	// this map maps inputs to the index of the node that uses it
	m := make(map[string]int)

	// build a map to create the DAG
	for i, bundle := range bundles {
		nodes[i] = graph.NewNode(bundle.Name)
		for _, input := range bundle.Inputs {
			m[input.Name] = i
		}
	}

	for i, bundle := range bundles {
		for _, output := range bundle.Outputs {
			if v, ok := m[output.Name]; ok {
				nodes[i].AddChildren(nodes[v])
			}
		}
	}
	return nodes
}

func localStart(ctx *Context, sortedPipeline []string) error {
	log.Printf(" |  Starting bundles...")
	managerDockerArgs := []string{"run", "-p", "9800:9800", "--rm", ctx.GetManagerImage()}
	// walk through the reverse sorted bundles and start them up
	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		log.Printf("    Starting: '%s'", bundleName)
		cmd := exec.Command(ctx.DockerCmd, "run", "-d", "-P", ctx.GetImage(bundleName))
		containerId, err := cmd.Output()
		if err != nil {
			return err
		}

		defer func() {
			log.Printf("    Stopping: '%s'", bundleName)
			cmd := exec.Command(ctx.DockerCmd, "rm", "-f", string(containerId)[0:4])
			_, err := cmd.Output()
			if err != nil {
				panic(err)
			}
			log.Printf("    Stopped.")
		}()

		log.Printf("    Started: %s", string(containerId))
		cmd = exec.Command(ctx.DockerCmd, "inspect", "--format='{{(index (index .NetworkSettings.Ports \"9800/tcp\") 0).HostPort}}'", string(containerId)[0:4])
		portNum, err := cmd.Output()
		if err != nil {
			return err
		}
		// get the docker host IP for local deploy
		hostIp, err := ctx.GetDockerHost()
		if err != nil {
			return err
		}
		managerDockerArgs = append(managerDockerArgs, fmt.Sprintf("http://%s:%s", hostIp, string(portNum[:len(portNum)-1])))
	}
	log.Printf("    Done.")
	log.Printf("    Args passed to 'docker': %v", managerDockerArgs)

	log.Printf(" |  Running manager. CTRL-C to quit.")
	err := shell.RunAndLog(ctx.DockerCmd, managerDockerArgs...)
	if err != nil {
		return err
	}
	log.Printf("    Done.")
	return nil
}

func writeKubernetesTemplate(tmplType string, destFilename string, templateData kubeData) error {
	tmpl, err := bindata.Asset(fmt.Sprintf("templates/%s.yaml", tmplType))
	if err != nil {
		return err
	}

	file, err := os.Create(destFilename)
	if err != nil {
		return err
	}
	defer func() {
		if err := file.Close(); err != nil {
			panic(err)
		}
	}()

	tmplFile, err := template.New("template").Parse(string(tmpl))
	if err != nil {
		return err
	}
	if err := tmplFile.Execute(file, templateData); err != nil {
		return err
	}
	return nil
}

func writeKubernetesFiles(ctx *Context, templateData kubeData) error {
	log.Printf(" |     Writing '%s'", templateData.BundleName)
	k8s := ctx.KubernetesPath(templateData.PipelineName)

	log.Printf("       Creating service file.")
	err := writeKubernetesTemplate("service", fmt.Sprintf("%s/%s.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")

	log.Printf("       Creating replication controller file.")
	err = writeKubernetesTemplate("replication-controller", fmt.Sprintf("%s/%s-rc.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")
	log.Printf("       Done.")
	return nil
}

func remoteStart(ctx *Context, sortedPipeline []string, projectId string, pipeline pipelineInfo) error {
	// we can probably get the project name with google cloud SDK
	log.Printf("   Creating '%s' directory...", ctx.KubeSubdir)
	k8s := ctx.KubernetesPath(pipeline.name)

	if err := os.MkdirAll(k8s, 0755); err != nil {
		return err
	}
	log.Printf("   Created.")

	args := []string{}

	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		localDockerTag := ctx.GetImage(bundleName)
		remoteDockerTag := fmt.Sprintf("gcr.io/%s/plumber-%s", projectId, bundleName)
		data := kubeData{
			BundleName:     bundleName,
			ImageName:      remoteDockerTag,
			PlumberVersion: ctx.Version,
			PlumberCommit:  ctx.GitCommit,
			PipelineName:   pipeline.name,
			PipelineCommit: pipeline.commit,
			ExternalFacing: false,
			Args:           []string{},
		}

		// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
		log.Printf("    Retagging: '%s'", bundleName)
		err := shell.RunAndLog(ctx.DockerCmd, "tag", "-f", localDockerTag, remoteDockerTag)
		if err != nil {
			return err
		}
		// step 2. push them to gce
		log.Printf("    Submitting: '%s'", remoteDockerTag)
		err = shell.RunAndLog(ctx.GcloudCmd, "docker", "push", remoteDockerTag)
		if err != nil {
			return err
		}
		// step 3. generate k8s files in pipelinePath
		if err := writeKubernetesFiles(ctx, data); err != nil {
			return err
		}

		// append to arglist (args now in sorted order)
		args = append(args, fmt.Sprintf("http://%s:9800", bundleName))
	}
	// create the manager service
	data := kubeData{
		BundleName:     "manager",
		ImageName:      fmt.Sprintf("gcr.io/%s/plumber-manager", projectId),
		PlumberVersion: ctx.Version,
		PlumberCommit:  ctx.GitCommit,
		PipelineName:   pipeline.name,
		PipelineCommit: pipeline.commit,
		ExternalFacing: true,
		Args:           args,
	}
	// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
	log.Printf("    Retagging: '%s'", ctx.GetManagerImage())
	err := shell.RunAndLog(ctx.DockerCmd, "tag", "-f", ctx.GetManagerImage(), data.ImageName)
	if err != nil {
		return err
	}
	// step 2. push them to gce
	log.Printf("    Submitting: '%s'", data.ImageName)
	err = shell.RunAndLog(ctx.GcloudCmd, "docker", "push", data.ImageName)
	if err != nil {
		return err
	}
	// step 3. generate k8s file in pipeline
	if err := writeKubernetesFiles(ctx, data); err != nil {
		return err
	}

	// step 4. launch all the services
	err = shell.RunAndLog(ctx.KubectlCmd, "create", "-f", k8s)
	if err != nil {
		return err
	}
	// step 5: open up the firewall?
	return nil
}

func (ctx *Context) Start(pipeline, gce string) error {
	log.Printf("==> Starting '%s' pipeline", pipeline)
	defer log.Printf("<== '%s' finished.", pipeline)

	log.Printf(" |  Building dependency graph.")
	path, err := ctx.GetPipeline(pipeline)
	if err != nil {
		return err
	}

	configs, err := filepath.Glob(fmt.Sprintf("%s/*.yml", path))
	if err != nil {
		return err
	}

	ctxs := make([]*Bundle, len(configs))
	for i, config := range configs {
		ctxs[i], err = ParseBundle(config)
		if err != nil {
			return err
		}
	}

	g := bundlesToGraphs(ctxs)
	sortedPipeline, err := graph.ReverseTopoSort(g)
	if err != nil {
		return err
	}
	log.Printf("    Reverse sorted: %v", sortedPipeline)
	log.Printf("    Completed.")

	if gce != "" {
		// start GOOGLE experiments?
		// when start is invoked with --gce PROJECT_ID, this piece of code
		// should be run
		// client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/compute")
		// if err != nil {
		// 	return err
		// }
		// cloudCtx := cloud.NewContext("kubernetes-fun", client)
		//
		// resources, err := container.Clusters(cloudCtx, "")
		// if err != nil {
		// 	return err
		// }
		// for _, op := range resources {
		// 	log.Printf("%v", op)
		// }
		//
		// loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
		// log.Printf("loading rules: %v", *loadingRules)
		// configOverrides := &clientcmd.ConfigOverrides{}
		// kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
		// cfg, err := kubeConfig.ClientConfig()
		// if err != nil {
		// 	return err
		// }

		// well, we just shell out!
		// f := cmdutil.NewFactory(nil)
		// cmd := kubectl.NewCmdCreate(f, os.Stdout)
		// f.BindFlags(cmd.PersistentFlags())
		// cmd.Flags().Set("filename", "/Users/echu/.plumber/foo/k8s")
		// cmd.Run(cmd, []string{})

		// end GOOGLE experiments

		info := pipelineInfo{
			name:   pipeline,
			path:   path,
			commit: "",
		}
		log.Printf(" |  Running remote pipeline.")
		return remoteStart(ctx, sortedPipeline, gce, info)
	} else {
		log.Printf(" |  Running local pipeline.")
		return localStart(ctx, sortedPipeline)
	}
	return nil
}
<commit_msg>removed dead return from start.go<commit_after>package cli

import (
	"fmt"
	"log"
	//	"gopkg.in/yaml.v2"
	"os"
	//	"text/template"
	"github.com/qadium/plumber/bindata"
	"github.com/qadium/plumber/graph"
	"github.com/qadium/plumber/shell"
	"os/exec"
	"path/filepath"
	"text/template"
	// "golang.org/x/oauth2/google"
	// "golang.org/x/oauth2"
	// "google.golang.org/cloud"
	// "google.golang.org/cloud/container"
	// kubectl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
	// cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	// "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	// "os"
)

type pipelineInfo struct {
	path   string
	name   string
	commit string
}

type kubeData struct {
	BundleName     string
	ExternalFacing bool
	PipelineName   string
	PipelineCommit string
	PlumberVersion string
	PlumberCommit  string
	ImageName      string
	Args           []string
}

func bundlesToGraphs(bundles []*Bundle) []*graph.Node {
	nodes := make([]*graph.Node, len(bundles))
	// this map maps inputs to the index of the node that uses it
	m := make(map[string]int)

	// build a map to create the DAG
	for i, bundle := range bundles {
		nodes[i] = graph.NewNode(bundle.Name)
		for _, input := range bundle.Inputs {
			m[input.Name] = i
		}
	}

	for i, bundle := range bundles {
		for _, output := range bundle.Outputs {
			if v, ok := m[output.Name]; ok {
				nodes[i].AddChildren(nodes[v])
			}
		}
	}
	return nodes
}

func localStart(ctx *Context, sortedPipeline []string) error {
	log.Printf(" |  Starting bundles...")
	managerDockerArgs := []string{"run", "-p", "9800:9800", "--rm", ctx.GetManagerImage()}
	// walk through the reverse sorted bundles and start them up
	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		log.Printf("    Starting: '%s'", bundleName)
		cmd := exec.Command(ctx.DockerCmd, "run", "-d", "-P", ctx.GetImage(bundleName))
		containerId, err := cmd.Output()
		if err != nil {
			return err
		}

		defer func() {
			log.Printf("    Stopping: '%s'", bundleName)
			cmd := exec.Command(ctx.DockerCmd, "rm", "-f", string(containerId)[0:4])
			_, err := cmd.Output()
			if err != nil {
				panic(err)
			}
			log.Printf("    Stopped.")
		}()

		log.Printf("    Started: %s", string(containerId))
		cmd = exec.Command(ctx.DockerCmd, "inspect", "--format='{{(index (index .NetworkSettings.Ports \"9800/tcp\") 0).HostPort}}'", string(containerId)[0:4])
		portNum, err := cmd.Output()
		if err != nil {
			return err
		}
		// get the docker host IP for local deploy
		hostIp, err := ctx.GetDockerHost()
		if err != nil {
			return err
		}
		managerDockerArgs = append(managerDockerArgs, fmt.Sprintf("http://%s:%s", hostIp, string(portNum[:len(portNum)-1])))
	}
	log.Printf("    Done.")
	log.Printf("    Args passed to 'docker': %v", managerDockerArgs)

	log.Printf(" |  Running manager. CTRL-C to quit.")
	err := shell.RunAndLog(ctx.DockerCmd, managerDockerArgs...)
	if err != nil {
		return err
	}
	log.Printf("    Done.")
	return nil
}

func writeKubernetesTemplate(tmplType string, destFilename string, templateData kubeData) error {
	tmpl, err := bindata.Asset(fmt.Sprintf("templates/%s.yaml", tmplType))
	if err != nil {
		return err
	}

	file, err := os.Create(destFilename)
	if err != nil {
		return err
	}
	defer func() {
		if err := file.Close(); err != nil {
			panic(err)
		}
	}()

	tmplFile, err := template.New("template").Parse(string(tmpl))
	if err != nil {
		return err
	}
	if err := tmplFile.Execute(file, templateData); err != nil {
		return err
	}
	return nil
}

func writeKubernetesFiles(ctx *Context, templateData kubeData) error {
	log.Printf(" |     Writing '%s'", templateData.BundleName)
	k8s := ctx.KubernetesPath(templateData.PipelineName)

	log.Printf("       Creating service file.")
	err := writeKubernetesTemplate("service", fmt.Sprintf("%s/%s.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")

	log.Printf("       Creating replication controller file.")
	err = writeKubernetesTemplate("replication-controller", fmt.Sprintf("%s/%s-rc.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")
	log.Printf("       Done.")
	return nil
}

func remoteStart(ctx *Context, sortedPipeline []string, projectId string, pipeline pipelineInfo) error {
	// we can probably get the project name with google cloud SDK
	log.Printf("   Creating '%s' directory...", ctx.KubeSubdir)
	k8s := ctx.KubernetesPath(pipeline.name)

	if err := os.MkdirAll(k8s, 0755); err != nil {
		return err
	}
	log.Printf("   Created.")

	args := []string{}

	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		localDockerTag := ctx.GetImage(bundleName)
		remoteDockerTag := fmt.Sprintf("gcr.io/%s/plumber-%s", projectId, bundleName)
		data := kubeData{
			BundleName:     bundleName,
			ImageName:      remoteDockerTag,
			PlumberVersion: ctx.Version,
			PlumberCommit:  ctx.GitCommit,
			PipelineName:   pipeline.name,
			PipelineCommit: pipeline.commit,
			ExternalFacing: false,
			Args:           []string{},
		}

		// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
		log.Printf("    Retagging: '%s'", bundleName)
		err := shell.RunAndLog(ctx.DockerCmd, "tag", "-f", localDockerTag, remoteDockerTag)
		if err != nil {
			return err
		}
		// step 2. push them to gce
		log.Printf("    Submitting: '%s'", remoteDockerTag)
		err = shell.RunAndLog(ctx.GcloudCmd, "docker", "push", remoteDockerTag)
		if err != nil {
			return err
		}
		// step 3. generate k8s files in pipelinePath
		if err := writeKubernetesFiles(ctx, data); err != nil {
			return err
		}

		// append to arglist (args now in sorted order)
		args = append(args, fmt.Sprintf("http://%s:9800", bundleName))
	}
	// create the manager service
	data := kubeData{
		BundleName:     "manager",
		ImageName:      fmt.Sprintf("gcr.io/%s/plumber-manager", projectId),
		PlumberVersion: ctx.Version,
		PlumberCommit:  ctx.GitCommit,
		PipelineName:   pipeline.name,
		PipelineCommit: pipeline.commit,
		ExternalFacing: true,
		Args:           args,
	}
	// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
	log.Printf("    Retagging: '%s'", ctx.GetManagerImage())
	err := shell.RunAndLog(ctx.DockerCmd, "tag", "-f", ctx.GetManagerImage(), data.ImageName)
	if err != nil {
		return err
	}
	// step 2. push them to gce
	log.Printf("    Submitting: '%s'", data.ImageName)
	err = shell.RunAndLog(ctx.GcloudCmd, "docker", "push", data.ImageName)
	if err != nil {
		return err
	}
	// step 3. generate k8s file in pipeline
	if err := writeKubernetesFiles(ctx, data); err != nil {
		return err
	}

	// step 4. launch all the services
	err = shell.RunAndLog(ctx.KubectlCmd, "create", "-f", k8s)
	if err != nil {
		return err
	}
	// step 5: open up the firewall?
	return nil
}

func (ctx *Context) Start(pipeline, gce string) error {
	log.Printf("==> Starting '%s' pipeline", pipeline)
	defer log.Printf("<== '%s' finished.", pipeline)

	log.Printf(" |  Building dependency graph.")
	path, err := ctx.GetPipeline(pipeline)
	if err != nil {
		return err
	}

	configs, err := filepath.Glob(fmt.Sprintf("%s/*.yml", path))
	if err != nil {
		return err
	}

	ctxs := make([]*Bundle, len(configs))
	for i, config := range configs {
		ctxs[i], err = ParseBundle(config)
		if err != nil {
			return err
		}
	}

	g := bundlesToGraphs(ctxs)
	sortedPipeline, err := graph.ReverseTopoSort(g)
	if err != nil {
		return err
	}
	log.Printf("    Reverse sorted: %v", sortedPipeline)
	log.Printf("    Completed.")

	if gce != "" {
		// start GOOGLE experiments?
		// when start is invoked with --gce PROJECT_ID, this piece of code
		// should be run
		// client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/compute")
		// if err != nil {
		// 	return err
		// }
		// cloudCtx := cloud.NewContext("kubernetes-fun", client)
		//
		// resources, err := container.Clusters(cloudCtx, "")
		// if err != nil {
		// 	return err
		// }
		// for _, op := range resources {
		// 	log.Printf("%v", op)
		// }
		//
		// loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
		// log.Printf("loading rules: %v", *loadingRules)
		// configOverrides := &clientcmd.ConfigOverrides{}
		// kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
		// cfg, err := kubeConfig.ClientConfig()
		// if err != nil {
		// 	return err
		// }

		// well, we just shell out!
		// f := cmdutil.NewFactory(nil)
		// cmd := kubectl.NewCmdCreate(f, os.Stdout)
		// f.BindFlags(cmd.PersistentFlags())
		// cmd.Flags().Set("filename", "/Users/echu/.plumber/foo/k8s")
		// cmd.Run(cmd, []string{})

		// end GOOGLE experiments

		info := pipelineInfo{
			name:   pipeline,
			path:   path,
			commit: "",
		}
		log.Printf(" |  Running remote pipeline.")
		return remoteStart(ctx, sortedPipeline, gce, info)
	} else {
		log.Printf(" |  Running local pipeline.")
		return localStart(ctx, sortedPipeline)
	}
}
<|endoftext|>


<commit_before>package goarabic

// Harf holds the Arabic character with its different representation forms (glyphs).
type Harf struct {
	Unicode, Isolated, Beggining, Medium, Final rune
}

// Vowels (Tashkeel) characters.
var (
	FATHA    rune = '\u064e'
	FATHATAN rune = '\u064b'
	DAMMA    rune = '\u064f'
	DAMMATAN rune = '\u064c'
	KASRA    rune = '\u0650'
	KASRATAN rune = '\u064d'
	SHADDA   rune = '\u0651'
	SUKUN    rune = '\u0652'
)

// Arabic Alphabet using the new Harf type.
var (
	ALEF_HAMZA_ABOVE = Harf{ // أ
		Unicode:   '\u0623',
		Isolated:  '\ufe83',
		Beggining: '\u0623',
		Medium:    '\ufe84',
		Final:     '\ufe84'}

	ALEF = Harf{ // ا
		Unicode:   '\u0627',
		Isolated:  '\ufe8d',
		Beggining: '\u0627',
		Medium:    '\ufe8e',
		Final:     '\ufe8e'}

	ALEF_MADDA_ABOVE = Harf{ // آ
		Unicode:   '\u0622',
		Isolated:  '\ufe81',
		Beggining: '\u0622',
		Medium:    '\ufe82',
		Final:     '\ufe82'}

	HAMZA = Harf{ // ء
		Unicode:   '\u0621',
		Isolated:  '\ufe80',
		Beggining: '\u0621',
		Medium:    '\u0621',
		Final:     '\u0621'}

	WAW_HAMZA_ABOVE = Harf{ // ؤ
		Unicode:   '\u0624',
		Isolated:  '\ufe85',
		Beggining: '\u0624',
		Medium:    '\ufe86',
		Final:     '\ufe86'}

	ALEF_HAMZA_BELOW = Harf{ // أ
		Unicode:   '\u0625',
		Isolated:  '\ufe87',
		Beggining: '\u0625',
		Medium:    '\ufe88',
		Final:     '\ufe88'}

	YEH_HAMZA_ABOVE = Harf{ // ئ
		Unicode:   '\u0626',
		Isolated:  '\ufe89',
		Beggining: '\ufe8b',
		Medium:    '\ufe8c',
		Final:     '\ufe8a'}

	BEH = Harf{ // ب
		Unicode:   '\u0628',
		Isolated:  '\ufe8f',
		Beggining: '\ufe91',
		Medium:    '\ufe92',
		Final:     '\ufe90'}

	TEH = Harf{ // ت
		Unicode:   '\u062A',
		Isolated:  '\ufe95',
		Beggining: '\ufe97',
		Medium:    '\ufe98',
		Final:     '\ufe96'}

	TEH_MARBUTA = Harf{ // ة
		Unicode:   '\u0629',
		Isolated:  '\ufe93',
		Beggining: '\u0629',
		Medium:    '\u0629',
		Final:     '\ufe94'}

	THEH = Harf{ // ث
		Unicode:   '\u062b',
		Isolated:  '\ufe99',
		Beggining: '\ufe9b',
		Medium:    '\ufe9c',
		Final:     '\ufe9a'}

	JEEM = Harf{ // ج
		Unicode:   '\u062c',
		Isolated:  '\ufe9d',
		Beggining: '\ufe9f',
		Medium:    '\ufea0',
		Final:     '\ufe9e'}

	HAH = Harf{ // ح
		Unicode:   '\u062d',
		Isolated:  '\ufea1',
		Beggining: '\ufea3',
		Medium:    '\ufea4',
		Final:     '\ufea2'}

	KHAH = Harf{ // خ
		Unicode:   '\u062e',
		Isolated:  '\ufea5',
		Beggining: '\ufea7',
		Medium:    '\ufea8',
		Final:     '\ufea6'}

	DAL = Harf{ // د
		Unicode:   '\u062f',
		Isolated:  '\ufea9',
		Beggining: '\u062f',
		Medium:    '\ufeaa',
		Final:     '\ufeaa'}

	THAL = Harf{ // ذ
		Unicode:   '\u0630',
		Isolated:  '\ufeab',
		Beggining: '\u0630',
		Medium:    '\ufeac',
		Final:     '\ufeac'}

	REH = Harf{ // ر
		Unicode:   '\u0631',
		Isolated:  '\ufead',
		Beggining: '\u0631',
		Medium:    '\ufeae',
		Final:     '\ufeae'}

	ZAIN = Harf{ // ز
		Unicode:   '\u0632',
		Isolated:  '\ufeaf',
		Beggining: '\u0632',
		Medium:    '\ufeb0',
		Final:     '\ufeb0'}

	SEEN = Harf{ // س
		Unicode:   '\u0633',
		Isolated:  '\ufeb1',
		Beggining: '\ufeb3',
		Medium:    '\ufeb4',
		Final:     '\ufeb2'}

	SHEEN = Harf{ // ش
		Unicode:   '\u0634',
		Isolated:  '\ufeb5',
		Beggining: '\ufeb7',
		Medium:    '\ufeb8',
		Final:     '\ufeb6'}

	SAD = Harf{ // ص
		Unicode:   '\u0635',
		Isolated:  '\ufeb9',
		Beggining: '\ufebb',
		Medium:    '\ufebc',
		Final:     '\ufeba'}

	DAD = Harf{ // ض
		Unicode:   '\u0636',
		Isolated:  '\ufebd',
		Beggining: '\ufebf',
		Medium:    '\ufec0',
		Final:     '\ufebe'}

	TAH = Harf{ // ط
		Unicode:   '\u0637',
		Isolated:  '\ufec1',
		Beggining: '\ufec3',
		Medium:    '\ufec4',
		Final:     '\ufec2'}

	ZAH = Harf{ // ظ
		Unicode:   '\u0638',
		Isolated:  '\ufec5',
		Beggining: '\ufec7',
		Medium:    '\ufec8',
		Final:     '\ufec6'}

	AIN = Harf{ // ع
		Unicode:   '\u0639',
		Isolated:  '\ufec9',
		Beggining: '\ufecb',
		Medium:    '\ufecc',
		Final:     '\ufeca'}

	GHAIN = Harf{ // غ
		Unicode:   '\u063a',
		Isolated:  '\ufecd',
		Beggining: '\ufecf',
		Medium:    '\ufed0',
		Final:     '\ufece'}

	FEH = Harf{ // ف
		Unicode:   '\u0641',
		Isolated:  '\ufed1',
		Beggining: '\ufed3',
		Medium:    '\ufed4',
		Final:     '\ufed2'}

	QAF = Harf{ // ق
		Unicode:   '\u0642',
		Isolated:  '\ufed5',
		Beggining: '\ufed7',
		Medium:    '\ufed8',
		Final:     '\ufed6'}

	KAF = Harf{ // ك
		Unicode:   '\u0643',
		Isolated:  '\ufed9',
		Beggining: '\ufedb',
		Medium:    '\ufedc',
		Final:     '\ufeda'}

	LAM = Harf{ // ل
		Unicode:   '\u0644',
		Isolated:  '\ufedd',
		Beggining: '\ufedf',
		Medium:    '\ufee0',
		Final:     '\ufede'}

	MEEM = Harf{ // م
		Unicode:   '\u0645',
		Isolated:  '\ufee1',
		Beggining: '\ufee3',
		Medium:    '\ufee4',
		Final:     '\ufee2'}

	NOON = Harf{ // ن
		Unicode:   '\u0646',
		Isolated:  '\ufee5',
		Beggining: '\ufee7',
		Medium:    '\ufee8',
		Final:     '\ufee6'}

	HEH = Harf{ // ه
		Unicode:   '\u0647',
		Isolated:  '\ufee9',
		Beggining: '\ufeeb',
		Medium:    '\ufeec',
		Final:     '\ufeea'}

	WAW = Harf{ // و
		Unicode:   '\u0648',
		Isolated:  '\ufeed',
		Beggining: '\u0648',
		Medium:    '\ufeee',
		Final:     '\ufeee'}

	YEH = Harf{ // ي
		Unicode:   '\u064a',
		Isolated:  '\ufef1',
		Beggining: '\ufef3',
		Medium:    '\ufef4',
		Final:     '\ufef2'}

	ALEF_MAKSURA = Harf{ // ى
		Unicode:   '\u0649',
		Isolated:  '\ufeef',
		Beggining: '\u0649',
		Medium:    '\ufef0',
		Final:     '\ufef0'}

	TATWEEL = Harf{ // ـ
		Unicode:   '\u0640',
		Isolated:  '\u0640',
		Beggining: '\u0640',
		Medium:    '\u0640',
		Final:     '\u0640'}

	LAM_ALEF = Harf{ // لا
		Unicode:   '\ufefb',
		Isolated:  '\ufefb',
		Beggining: '\ufefb',
		Medium:    '\ufefc',
		Final:     '\ufefc'}

	LAM_ALEF_HAMZA_ABOVE = Harf{ // ﻷ
		Unicode:   '\ufef7',
		Isolated:  '\ufef7',
		Beggining: '\ufef7',
		Medium:    '\ufef8',
		Final:     '\ufef8'}
)

var alphabet = []Harf{
	ALEF_HAMZA_ABOVE,
	ALEF,
	ALEF_MADDA_ABOVE,
	HAMZA,
	WAW_HAMZA_ABOVE,
	ALEF_HAMZA_BELOW,
	YEH_HAMZA_ABOVE,
	BEH,
	TEH,
	TEH_MARBUTA,
	THEH,
	JEEM,
	HAH,
	KHAH,
	DAL,
	THAL,
	REH,
	ZAIN,
	SEEN,
	SHEEN,
	SAD,
	DAD,
	TAH,
	ZAH,
	AIN,
	GHAIN,
	FEH,
	QAF,
	KAF,
	LAM,
	MEEM,
	NOON,
	HEH,
	WAW,
	YEH,
	ALEF_MAKSURA,
	TATWEEL,
	LAM_ALEF,
	LAM_ALEF_HAMZA_ABOVE,
}

// use map for faster lookups.
var tashkeel = map[rune]bool{FATHA: true, FATHATAN: true, DAMMA: true,
	DAMMATAN: true, KASRA: true, KASRATAN: true,
	SHADDA: true, SUKUN: true}

// use map for faster lookups.
// var special_char = map[rune]bool{"": true, ' ': true, '?': true,
//	'؟': true, '.': true, KASRATAN: true,
//	SHADDA: true, SUKUN: true}

// use map for faster lookups.
var beggining_after = map[Harf]bool{
	ALEF_HAMZA_ABOVE: true,
	ALEF_MADDA_ABOVE: true,
	ALEF:             true,
	HAMZA:            true,
	WAW_HAMZA_ABOVE:  true,
	ALEF_HAMZA_BELOW: true,
	TEH_MARBUTA:      true,
	DAL:              true,
	THAL:             true,
	REH:              true,
	ZAIN:             true,
	WAW:              true,
	ALEF_MAKSURA:     true}
<commit_msg>add GAF,CHEH,JEH and few other persian letters<commit_after>package goarabic

// Harf holds the Arabic character with its different representation forms (glyphs).
type Harf struct {
	Unicode, Isolated, Beggining, Medium, Final rune
}

// Vowels (Tashkeel) characters.
var (
	FATHA    rune = '\u064e'
	FATHATAN rune = '\u064b'
	DAMMA    rune = '\u064f'
	DAMMATAN rune = '\u064c'
	KASRA    rune = '\u0650'
	KASRATAN rune = '\u064d'
	SHADDA   rune = '\u0651'
	SUKUN    rune = '\u0652'
)

// Arabic Alphabet using the new Harf type.
var (
	ALEF_HAMZA_ABOVE = Harf{ // أ
		Unicode:   '\u0623',
		Isolated:  '\ufe83',
		Beggining: '\u0623',
		Medium:    '\ufe84',
		Final:     '\ufe84'}

	ALEF = Harf{ // ا
		Unicode:   '\u0627',
		Isolated:  '\ufe8d',
		Beggining: '\u0627',
		Medium:    '\ufe8e',
		Final:     '\ufe8e'}

	ALEF_MADDA_ABOVE = Harf{ // آ
		Unicode:   '\u0622',
		Isolated:  '\ufe81',
		Beggining: '\u0622',
		Medium:    '\ufe82',
		Final:     '\ufe82'}

	HAMZA = Harf{ // ء
		Unicode:   '\u0621',
		Isolated:  '\ufe80',
		Beggining: '\u0621',
		Medium:    '\u0621',
		Final:     '\u0621'}

	WAW_HAMZA_ABOVE = Harf{ // ؤ
		Unicode:   '\u0624',
		Isolated:  '\ufe85',
		Beggining: '\u0624',
		Medium:    '\ufe86',
		Final:     '\ufe86'}

	ALEF_HAMZA_BELOW = Harf{ // أ
		Unicode:   '\u0625',
		Isolated:  '\ufe87',
		Beggining: '\u0625',
		Medium:    '\ufe88',
		Final:     '\ufe88'}

	YEH_HAMZA_ABOVE = Harf{ // ئ
		Unicode:   '\u0626',
		Isolated:  '\ufe89',
		Beggining: '\ufe8b',
		Medium:    '\ufe8c',
		Final:     '\ufe8a'}

	BEH = Harf{ // ب
		Unicode:   '\u0628',
		Isolated:  '\ufe8f',
		Beggining: '\ufe91',
		Medium:    '\ufe92',
		Final:     '\ufe90'}

	PEH = Harf{ // پ
		Unicode:   '\u067e',
		Isolated:  '\ufb56',
		Beggining: '\ufb58',
		Medium:    '\ufb59',
		Final:     '\ufb57'}

	TEH = Harf{ // ت
		Unicode:   '\u062A',
		Isolated:  '\ufe95',
		Beggining: '\ufe97',
		Medium:    '\ufe98',
		Final:     '\ufe96'}

	TEH_MARBUTA = Harf{ // ة
		Unicode:   '\u0629',
		Isolated:  '\ufe93',
		Beggining: '\u0629',
		Medium:    '\u0629',
		Final:     '\ufe94'}

	THEH = Harf{ // ث
		Unicode:   '\u062b',
		Isolated:  '\ufe99',
		Beggining: '\ufe9b',
		Medium:    '\ufe9c',
		Final:     '\ufe9a'}

	JEEM = Harf{ // ج
		Unicode:   '\u062c',
		Isolated:  '\ufe9d',
		Beggining: '\ufe9f',
		Medium:    '\ufea0',
		Final:     '\ufe9e'}

	TCHEH = Harf{ // چ
		Unicode:   '\u0686',
		Isolated:  '\ufb7a',
		Beggining: '\ufb7c',
		Medium:    '\ufb7d',
		Final:     '\ufb7b'}

	HAH = Harf{ // ح
		Unicode:   '\u062d',
		Isolated:  '\ufea1',
		Beggining: '\ufea3',
		Medium:    '\ufea4',
		Final:     '\ufea2'}

	KHAH = Harf{ // خ
		Unicode:   '\u062e',
		Isolated:  '\ufea5',
		Beggining: '\ufea7',
		Medium:    '\ufea8',
		Final:     '\ufea6'}

	DAL = Harf{ // د
		Unicode:   '\u062f',
		Isolated:  '\ufea9',
		Beggining: '\u062f',
		Medium:    '\ufeaa',
		Final:     '\ufeaa'}

	THAL = Harf{ // ذ
		Unicode:   '\u0630',
		Isolated:  '\ufeab',
		Beggining: '\u0630',
		Medium:    '\ufeac',
		Final:     '\ufeac'}

	REH = Harf{ // ر
		Unicode:   '\u0631',
		Isolated:  '\ufead',
		Beggining: '\u0631',
		Medium:    '\ufeae',
		Final:     '\ufeae'}

	JEH = Harf{
		Unicode:   '\u0698',
		Isolated:  '\ufb8a',
		Beggining: '\u0698',
		Medium:    '\ufb8b',
		Final:     '\ufb8b',
	}

	ZAIN = Harf{ // ز
		Unicode:   '\u0632',
		Isolated:  '\ufeaf',
		Beggining: '\u0632',
		Medium:    '\ufeb0',
		Final:     '\ufeb0'}

	SEEN = Harf{ // س
		Unicode:   '\u0633',
		Isolated:  '\ufeb1',
		Beggining: '\ufeb3',
		Medium:    '\ufeb4',
		Final:     '\ufeb2'}

	SHEEN = Harf{ // ش
		Unicode:   '\u0634',
		Isolated:  '\ufeb5',
		Beggining: '\ufeb7',
		Medium:    '\ufeb8',
		Final:     '\ufeb6'}

	SAD = Harf{ // ص
		Unicode:   '\u0635',
		Isolated:  '\ufeb9',
		Beggining: '\ufebb',
		Medium:    '\ufebc',
		Final:     '\ufeba'}

	DAD = Harf{ // ض
		Unicode:   '\u0636',
		Isolated:  '\ufebd',
		Beggining: '\ufebf',
		Medium:    '\ufec0',
		Final:     '\ufebe'}

	TAH = Harf{ // ط
		Unicode:   '\u0637',
		Isolated:  '\ufec1',
		Beggining: '\ufec3',
		Medium:    '\ufec4',
		Final:     '\ufec2'}

	ZAH = Harf{ // ظ
		Unicode:   '\u0638',
		Isolated:  '\ufec5',
		Beggining: '\ufec7',
		Medium:    '\ufec8',
		Final:     '\ufec6'}

	AIN = Harf{ // ع
		Unicode:   '\u0639',
		Isolated:  '\ufec9',
		Beggining: '\ufecb',
		Medium:    '\ufecc',
		Final:     '\ufeca'}

	GHAIN = Harf{ // غ
		Unicode:   '\u063a',
		Isolated:  '\ufecd',
		Beggining: '\ufecf',
		Medium:    '\ufed0',
		Final:     '\ufece'}

	FEH = Harf{ // ف
		Unicode:   '\u0641',
		Isolated:  '\ufed1',
		Beggining: '\ufed3',
		Medium:    '\ufed4',
		Final:     '\ufed2'}

	QAF = Harf{ // ق
		Unicode:   '\u0642',
		Isolated:  '\ufed5',
		Beggining: '\ufed7',
		Medium:    '\ufed8',
		Final:     '\ufed6'}

	KAF = Harf{ // ك
		Unicode:   '\u0643',
		Isolated:  '\ufed9',
		Beggining: '\ufedb',
		Medium:    '\ufedc',
		Final:     '\ufeda'}

	KEHEH = Harf{ // ک
		Unicode:   '\u06a9',
		Isolated:  '\ufb8e',
		Beggining: '\ufb90',
		Medium:    '\ufb91',
		Final:     '\ufb8f',
	}

	GAF = Harf{ // گ
		Unicode:   '\u06af',
		Isolated:  '\ufb92',
		Beggining: '\ufb94',
		Medium:    '\ufb95',
		Final:     '\ufb93'}

	LAM = Harf{ // ل
		Unicode:   '\u0644',
		Isolated:  '\ufedd',
		Beggining: '\ufedf',
		Medium:    '\ufee0',
		Final:     '\ufede'}

	MEEM = Harf{ // م
		Unicode:   '\u0645',
		Isolated:  '\ufee1',
		Beggining: '\ufee3',
		Medium:    '\ufee4',
		Final:     '\ufee2'}

	NOON = Harf{ // ن
		Unicode:   '\u0646',
		Isolated:  '\ufee5',
		Beggining: '\ufee7',
		Medium:    '\ufee8',
		Final:     '\ufee6'}

	HEH = Harf{ // ه
		Unicode:   '\u0647',
		Isolated:  '\ufee9',
		Beggining: '\ufeeb',
		Medium:    '\ufeec',
		Final:     '\ufeea'}

	WAW = Harf{ // و
		Unicode:   '\u0648',
		Isolated:  '\ufeed',
		Beggining: '\u0648',
		Medium:    '\ufeee',
		Final:     '\ufeee'}

	YEH = Harf{ // ی
		Unicode:   '\u06cc',
		Isolated:  '\ufbfc',
		Beggining: '\ufbfe',
		Medium:    '\ufbff',
		Final:     '\ufbfd'}

	ARABICYEH = Harf{ // ي
		Unicode:   '\u064a',
		Isolated:  '\ufef1',
		Beggining: '\ufef3',
		Medium:    '\ufef4',
		Final:     '\ufef2'}

	ALEF_MAKSURA = Harf{ // ى
		Unicode:   '\u0649',
		Isolated:  '\ufeef',
		Beggining: '\u0649',
		Medium:    '\ufef0',
		Final:     '\ufef0'}

	TATWEEL = Harf{ // ـ
		Unicode:   '\u0640',
		Isolated:  '\u0640',
		Beggining: '\u0640',
		Medium:    '\u0640',
		Final:     '\u0640'}

	LAM_ALEF = Harf{ // لا
		Unicode:   '\ufefb',
		Isolated:  '\ufefb',
		Beggining: '\ufefb',
		Medium:    '\ufefc',
		Final:     '\ufefc'}

	LAM_ALEF_HAMZA_ABOVE = Harf{ // ﻷ
		Unicode:   '\ufef7',
		Isolated:  '\ufef7',
		Beggining: '\ufef7',
		Medium:    '\ufef8',
		Final:     '\ufef8'}
)

var alphabet = []Harf{
	ALEF_HAMZA_ABOVE,
	ALEF,
	ALEF_MADDA_ABOVE,
	HAMZA,
	WAW_HAMZA_ABOVE,
	ALEF_HAMZA_BELOW,
	YEH_HAMZA_ABOVE,
	BEH,
	PEH,
	TEH,
	TEH_MARBUTA,
	THEH,
	JEEM,
	TCHEH,
	HAH,
	KHAH,
	DAL,
	THAL,
	REH,
	JEH,
	ZAIN,
	SEEN,
	SHEEN,
	SAD,
	DAD,
	TAH,
	ZAH,
	AIN,
	GHAIN,
	FEH,
	QAF,
	KAF,
	KEHEH,
	GAF,
	LAM,
	MEEM,
	NOON,
	HEH,
	WAW,
	YEH,
	ARABIYEH,
	ALEF_MAKSURA,
	TATWEEL,
	LAM_ALEF,
	LAM_ALEF_HAMZA_ABOVE,
}

// use map for faster lookups.
var tashkeel = map[rune]bool{FATHA: true, FATHATAN: true, DAMMA: true,
	DAMMATAN: true, KASRA: true, KASRATAN: true,
	SHADDA: true, SUKUN: true}

// use map for faster lookups.
// var special_char = map[rune]bool{"": true, ' ': true, '?': true,
//	'؟': true, '.': true, KASRATAN: true,
//	SHADDA: true, SUKUN: true}

// use map for faster lookups.
var beggining_after = map[Harf]bool{
	ALEF_HAMZA_ABOVE: true,
	ALEF_MADDA_ABOVE: true,
	ALEF:             true,
	HAMZA:            true,
	WAW_HAMZA_ABOVE:  true,
	ALEF_HAMZA_BELOW: true,
	TEH_MARBUTA:      true,
	DAL:              true,
	THAL:             true,
	REH:              true,
	ZAIN:             true,
	WAW:              true,
	ALEF_MAKSURA:     true}
<|endoftext|>


<commit_before>//// +build dev

// build.go automates proper versioning of authms binaries
// and installer scripts.
// Use it like:   go run build.go
// The result binary will be located in bin/app
// You can customize the build with the -goos, -goarch, and
// -goarm CLI options:   go run build.go -goos=windows
//
// This program is NOT required to build authms from source
// since it is go-gettable. (You can run plain `go build`
// in this directory to get a binary).
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"

	"github.com/tomogoma/go-typed-errors"
	"github.com/tomogoma/seedms/pkg/config"
	"github.com/tomogoma/seedms/pkg/fileutils"
)

func main() {
	var goos, goarch, goarm string
	var help bool
	flag.StringVar(&goos, "goos", "",
		"GOOS\tThe operating system for which to compile\n"+
			"\t\tExamples are linux, darwin, windows, netbsd.")
	flag.StringVar(&goarch, "goarch", "",
		"GOARCH\tThe architecture, or processor, for which to compile code.\n"+
			"\t\tExamples are amd64, 386, arm, ppc64.")
	flag.StringVar(&goarm, "goarm", "",
		"GOARM\tFor GOARCH=arm, the ARM architecture for which to compile.\n"+
			"\t\tValid values are 5, 6, 7.")
	flag.BoolVar(&help, "help", false, "Show this help message")
	flag.Parse()
	if help {
		flag.Usage()
		os.Exit(0)
	}
	if err := buildMicroservice(goos, goarch, goarm); err != nil {
		log.Fatalf("buildMicroservice error: %v", err)
	}
	if err := installVars(); err != nil {
		log.Fatalf("write installer script error: %v", err)
	}
	if err := buildGcloud(); err != nil {
		log.Fatalf("build GCloud error: %v", err)
	}
}

func installVars() error {
	content := `#!/usr/bin/env bash
NAME="` + config.Name + `"
VERSION="` + config.VersionFull + `"
DESCRIPTION="` + config.Description + `"
CANONICAL_NAME="` + config.CanonicalName() + `"
CONF_DIR="` + config.DefaultConfDir() + `"
CONF_FILE="` + config.DefaultConfPath() + `"
INSTALL_DIR="` + config.DefaultInstallDir() + `"
INSTALL_FILE="` + config.DefaultInstallPath() + `"
UNIT_NAME="` + config.DefaultSysDUnitName() + `"
UNIT_FILE="` + config.DefaultSysDUnitFilePath() + `"
DOCS_DIR="` + config.DefaultDocsDir() + `"
`
	return ioutil.WriteFile("install/vars.sh", []byte(content), 0755)
}

func buildMicroservice(goos, goarch, goarm string) error {
	docsDir := path.Join("install", "docs", config.VersionMajorPrefixed(), config.Name, "docs")
	if err := compileDocs(docsDir); err != nil {
		return err
	}
	args := []string{"build", "-o", "bin/app", "./cmd/micro"}
	cmd := exec.Command("go", args...)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = os.Environ()
	for _, env := range []string{
		"GOOS=" + goos,
		"GOARCH=" + goarch,
		"GOARM=" + goarm,
	} {
		cmd.Env = append(cmd.Env, env)
	}
	// TODO USE cmd.CombinedOutput()
	return cmd.Run()
}

func buildGcloud() error {
	confDir := config.DefaultConfDir("cmd", "gcloud", "conf")

	if err := os.MkdirAll(confDir, 0755); err != nil {
		return errors.Newf("create conf dir: %v", err)
	}

	docsDir := path.Join(config.DefaultDocsDir(), config.VersionMajorPrefixed(), config.Name, "docs")
	if err := compileDocs(docsDir); err != nil {
		return err
	}

	err := fileutils.CopyIfDestNotExists(path.Join("install", "conf.yml"), config.DefaultConfPath())
	if err != nil {
		return err
	}
	if err := cleanGCloudConfFile(); err != nil {
		return errors.Newf("clean gcloud config file: %v", err)
	}

	return nil
}

func compileDocs(docsDir string) error {

	subjDir := path.Join("pkg", "handler", "http")
	headerFile := path.Join(subjDir, "apidoc_header.md")
	APIDocConfFile := path.Join(subjDir, "apidoc.json")

	apiDoc := struct {
		Name        string      `json:"name"`
		Version     string      `json:"version"`
		Description string      `json:"description"`
		Title       string      `json:"title"`
		Header      interface{} `json:"header"`
	}{
		Name:        config.Name,
		Version:     config.VersionFull,
		Description: config.Description,
		Title:       config.CanonicalName(),
		Header: struct {
			Title    string `json:"title"`
			FileName string `json:"filename"`
		}{
			Title:    "Introduction",
			FileName: headerFile,
		},
	}

	apiDocB, err := json.Marshal(apiDoc)
	if err != nil {
		return errors.Newf("Marshal API doc config: %v", err)
	}

	err = ioutil.WriteFile(APIDocConfFile, apiDocB, 0655)
	if err != nil {
		return errors.Newf("Write API doc file: %v", err)
	}

	args := []string{"-i", subjDir, "-c", subjDir, "-o", docsDir}
	cmd := exec.Command("apidoc", args...)
	if out, err := cmd.CombinedOutput(); err != nil {
		return errors.Newf("generate http docs: %v: %s", err, out)
	}
	return nil
}

func cleanGCloudConfFile() error {
	newPath := config.DefaultConfPath()
	confContent, err := ioutil.ReadFile(config.DefaultConfPath())
	if err != nil {
		return errors.Newf("read file for transform: %v", err)
	}
	confContentClean := bytes.Replace(confContent, []byte(config.SysDConfDir()+"/"), []byte("conf/"), -1)
	err = ioutil.WriteFile(newPath, confContentClean, 0644)
	if err != nil {
		return errors.Newf("write transformed file: %v", err)
	}
	return nil
}
<commit_msg>Provide more context on build result<commit_after>//// +build dev

// build.go automates proper versioning of authms binaries
// and installer scripts.
// Use it like:   go run build.go
// The result binary will be located in bin/app
// You can customize the build with the -goos, -goarch, and
// -goarm CLI options:   go run build.go -goos=windows
//
// This program is NOT required to build authms from source
// since it is go-gettable. (You can run plain `go build`
// in this directory to get a binary).
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"

	"github.com/tomogoma/go-typed-errors"
	"github.com/tomogoma/seedms/pkg/config"
	"github.com/tomogoma/seedms/pkg/fileutils"
)

func main() {
	var goos, goarch, goarm string
	var help bool
	flag.StringVar(&goos, "goos", "",
		"GOOS\tThe operating system for which to compile\n"+
			"\t\tExamples are linux, darwin, windows, netbsd.")
	flag.StringVar(&goarch, "goarch", "",
		"GOARCH\tThe architecture, or processor, for which to compile code.\n"+
			"\t\tExamples are amd64, 386, arm, ppc64.")
	flag.StringVar(&goarm, "goarm", "",
		"GOARM\tFor GOARCH=arm, the ARM architecture for which to compile.\n"+
			"\t\tValid values are 5, 6, 7.")
	flag.BoolVar(&help, "help", false, "Show this help message")
	flag.Parse()
	if help {
		flag.Usage()
		os.Exit(0)
	}
	if err := buildMicroservice(goos, goarch, goarm); err != nil {
		log.Fatalf("buildMicroservice error: %v", err)
	}
	if err := installVars(); err != nil {
		log.Fatalf("write installer script error: %v", err)
	}
	if err := buildGcloud(); err != nil {
		log.Fatalf("build GCloud error: %v", err)
	}
}

func installVars() error {
	content := `#!/usr/bin/env bash
NAME="` + config.Name + `"
VERSION="` + config.VersionFull + `"
DESCRIPTION="` + config.Description + `"
CANONICAL_NAME="` + config.CanonicalName() + `"
CONF_DIR="` + config.DefaultConfDir() + `"
CONF_FILE="` + config.DefaultConfPath() + `"
INSTALL_DIR="` + config.DefaultInstallDir() + `"
INSTALL_FILE="` + config.DefaultInstallPath() + `"
UNIT_NAME="` + config.DefaultSysDUnitName() + `"
UNIT_FILE="` + config.DefaultSysDUnitFilePath() + `"
DOCS_DIR="` + config.DefaultDocsDir() + `"
`
	return ioutil.WriteFile("install/vars.sh", []byte(content), 0755)
}

func buildMicroservice(goos, goarch, goarm string) error {
	docsDir := path.Join("install", "docs", config.VersionMajorPrefixed(), config.Name, "docs")
	if err := compileDocs(docsDir); err != nil {
		return err
	}
	args := []string{"build", "-o", "bin/app", "./cmd/micro"}
	cmd := exec.Command("go", args...)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = os.Environ()
	for _, env := range []string{
		"GOOS=" + goos,
		"GOARCH=" + goarch,
		"GOARM=" + goarm,
	} {
		cmd.Env = append(cmd.Env, env)
	}
	if out, err := cmd.CombinedOutput(); err != nil {
		return errors.Newf("build: %s - %v", out, err)
	}
	return nil
}

func buildGcloud() error {
	confDir := config.DefaultConfDir("cmd", "gcloud", "conf")

	if err := os.MkdirAll(confDir, 0755); err != nil {
		return errors.Newf("create conf dir: %v", err)
	}

	docsDir := path.Join(config.DefaultDocsDir(), config.VersionMajorPrefixed(), config.Name, "docs")
	if err := compileDocs(docsDir); err != nil {
		return err
	}

	err := fileutils.CopyIfDestNotExists(path.Join("install", "conf.yml"), config.DefaultConfPath())
	if err != nil {
		return err
	}
	if err := cleanGCloudConfFile(); err != nil {
		return errors.Newf("clean gcloud config file: %v", err)
	}

	return nil
}

func compileDocs(docsDir string) error {

	subjDir := path.Join("pkg", "handler", "http")
	headerFile := path.Join(subjDir, "apidoc_header.md")
	APIDocConfFile := path.Join(subjDir, "apidoc.json")

	apiDoc := struct {
		Name        string      `json:"name"`
		Version     string      `json:"version"`
		Description string      `json:"description"`
		Title       string      `json:"title"`
		Header      interface{} `json:"header"`
	}{
		Name:        config.Name,
		Version:     config.VersionFull,
		Description: config.Description,
		Title:       config.CanonicalName(),
		Header: struct {
			Title    string `json:"title"`
			FileName string `json:"filename"`
		}{
			Title:    "Introduction",
			FileName: headerFile,
		},
	}

	apiDocB, err := json.Marshal(apiDoc)
	if err != nil {
		return errors.Newf("Marshal API doc config: %v", err)
	}

	err = ioutil.WriteFile(APIDocConfFile, apiDocB, 0655)
	if err != nil {
		return errors.Newf("Write API doc file: %v", err)
	}

	args := []string{"-i", subjDir, "-c", subjDir, "-o", docsDir}
	cmd := exec.Command("apidoc", args...)
	if out, err := cmd.CombinedOutput(); err != nil {
		return errors.Newf("generate http docs: %v: %s", err, out)
	}
	return nil
}

func cleanGCloudConfFile() error {
	newPath := config.DefaultConfPath()
	confContent, err := ioutil.ReadFile(config.DefaultConfPath())
	if err != nil {
		return errors.Newf("read file for transform: %v", err)
	}
	confContentClean := bytes.Replace(confContent, []byte(config.SysDConfDir()+"/"), []byte("conf/"), -1)
	err = ioutil.WriteFile(newPath, confContentClean, 0644)
	if err != nil {
		return errors.Newf("write transformed file: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 Brian Danowski <briandanowski@gmail.com>
// 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 cmd

import (
	"compress/gzip"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

var err error

// splitCmd represents the split command
var splitCmd = &cobra.Command{
	Use:   "split /path/to/file/to/split",
	Short: "split a file into chunks",
	Long: `Split a file into chunks

			use -r to specify rows per file
	`,
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) < 1 {
			cmd.Usage()
			return
		}
		flags := cmd.Flags()
		lazy, err := flags.GetBool("lazy-quotes")
		if err != nil {
			log.Fatal(err)
		}

		rowsPerFile, err := flags.GetInt64("rows-per-file")
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("split called")
		fmt.Println(args[0])
		inPath := args[0]

		path, err := split(inPath, rowsPerFile, lazy)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(inPath + " split into " + path)

	},
}

func init() {
	RootCmd.AddCommand(splitCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// splitCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// splitCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}

func getBaseBase(base string) (string, error) {
	baseBase := base[0 : len(base)-len(filepath.Ext(base))]
	ext := filepath.Ext(baseBase)
	switch ext {
	case ".csv":
		return getBaseBase(baseBase)
	case "":
		return baseBase, nil
	default:
		return "", errors.New("Unknown file ext. Known file exts are .csv and .csv.gz")
	}
}

func getFolderName(base string) string {

	baseBase, err := getBaseBase(base)
	if err != nil {
		log.Fatal(err)
	}

	ret := baseBase + "-split"
	if _, err := os.Stat(ret); err != nil {
		return ret

	}

	// this needs its own function, but tonight i need to go to seleep
	i := 1
	for {
		if _, err = os.Stat(ret + "_" + strconv.Itoa(i)); os.IsNotExist(err) {
			return ret + "_" + strconv.Itoa(i)
		}
		i++

	}
}

// split returns the path of folder that the files were split into
func split(path string, rowsPerFile int64, lazyQuotes bool) (string, error) {
	fmt.Println(path)

	base := filepath.Base(path)

	folderName := getFolderName(base)

	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	var cr *csv.Reader
	if filepath.Ext(filepath.Base(path)) == ".gz" {
		gr, err := gzip.NewReader(f)
		if err != nil {
			log.Fatal(err)
		}

		cr = csv.NewReader(gr)
	} else {
		cr = csv.NewReader(f)
	}

	cr.LazyQuotes = lazyQuotes
	header, err := cr.Read()
	if err != nil {
		return "", err
	}
	if _, err := os.Stat(folderName); os.IsNotExist(err) {
		os.Mkdir(folderName, 0700)
	}

	i := 0
	for {

		fileIndex := strconv.Itoa(i + 1)
		outFilename := base[:len(base)-4] + "_" + fileIndex + ".csv"
		out, err := os.OpenFile(filepath.Join(folderName, outFilename), os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			return "", err
		}

		cw := csv.NewWriter(out)

		// read before writing the header just incase we are about
		// to hit EOF so we don't end up with a header only file
		row1, err := cr.Read()
		if err == io.EOF {
			return "", nil
		} else if err != nil {
			return "", err
		}

		cw.Write(header)
		cw.Write(row1)

		for j := int64(0); j < rowsPerFile; j++ {
			row, err := cr.Read()
			if err == io.EOF {
				return folderName, nil
			} else if err != nil {
				return "", err
			}

			cw.Write(row)
		}
		// closing explicity because it's possible to have too many files open
		cw.Flush()
		err = out.Close()
		if err != nil {
			log.Fatal(errors.Wrap(err, "closing file"))
		}
		i++
	}

}
<commit_msg>split into .csv.gz with --compressed<commit_after>// Copyright © 2017 Brian Danowski <briandanowski@gmail.com>
// 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 cmd

import (
	"bufio"
	"compress/gzip"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

var err error

// splitCmd represents the split command
var splitCmd = &cobra.Command{
	Use:   "split /path/to/file/to/split",
	Short: "split a file into chunks",
	Long: `Split a file into chunks

			use -r to specify rows per file
	`,
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) < 1 {
			cmd.Usage()
			return
		}
		flags := cmd.Flags()
		lazy, err := flags.GetBool("lazy-quotes")
		if err != nil {
			log.Fatal(err)
		}

		rowsPerFile, err := flags.GetInt64("rows-per-file")
		if err != nil {
			log.Fatal(err)
		}

		compressed, err := flags.GetBool("compressed")
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("split called")
		fmt.Println(args[0])
		inPath := args[0]

		path, err := split(inPath, rowsPerFile, lazy, compressed)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(inPath + " split into " + path)

	},
}

func init() {
	RootCmd.AddCommand(splitCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// splitCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// splitCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	splitCmd.Flags().BoolP("compressed", "", false, "With this flag, split will output .csv.gz instead of .csv")

}

func getBaseBase(base string) (string, error) {
	baseBase := base[0 : len(base)-len(filepath.Ext(base))]
	ext := filepath.Ext(baseBase)
	switch ext {
	case ".csv":
		return getBaseBase(baseBase)
	case "":
		return baseBase, nil
	default:
		return "", errors.New("Unknown file ext. Known file exts are .csv and .csv.gz")
	}
}

func getFolderName(base string) string {

	baseBase, err := getBaseBase(base)
	if err != nil {
		log.Fatal(err)
	}

	ret := baseBase + "-split"
	if _, err := os.Stat(ret); err != nil {
		return ret

	}

	// this needs its own function, but tonight i need to go to seleep
	i := 1
	for {
		if _, err = os.Stat(ret + "_" + strconv.Itoa(i)); os.IsNotExist(err) {
			return ret + "_" + strconv.Itoa(i)
		}
		i++

	}
}

// split returns the path of folder that the files were split into
func split(path string, rowsPerFile int64, lazyQuotes, compressed bool) (string, error) {
	fmt.Println(path)

	base := filepath.Base(path)

	folderName := getFolderName(base)

	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	var cr *csv.Reader
	if filepath.Ext(filepath.Base(path)) == ".gz" {
		gr, err := gzip.NewReader(f)
		if err != nil {
			log.Fatal(err)
		}

		cr = csv.NewReader(gr)
	} else {
		cr = csv.NewReader(f)
	}

	cr.LazyQuotes = lazyQuotes
	header, err := cr.Read()
	if err != nil {
		return "", err
	}
	if _, err := os.Stat(folderName); os.IsNotExist(err) {
		os.Mkdir(folderName, 0700)
	}

	i := 0
	for {

		fileIndex := strconv.Itoa(i + 1)
		outFilename := base[:len(base)-len(filepath.Ext(base))] + "_" + fileIndex + ".csv"
		if compressed {
			outFilename = outFilename + ".gz"
		}
		out, err := os.OpenFile(filepath.Join(folderName, outFilename), os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			return "", err
		}

		var cw *csv.Writer
		var gw *gzip.Writer
		var bw *bufio.Writer

		if compressed {
			gw = gzip.NewWriter(out)
			bw = bufio.NewWriter(gw)
			cw = csv.NewWriter(bw)

		} else {
			cw = csv.NewWriter(out)
		}

		// read before writing the header just incase we are about
		// to hit EOF so we don't end up with a header only file
		row1, err := cr.Read()
		if err == io.EOF {
			return "", nil
		} else if err != nil {
			return "", err
		}

		cw.Write(header)
		cw.Write(row1)

		for j := int64(0); j < rowsPerFile; j++ {
			row, err := cr.Read()
			if err == io.EOF {
				return folderName, nil
			} else if err != nil {
				return "", err
			}

			cw.Write(row)
		}
		// closing explicity because it's possible to have too many files open
		if compressed {
			bw.Flush()
			gw.Flush()
			gw.Close()
		}
		cw.Flush()
		err = out.Close()
		if err != nil {
			log.Fatal(errors.Wrap(err, "closing file"))
		}
		i++
	}

}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"sync"

	log "github.com/Sirupsen/logrus"
	"k8s.io/kubernetes/pkg/api"
)

// store implements the k8s framework ResourceEventHandler interface.
type store struct {
	defaultRole string
	iamRoleKey  string
	mutex       sync.RWMutex
	rolesByIP   map[string]string
}

// Get returns the iam role based on IP address.
func (s *store) Get(IP string) (string, error) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if role, ok := s.rolesByIP[IP]; ok {
		return role, nil
	}
	if s.defaultRole != "" {
		log.Warnf("Using fallback role for IP %s", IP)
		return s.defaultRole, nil
	}
	return "", fmt.Errorf("Unable to find role for IP %s", IP)
}

// OnAdd is called when a pod is added.
func (s *store) OnAdd(obj interface{}) {
	if pod, ok := obj.(*api.Pod); ok {
		if pod.Status.PodIP != "" {
			if role, ok := pod.Annotations[s.iamRoleKey]; ok {
				s.mutex.Lock()
				s.rolesByIP[pod.Status.PodIP] = role
				s.mutex.Unlock()
			}
		}
	}
}

// OnUpdate is called when a pod is modified.
func (s *store) OnUpdate(oldObj, newObj interface{}) {
	oldPod, okOld := oldObj.(*api.Pod)
	newPod, okNew := newObj.(*api.Pod)

	// Validate that the objects are good
	if okOld && okNew {
		if oldPod.Status.PodIP != newPod.Status.PodIP {
			s.OnDelete(oldPod)
			s.OnAdd(newPod)
		}
	} else if okNew {
		s.OnAdd(newPod)
	} else if okOld {
		s.OnDelete(oldPod)
	}
}

// OnDelete is called when a pod is deleted.
func (s *store) OnDelete(obj interface{}) {
	if pod, ok := obj.(*api.Pod); ok {
		if pod.Status.PodIP != "" {
			s.mutex.Lock()
			delete(s.rolesByIP, pod.Status.PodIP)
			s.mutex.Unlock()
		}
	}
}

func newStore(key string, defaultRole string) *store {
	return &store{
		defaultRole: defaultRole,
		iamRoleKey:  key,
		rolesByIP:   make(map[string]string),
	}
}
<commit_msg>Get rid of error swallowing in store & handle DeletedFinalStateUnknown (#18)<commit_after>package cmd

import (
	"fmt"
	"sync"

	log "github.com/Sirupsen/logrus"
	"k8s.io/kubernetes/pkg/api"
	kcache "k8s.io/kubernetes/pkg/client/cache"
)

// store implements the k8s framework ResourceEventHandler interface.
type store struct {
	defaultRole string
	iamRoleKey  string
	mutex       sync.RWMutex
	rolesByIP   map[string]string
}

// Get returns the iam role based on IP address.
func (s *store) Get(IP string) (string, error) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if role, ok := s.rolesByIP[IP]; ok {
		return role, nil
	}
	if s.defaultRole != "" {
		log.Warnf("Using fallback role for IP %s", IP)
		return s.defaultRole, nil
	}
	return "", fmt.Errorf("Unable to find role for IP %s", IP)
}

// OnAdd is called when a pod is added.
func (s *store) OnAdd(obj interface{}) {
	pod, ok := obj.(*api.Pod)
	if !ok {
		log.Errorf("Bad object in OnAdd %+v", obj)
		return
	}

	if pod.Status.PodIP != "" {
		if role, ok := pod.Annotations[s.iamRoleKey]; ok {
			s.mutex.Lock()
			s.rolesByIP[pod.Status.PodIP] = role
			s.mutex.Unlock()
		}
	}
}

// OnUpdate is called when a pod is modified.
func (s *store) OnUpdate(oldObj, newObj interface{}) {
	oldPod, ok1 := oldObj.(*api.Pod)
	newPod, ok2 := newObj.(*api.Pod)
	if !ok1 || !ok2 {
		log.Errorf("Bad call to OnUpdate %+v %+v", oldObj, newObj)
		return
	}

	if oldPod.Status.PodIP != newPod.Status.PodIP {
		s.OnDelete(oldPod)
		s.OnAdd(newPod)
	}
}

// OnDelete is called when a pod is deleted.
func (s *store) OnDelete(obj interface{}) {
	pod, ok := obj.(*api.Pod)
	if !ok {
		deletedObj, dok := obj.(kcache.DeletedFinalStateUnknown)
		if dok {
			pod, ok = deletedObj.(*api.Pod)
		}
	}

	if !ok {
		log.Errorf("Bad call to OnUpdate %+v %+v", oldObj, newObj)
		return
	}

	if pod.Status.PodIP != "" {
		s.mutex.Lock()
		delete(s.rolesByIP, pod.Status.PodIP)
		s.mutex.Unlock()
	}
}

func newStore(key string, defaultRole string) *store {
	return &store{
		defaultRole: defaultRole,
		iamRoleKey:  key,
		rolesByIP:   make(map[string]string),
	}
}
<|endoftext|>


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

// On-disk mutex protecting a resource
//
// A lock is represented on disk by a directory of a particular name,
// containing an information file.  Taking a lock is done by renaming a
// temporary directory into place.  We use temporary directories because for
// all filesystems we believe that exactly one attempt to claim the lock will
// succeed and the others will fail.
package fslock

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"time"

	"github.com/juju/loggo"

	"launchpad.net/juju-core/utils"
)

const (
	// NameRegexp specifies the regular expression used to identify valid lock names.
	NameRegexp      = "^[a-z]+[a-z0-9.-]*$"
	heldFilename    = "held"
	messageFilename = "message"
)

var (
	logger         = loggo.GetLogger("juju.utils.fslock")
	ErrLockNotHeld = errors.New("lock not held")
	ErrTimeout     = errors.New("lock timeout exceeded")

	validName = regexp.MustCompile(NameRegexp)

	LockWaitDelay = 1 * time.Second
)

type Lock struct {
	name   string
	parent string
	nonce  []byte
}

// NewLock returns a new lock with the given name within the given lock
// directory, without acquiring it. The lock name must match the regular
// expression defined by NameRegexp.
func NewLock(lockDir, name string) (*Lock, error) {
	if !validName.MatchString(name) {
		return nil, fmt.Errorf("Invalid lock name %q.  Names must match %q", name, NameRegexp)
	}
	nonce, err := utils.NewUUID()
	if err != nil {
		return nil, err
	}
	lock := &Lock{
		name:   name,
		parent: lockDir,
		nonce:  nonce[:],
	}
	// Ensure the parent exists.
	if err := os.MkdirAll(lock.parent, 0755); err != nil {
		return nil, err
	}
	return lock, nil
}

func (lock *Lock) lockDir() string {
	return path.Join(lock.parent, lock.name)
}

func (lock *Lock) heldFile() string {
	return path.Join(lock.lockDir(), "held")
}

func (lock *Lock) messageFile() string {
	return path.Join(lock.lockDir(), "message")
}

// If message is set, it will write the message to the lock directory as the
// lock is taken.
func (lock *Lock) acquire(message string) (bool, error) {
	// If the lockDir exists, then the lock is held by someone else.
	_, err := os.Stat(lock.lockDir())
	if err == nil {
		return false, nil
	}
	if !os.IsNotExist(err) {
		return false, err
	}
	// Create a temporary directory (in the parent dir), and then move it to
	// the right name.  Using the same directory to make sure the directories
	// are on the same filesystem.  Use a directory name starting with "." as
	// it isn't a valid lock name.
	tempLockName := fmt.Sprintf(".%x", lock.nonce)
	tempDirName, err := ioutil.TempDir(lock.parent, tempLockName)
	if err != nil {
		return false, err // this shouldn't really fail...
	}
	// write nonce into the temp dir
	err = ioutil.WriteFile(path.Join(tempDirName, heldFilename), lock.nonce, 0755)
	if err != nil {
		return false, err
	}
	if message != "" {
		err = ioutil.WriteFile(path.Join(tempDirName, messageFilename), []byte(message), 0755)
		if err != nil {
			return false, err
		}
	}
	// Now move the temp directory to the lock directory.
	err = utils.ReplaceFile(tempDirName, lock.lockDir())
	if err != nil {
		// Any error on rename means we failed.
		// Beaten to it, clean up temporary directory.
		os.RemoveAll(tempDirName)
		return false, nil
	}
	// We now have the lock.
	return true, nil
}

// lockLoop tries to acquire the lock. If the acquisition fails, the
// continueFunc is run to see if the function should continue waiting.
func (lock *Lock) lockLoop(message string, continueFunc func() error) error {
	var heldMessage = ""
	for {
		acquired, err := lock.acquire(message)
		if err != nil {
			return err
		}
		if acquired {
			return nil
		}
		if err = continueFunc(); err != nil {
			return err
		}
		currMessage := lock.Message()
		if currMessage != heldMessage {
			logger.Infof("attempted lock failed %q, %s, currently held: %s", lock.name, message, currMessage)
			heldMessage = currMessage
		}
		time.Sleep(LockWaitDelay)
	}
}

// Lock blocks until it is able to acquire the lock.  Since we are dealing
// with sharing and locking using the filesystem, it is good behaviour to
// provide a message that is saved with the lock.  This is output in debugging
// information, and can be queried by any other Lock dealing with the same
// lock name and lock directory.
func (lock *Lock) Lock(message string) error {
	// The continueFunc is effectively a no-op, causing continual looping
	// until the lock is acquired.
	continueFunc := func() error { return nil }
	return lock.lockLoop(message, continueFunc)
}

// LockWithTimeout tries to acquire the lock. If it cannot acquire the lock
// within the given duration, it returns ErrTimeout.  See `Lock` for
// information about the message.
func (lock *Lock) LockWithTimeout(duration time.Duration, message string) error {
	deadline := time.Now().Add(duration)
	continueFunc := func() error {
		if time.Now().After(deadline) {
			return ErrTimeout
		}
		return nil
	}
	return lock.lockLoop(message, continueFunc)
}

// Lock blocks until it is able to acquire the lock.  If the lock is failed to
// be acquired, the continueFunc is called prior to the sleeping.  If the
// continueFunc returns an error, that error is returned from LockWithFunc.
func (lock *Lock) LockWithFunc(message string, continueFunc func() error) error {
	return lock.lockLoop(message, continueFunc)
}

// IsHeld returns whether the lock is currently held by the receiver.
func (lock *Lock) IsLockHeld() bool {
	heldNonce, err := ioutil.ReadFile(lock.heldFile())
	if err != nil {
		return false
	}
	return bytes.Equal(heldNonce, lock.nonce)
}

// Unlock releases a held lock.  If the lock is not held ErrLockNotHeld is
// returned.
func (lock *Lock) Unlock() error {
	if !lock.IsLockHeld() {
		return ErrLockNotHeld
	}
	// To ensure reasonable unlocking, we should rename to a temp name, and delete that.
	tempLockName := fmt.Sprintf(".%s.%x", lock.name, lock.nonce)
	tempDirName := path.Join(lock.parent, tempLockName)
	// Now move the lock directory to the temp directory to release the lock.
	if err := utils.ReplaceFile(lock.lockDir(), tempDirName); err != nil {
		return err
	}
	// And now cleanup.
	return os.RemoveAll(tempDirName)
}

// IsLocked returns true if the lock is currently held by anyone.
func (lock *Lock) IsLocked() bool {
	_, err := os.Stat(lock.heldFile())
	return err == nil
}

// BreakLock forcably breaks the lock that is currently being held.
func (lock *Lock) BreakLock() error {
	return os.RemoveAll(lock.lockDir())
}

// Message returns the saved message, or the empty string if there is no
// saved message.
func (lock *Lock) Message() string {
	message, err := ioutil.ReadFile(lock.messageFile())
	if err != nil {
		return ""
	}
	return string(message)
}
<commit_msg>Make doc comments match function names<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// On-disk mutex protecting a resource
//
// A lock is represented on disk by a directory of a particular name,
// containing an information file.  Taking a lock is done by renaming a
// temporary directory into place.  We use temporary directories because for
// all filesystems we believe that exactly one attempt to claim the lock will
// succeed and the others will fail.
package fslock

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"time"

	"github.com/juju/loggo"

	"launchpad.net/juju-core/utils"
)

const (
	// NameRegexp specifies the regular expression used to identify valid lock names.
	NameRegexp      = "^[a-z]+[a-z0-9.-]*$"
	heldFilename    = "held"
	messageFilename = "message"
)

var (
	logger         = loggo.GetLogger("juju.utils.fslock")
	ErrLockNotHeld = errors.New("lock not held")
	ErrTimeout     = errors.New("lock timeout exceeded")

	validName = regexp.MustCompile(NameRegexp)

	LockWaitDelay = 1 * time.Second
)

type Lock struct {
	name   string
	parent string
	nonce  []byte
}

// NewLock returns a new lock with the given name within the given lock
// directory, without acquiring it. The lock name must match the regular
// expression defined by NameRegexp.
func NewLock(lockDir, name string) (*Lock, error) {
	if !validName.MatchString(name) {
		return nil, fmt.Errorf("Invalid lock name %q.  Names must match %q", name, NameRegexp)
	}
	nonce, err := utils.NewUUID()
	if err != nil {
		return nil, err
	}
	lock := &Lock{
		name:   name,
		parent: lockDir,
		nonce:  nonce[:],
	}
	// Ensure the parent exists.
	if err := os.MkdirAll(lock.parent, 0755); err != nil {
		return nil, err
	}
	return lock, nil
}

func (lock *Lock) lockDir() string {
	return path.Join(lock.parent, lock.name)
}

func (lock *Lock) heldFile() string {
	return path.Join(lock.lockDir(), "held")
}

func (lock *Lock) messageFile() string {
	return path.Join(lock.lockDir(), "message")
}

// If message is set, it will write the message to the lock directory as the
// lock is taken.
func (lock *Lock) acquire(message string) (bool, error) {
	// If the lockDir exists, then the lock is held by someone else.
	_, err := os.Stat(lock.lockDir())
	if err == nil {
		return false, nil
	}
	if !os.IsNotExist(err) {
		return false, err
	}
	// Create a temporary directory (in the parent dir), and then move it to
	// the right name.  Using the same directory to make sure the directories
	// are on the same filesystem.  Use a directory name starting with "." as
	// it isn't a valid lock name.
	tempLockName := fmt.Sprintf(".%x", lock.nonce)
	tempDirName, err := ioutil.TempDir(lock.parent, tempLockName)
	if err != nil {
		return false, err // this shouldn't really fail...
	}
	// write nonce into the temp dir
	err = ioutil.WriteFile(path.Join(tempDirName, heldFilename), lock.nonce, 0755)
	if err != nil {
		return false, err
	}
	if message != "" {
		err = ioutil.WriteFile(path.Join(tempDirName, messageFilename), []byte(message), 0755)
		if err != nil {
			return false, err
		}
	}
	// Now move the temp directory to the lock directory.
	err = utils.ReplaceFile(tempDirName, lock.lockDir())
	if err != nil {
		// Any error on rename means we failed.
		// Beaten to it, clean up temporary directory.
		os.RemoveAll(tempDirName)
		return false, nil
	}
	// We now have the lock.
	return true, nil
}

// lockLoop tries to acquire the lock. If the acquisition fails, the
// continueFunc is run to see if the function should continue waiting.
func (lock *Lock) lockLoop(message string, continueFunc func() error) error {
	var heldMessage = ""
	for {
		acquired, err := lock.acquire(message)
		if err != nil {
			return err
		}
		if acquired {
			return nil
		}
		if err = continueFunc(); err != nil {
			return err
		}
		currMessage := lock.Message()
		if currMessage != heldMessage {
			logger.Infof("attempted lock failed %q, %s, currently held: %s", lock.name, message, currMessage)
			heldMessage = currMessage
		}
		time.Sleep(LockWaitDelay)
	}
}

// Lock blocks until it is able to acquire the lock.  Since we are dealing
// with sharing and locking using the filesystem, it is good behaviour to
// provide a message that is saved with the lock.  This is output in debugging
// information, and can be queried by any other Lock dealing with the same
// lock name and lock directory.
func (lock *Lock) Lock(message string) error {
	// The continueFunc is effectively a no-op, causing continual looping
	// until the lock is acquired.
	continueFunc := func() error { return nil }
	return lock.lockLoop(message, continueFunc)
}

// LockWithTimeout tries to acquire the lock. If it cannot acquire the lock
// within the given duration, it returns ErrTimeout.  See `Lock` for
// information about the message.
func (lock *Lock) LockWithTimeout(duration time.Duration, message string) error {
	deadline := time.Now().Add(duration)
	continueFunc := func() error {
		if time.Now().After(deadline) {
			return ErrTimeout
		}
		return nil
	}
	return lock.lockLoop(message, continueFunc)
}

// LockWithFunc blocks until it is able to acquire the lock.  If the lock is failed to
// be acquired, the continueFunc is called prior to the sleeping.  If the
// continueFunc returns an error, that error is returned from LockWithFunc.
func (lock *Lock) LockWithFunc(message string, continueFunc func() error) error {
	return lock.lockLoop(message, continueFunc)
}

// IsLockHeld returns whether the lock is currently held by the receiver.
func (lock *Lock) IsLockHeld() bool {
	heldNonce, err := ioutil.ReadFile(lock.heldFile())
	if err != nil {
		return false
	}
	return bytes.Equal(heldNonce, lock.nonce)
}

// Unlock releases a held lock.  If the lock is not held ErrLockNotHeld is
// returned.
func (lock *Lock) Unlock() error {
	if !lock.IsLockHeld() {
		return ErrLockNotHeld
	}
	// To ensure reasonable unlocking, we should rename to a temp name, and delete that.
	tempLockName := fmt.Sprintf(".%s.%x", lock.name, lock.nonce)
	tempDirName := path.Join(lock.parent, tempLockName)
	// Now move the lock directory to the temp directory to release the lock.
	if err := utils.ReplaceFile(lock.lockDir(), tempDirName); err != nil {
		return err
	}
	// And now cleanup.
	return os.RemoveAll(tempDirName)
}

// IsLocked returns true if the lock is currently held by anyone.
func (lock *Lock) IsLocked() bool {
	_, err := os.Stat(lock.heldFile())
	return err == nil
}

// BreakLock forcably breaks the lock that is currently being held.
func (lock *Lock) BreakLock() error {
	return os.RemoveAll(lock.lockDir())
}

// Message returns the saved message, or the empty string if there is no
// saved message.
func (lock *Lock) Message() string {
	message, err := ioutil.ReadFile(lock.messageFile())
	if err != nil {
		return ""
	}
	return string(message)
}
<|endoftext|>


<commit_before>package main

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

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"github.com/mana-fwk/hwaf/hwaflib"
	gocfg "github.com/sbinet/go-config/config"
)

func hwaf_make_cmd_setup() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_setup,
		UsageLine: "setup [options] <workarea>",
		Short:     "setup an existing workarea",
		Long: `
setup sets up an existing workarea.

ex:
 $ hwaf setup
 $ hwaf setup .
 $ hwaf setup my-work-area
 $ hwaf setup -p=/opt/sw/mana/mana-core/20121207 my-work-area
 $ hwaf setup -p=/path1:/path2 my-work-area
 $ hwaf setup -cfg=${HWAF_CFG}/usr.cfg my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-setup", flag.ExitOnError),
	}
	cmd.Flag.String("p", "", "List of paths to projects to setup against")
	cmd.Flag.String("cfg", "", "Path to a configuration file")
	cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed")

	return cmd
}

func hwaf_run_cmd_setup(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := "."
	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	cfg_fname := cmd.Flag.Lookup("cfg").Value.Get().(string)

	projdirs := []string{}
	const pathsep = string(os.PathListSeparator)
	for _, v := range strings.Split(cmd.Flag.Lookup("p").Value.Get().(string), pathsep) {
		if v != "" {
			v = os.ExpandEnv(v)
			v = filepath.Clean(v)
			projdirs = append(projdirs, v)
		}
	}

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]...\n", n, dirname)
		fmt.Printf("%s: projects=%v\n", n, projdirs)
		if cfg_fname != "" {
			fmt.Printf("%s: cfg-file=%s\n", n, cfg_fname)
		}
	}

	for _, projdir := range projdirs {
		if !path_exists(projdir) {
			err = fmt.Errorf("no such directory: [%s]", projdir)
			handle_err(err)
		}

		pinfo := filepath.Join(projdir, "project.info")
		if !path_exists(pinfo) {
			err = fmt.Errorf("no such file: [%s]", pinfo)
			handle_err(err)
		}
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: create local config...\n", n)
	}

	lcfg_fname := filepath.Join(".hwaf", "local.conf")
	if path_exists(lcfg_fname) {
		err = os.Remove(lcfg_fname)
		handle_err(err)
	}

	lcfg := gocfg.NewDefault()
	section := "hwaf-cfg"
	if !lcfg.AddSection(section) {
		err = fmt.Errorf("%s: could not create section [%s] in file [%s]",
			n, section, lcfg_fname)
		handle_err(err)
	}

	// fetch a few informations from the first project.info
	cmtcfg := g_ctx.DefaultCmtcfg()
	//projvers := time.Now().Format("20060102")
	if len(projdirs) > 0 {
		pinfo, err := hwaflib.NewProjectInfo(filepath.Join(projdirs[0], "project.info"))
		handle_err(err)
		cmtcfg, err = pinfo.Get("CMTCFG")
		handle_err(err)
	}

	for k, v := range map[string]string{
		"projects": strings.Join(projdirs, pathsep),
		"cmtpkgs":  "src",
		"cmtcfg":   cmtcfg,
	} {
		if !lcfg.AddOption(section, k, v) {
			err := fmt.Errorf("%s: could not add option [%s] to section [%s]",
				n, k, section,
			)
			handle_err(err)
		}
	}

	err = lcfg.WriteFile(lcfg_fname, 0600, "")
	handle_err(err)

	// add local config to git-repo
	git := exec.Command(
		"git", "add", lcfg_fname,
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// commit
	git = exec.Command(
		"git", "commit", "-m", "adding local config",
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<commit_msg>setup: better cmtcfg default + handle hysteresis in local.conf<commit_after>package main

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

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"github.com/mana-fwk/hwaf/hwaflib"
	gocfg "github.com/sbinet/go-config/config"
)

func hwaf_make_cmd_setup() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_setup,
		UsageLine: "setup [options] <workarea>",
		Short:     "setup an existing workarea",
		Long: `
setup sets up an existing workarea.

ex:
 $ hwaf setup
 $ hwaf setup .
 $ hwaf setup my-work-area
 $ hwaf setup -p=/opt/sw/mana/mana-core/20121207 my-work-area
 $ hwaf setup -p=/path1:/path2 my-work-area
 $ hwaf setup -cfg=${HWAF_CFG}/usr.cfg my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-setup", flag.ExitOnError),
	}
	cmd.Flag.String("p", "", "List of paths to projects to setup against")
	cmd.Flag.String("cfg", "", "Path to a configuration file")
	cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed")

	return cmd
}

func hwaf_run_cmd_setup(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := "."
	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	cfg_fname := cmd.Flag.Lookup("cfg").Value.Get().(string)

	projdirs := []string{}
	const pathsep = string(os.PathListSeparator)
	for _, v := range strings.Split(cmd.Flag.Lookup("p").Value.Get().(string), pathsep) {
		if v != "" {
			v = os.ExpandEnv(v)
			v = filepath.Clean(v)
			projdirs = append(projdirs, v)
		}
	}

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]...\n", n, dirname)
		fmt.Printf("%s: projects=%v\n", n, projdirs)
		if cfg_fname != "" {
			fmt.Printf("%s: cfg-file=%s\n", n, cfg_fname)
		}
	}

	for _, projdir := range projdirs {
		if !path_exists(projdir) {
			err = fmt.Errorf("no such directory: [%s]", projdir)
			handle_err(err)
		}

		pinfo := filepath.Join(projdir, "project.info")
		if !path_exists(pinfo) {
			err = fmt.Errorf("no such file: [%s]", pinfo)
			handle_err(err)
		}
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: create local config...\n", n)
	}

	var lcfg *gocfg.Config
	lcfg_fname := filepath.Join(".hwaf", "local.conf")
	if path_exists(lcfg_fname) {
		lcfg, err = gocfg.ReadDefault(lcfg_fname)
		handle_err(err)
	} else {
		lcfg = gocfg.NewDefault()
	}

	section := "hwaf-cfg"
	if !lcfg.HasSection(section) && !lcfg.AddSection(section) {
		err = fmt.Errorf("%s: could not create section [%s] in file [%s]",
			n, section, lcfg_fname)
		handle_err(err)
	}

	// fetch a few informations from the first project.info
	cmtcfg := g_ctx.Cmtcfg()
	//projvers := time.Now().Format("20060102")
	if len(projdirs) > 0 {
		pinfo, err := hwaflib.NewProjectInfo(filepath.Join(projdirs[0], "project.info"))
		handle_err(err)
		cmtcfg, err = pinfo.Get("CMTCFG")
		handle_err(err)
	}

	for k, v := range map[string]string{
		"projects": strings.Join(projdirs, pathsep),
		"cmtpkgs":  "src",
		"cmtcfg":   cmtcfg,
	} {
		if lcfg.HasOption(section, k) {
			lcfg.RemoveOption(section, k)
		}
		if !lcfg.AddOption(section, k, v) {
			err := fmt.Errorf("%s: could not add option [%s] to section [%s]",
				n, k, section,
			)
			handle_err(err)
		}
	}

	err = lcfg.WriteFile(lcfg_fname, 0600, "")
	handle_err(err)

	// add local config to git-repo
	git := exec.Command(
		"git", "add", lcfg_fname,
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// commit
	git = exec.Command(
		"git", "commit", "-m", "adding local config",
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<|endoftext|>


<commit_before>package funcache

import (
	"runtime"
	"testing"
	"time"

	"github.com/hashicorp/golang-lru"
	"github.com/stretchr/testify/assert"
)

type noisyTestStore struct {
	t *testing.T
	m map[interface{}]interface{}
}

func noisyTestCache(t *testing.T) *Cache {
	return New(&noisyTestStore{t: t, m: make(map[interface{}]interface{})})
}

func (ts *noisyTestStore) Add(key, value interface{}) {
	ts.m[key] = value
	ts.t.Logf("Add(%v, %v)", key, value)
}

func (ts *noisyTestStore) Get(key interface{}) (value interface{}, ok bool) {
	value, ok = ts.m[key]
	ts.t.Logf("Get(%v) -> (%v, %v)", key, value, ok)
	return
}

// -----------------------------------------------------------------------------

func testGetCallingFuncs() (funcNames []string) {
	// Skip the first 3 callers:
	// 1. runtime.Callers
	// 2. github.com/aviddiviner/funcache.getAllCallers
	// 3. testGetCallingFuncs (this)
	pcs := getAllCallers(3)
	frames := runtime.CallersFrames(pcs)
	for {
		frame, more := frames.Next()
		funcNames = append(funcNames, frame.Function)
		if !more {
			break
		}
	}
	return
}

func TestCaller(t *testing.T) {
	fns := testGetCallingFuncs()
	var found bool
	for _, fn := range fns {
		if fn == "github.com/aviddiviner/go-funcache.TestCaller" {
			found = true
		}
	}
	assert.True(t, found)

	cache := nilCache()
	cache.Bust(func() {
		assert.True(t, wasCalledByCacheBustingFn())
	})
}

func TestWrapIsDistinct(t *testing.T) {
	cache := nilCache()

	getValueA := func() (value, caller string) {
		value = cache.Wrap(func() interface{} {
			caller = testGetCallingFuncs()[0]
			return "A"
		}).(string)
		return
	}
	valueA, callerA := getValueA()
	assert.Equal(t, "A", valueA)

	valueA, callerA1 := getValueA() // Inner func called again, because nilCache
	assert.Equal(t, "A", valueA)

	assert.Equal(t, callerA, callerA1)

	var callerB string
	valueB := cache.Wrap(func() interface{} {
		callerB = testGetCallingFuncs()[0]
		return "B"
	})
	assert.Equal(t, "B", valueB)

	assert.NotEqual(t, callerA, callerB)
}

func TestBasics(t *testing.T) {
	cache := noisyTestCache(t)

	var callCount int
	getFoo := func() string {
		return cache.Wrap(func() interface{} {
			callCount += 1
			return "Foo!"
		}).(string)
	}

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	cache.Bust(func() {
		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 2, callCount)

		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 3, callCount)
	})
}

func withTestTimeout(t *testing.T, millis int, fn func()) {
	done := make(chan bool)
	go func() {
		fn()
		done <- true
	}()
	select {
	case <-done:
	case <-time.After(time.Duration(millis) * time.Millisecond):
		t.Fatal("timed out")
	}
}

func TestFibonacci(t *testing.T) {
	cache := NewInMemCache()
	var fib func(k int) int
	fib = func(k int) int {
		if k < 2 {
			return k
		}
		a := cache.Cache(k-1, func() interface{} { return fib(k - 1) })
		b := cache.Cache(k-2, func() interface{} { return fib(k - 2) })
		return a.(int) + b.(int)
	}
	withTestTimeout(t, 500, func() { fib(36) }) // Retardedly slow without caching
}

func testCacheUse(t *testing.T, cache *Cache, key, val interface{}, bust bool) {
	var gotBust bool
	gotVal := cache.Cache(key, func() interface{} {
		gotBust = true
		return val
	})
	assert.Equal(t, val, gotVal)
	assert.Equal(t, bust, gotBust)
}

func TestNestedCachingAndBusting(t *testing.T) {
	cache := NewInMemCache()

	var callCount int
	getFoo := func() interface{} {
		return cache.Wrap(func() interface{} {
			callCount += 1
			return "Foo!"
		})
	}
	getBar := func() interface{} {
		return cache.Wrap(func() interface{} {
			getFoo()
			getFoo()
			callCount += 1
			return "Bar!"
		})
	}

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	assert.Equal(t, "Bar!", getBar())
	assert.Equal(t, 2, callCount)

	cache.Bust(func() {
		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 3, callCount)

		assert.Equal(t, "Bar!", getBar())
		assert.Equal(t, 6, callCount)

		func() {
			assert.Equal(t, "Bar!", getBar())
			assert.Equal(t, 9, callCount)
		}()

		cache.Bust(func() {
			assert.Equal(t, "Bar!", getBar())
			assert.Equal(t, 12, callCount)
		})
	})

	assert.Equal(t, "Bar!", getBar())
	assert.Equal(t, 12, callCount)
}

func TestBackedByAnotherStore(t *testing.T) {
	store, err := lru.New2Q(10)
	assert.NoError(t, err)
	cache := New(store)

	testCacheUse(t, cache, "foo", "Foo!", true)
	testCacheUse(t, cache, "foo", "Foo!", false)
	testCacheUse(t, cache, "bar", "Bar!", true)
	testCacheUse(t, cache, "foo", "Foo!", false)

	cache.Bust(func() {
		testCacheUse(t, cache, "bar", "Bar!", true)
		testCacheUse(t, cache, "foo", "Foo!", true)
	})

	testCacheUse(t, cache, "foo", "Foo!", false)
	testCacheUse(t, cache, "bar", "Bar!", false)
}

func TestCacheNil(t *testing.T) {
	cache := NewInMemCache()

	testCacheUse(t, cache, nil, "Foo!", true)
	testCacheUse(t, cache, nil, "Foo!", false)
}

func TestCacheMixedKeys(t *testing.T) {
	cache := NewInMemCache()

	testCacheUse(t, cache, "abc", "Foo!", true)
	testCacheUse(t, cache, 123, "Foo!", true)
}

func TestDeferredFuncs(t *testing.T) {
	cache := NewInMemCache()

	testCacheUse(t, cache, "foo", "Foo!", true)
	defer testCacheUse(t, cache, "foo", "Foo!", false)
	defer cache.Bust(func() {
		testCacheUse(t, cache, "foo", "Foo!", true)
	})
}

// -----------------------------------------------------------------------------

func BenchmarkUncached(b *testing.B) {
	for n := 0; n < b.N; n++ {
		func() interface{} {
			return "xyz"
		}()
	}
}

func BenchmarkCacheHitsMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheHitsCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheMisses(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheBusted(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}
func BenchmarkCacheBustedMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}
func BenchmarkCacheBustedCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}

func BenchmarkWrapHitsMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapHitsCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapMisses(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapBusted(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Wrap(func() interface{} {
				return "xyz"
			})
		})
	}
}
<commit_msg>Add some tests, benchmarks<commit_after>package funcache

import (
	"runtime"
	"testing"
	"time"

	"github.com/hashicorp/golang-lru"
	"github.com/stretchr/testify/assert"
)

type noisyTestStore struct {
	t *testing.T
	m map[interface{}]interface{}
}

func noisyTestCache(t *testing.T) *Cache {
	return New(&noisyTestStore{t: t, m: make(map[interface{}]interface{})})
}

func (ts *noisyTestStore) Add(key, value interface{}) {
	ts.m[key] = value
	ts.t.Logf("Add(%v, %v)", key, value)
}

func (ts *noisyTestStore) Get(key interface{}) (value interface{}, ok bool) {
	value, ok = ts.m[key]
	ts.t.Logf("Get(%v) -> (%v, %v)", key, value, ok)
	return
}

// -----------------------------------------------------------------------------

func testGetCallingFuncs() (funcNames []string) {
	// Skip the first 3 callers:
	// 1. runtime.Callers
	// 2. github.com/aviddiviner/funcache.getAllCallers
	// 3. testGetCallingFuncs (this)
	pcs := getAllCallers(3)
	frames := runtime.CallersFrames(pcs)
	for {
		frame, more := frames.Next()
		funcNames = append(funcNames, frame.Function)
		if !more {
			break
		}
	}
	return
}

func TestCaller(t *testing.T) {
	fns := testGetCallingFuncs()
	var found bool
	for _, fn := range fns {
		if fn == "github.com/aviddiviner/go-funcache.TestCaller" {
			found = true
		}
	}
	assert.True(t, found)

	cache := nilCache()
	cache.Bust(func() {
		assert.True(t, wasCalledByCacheBustingFn())
	})
}

func TestWrapIsDistinct(t *testing.T) {
	cache := nilCache()

	getValueA := func() (value, caller string) {
		value = cache.Wrap(func() interface{} {
			caller = testGetCallingFuncs()[0]
			return "A"
		}).(string)
		return
	}
	valueA, callerA := getValueA()
	assert.Equal(t, "A", valueA)

	valueA, callerA1 := getValueA() // Inner func called again, because nilCache
	assert.Equal(t, "A", valueA)

	assert.Equal(t, callerA, callerA1)

	var callerB string
	valueB := cache.Wrap(func() interface{} {
		callerB = testGetCallingFuncs()[0]
		return "B"
	})
	assert.Equal(t, "B", valueB)

	assert.NotEqual(t, callerA, callerB)
}

func TestBasics(t *testing.T) {
	cache := noisyTestCache(t)

	var callCount int
	getFoo := func() string {
		return cache.Wrap(func() interface{} {
			callCount += 1
			return "Foo!"
		}).(string)
	}

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	cache.Bust(func() {
		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 2, callCount)

		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 3, callCount)
	})
}

func withTestTimeout(t *testing.T, millis int, fn func()) {
	done := make(chan bool)
	go func() {
		fn()
		done <- true
	}()
	select {
	case <-done:
	case <-time.After(time.Duration(millis) * time.Millisecond):
		t.Fatal("timed out")
	}
}

func TestFibonacci(t *testing.T) {
	cache := NewInMemCache()
	var fib func(k int) int
	fib = func(k int) int {
		if k < 2 {
			return k
		}
		a := cache.Cache(k-1, func() interface{} { return fib(k - 1) })
		b := cache.Cache(k-2, func() interface{} { return fib(k - 2) })
		return a.(int) + b.(int)
	}
	withTestTimeout(t, 500, func() { fib(36) }) // Retardedly slow without caching
}

func testCacheUse(t *testing.T, cache *Cache, key, val interface{}, bust bool) {
	var gotBust bool
	gotVal := cache.Cache(key, func() interface{} {
		gotBust = true
		return val
	})
	assert.Equal(t, val, gotVal)
	assert.Equal(t, bust, gotBust)
}

func TestNestedCachingAndBusting(t *testing.T) {
	cache := noisyTestCache(t)

	var callCount int
	getFoo := func() interface{} {
		return cache.Wrap(func() interface{} {
			callCount += 1
			return "Foo!"
		})
	}
	getBar := func() interface{} {
		return cache.Wrap(func() interface{} {
			getFoo()
			getFoo()
			callCount += 1
			return "Bar!"
		})
	}

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	assert.Equal(t, "Bar!", getBar())
	assert.Equal(t, 2, callCount)

	cache.Bust(func() {
		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 3, callCount)

		assert.Equal(t, "Bar!", getBar())
		assert.Equal(t, 6, callCount)

		func() {
			assert.Equal(t, "Bar!", getBar())
			assert.Equal(t, 9, callCount)
		}()

		cache.Bust(func() {
			assert.Equal(t, "Bar!", getBar())
			assert.Equal(t, 12, callCount)
		})
	})

	assert.Equal(t, "Bar!", getBar())
	assert.Equal(t, 12, callCount)
}

func TestBackedByAnotherStore(t *testing.T) {
	store, err := lru.New2Q(10)
	assert.NoError(t, err)
	cache := New(store)

	testCacheUse(t, cache, "foo", "Foo!", true)
	testCacheUse(t, cache, "foo", "Foo!", false)
	testCacheUse(t, cache, "bar", "Bar!", true)
	testCacheUse(t, cache, "foo", "Foo!", false)

	cache.Bust(func() {
		testCacheUse(t, cache, "bar", "Bar!", true)
		testCacheUse(t, cache, "foo", "Foo!", true)
	})

	testCacheUse(t, cache, "foo", "Foo!", false)
	testCacheUse(t, cache, "bar", "Bar!", false)
}

func TestCacheNil(t *testing.T) {
	cache := noisyTestCache(t)

	testCacheUse(t, cache, nil, "Foo!", true)
	testCacheUse(t, cache, nil, "Foo!", false)
}

func TestCacheMixedKeys(t *testing.T) {
	cache := noisyTestCache(t)

	testCacheUse(t, cache, "abc", "Foo!", true)
	testCacheUse(t, cache, 123, "123!", true)

	testCacheUse(t, cache, "abc", "Foo!", false)
	testCacheUse(t, cache, 123, "123!", false)
}

func TestDeferredFuncs(t *testing.T) {
	cache := noisyTestCache(t)

	testCacheUse(t, cache, "foo", "Foo!", true)
	defer testCacheUse(t, cache, "foo", "Foo!", false)
	defer cache.Bust(func() {
		testCacheUse(t, cache, "foo", "Foo!", true)
	})
}

type embeddedTestCache struct{ *Cache }

func TestComposition(t *testing.T) {
	cache := embeddedTestCache{noisyTestCache(t)}

	var callCount int
	getFoo := func() interface{} {
		return cache.Wrap(func() interface{} {
			callCount += 1
			return "Foo!"
		})
	}

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 1, callCount)

	cache.Bust(func() {
		assert.Equal(t, "Foo!", getFoo())
		assert.Equal(t, 2, callCount)
	})

	assert.Equal(t, "Foo!", getFoo())
	assert.Equal(t, 2, callCount)
}

// -----------------------------------------------------------------------------

func BenchmarkUncached(b *testing.B) {
	for n := 0; n < b.N; n++ {
		func() interface{} {
			return "xyz"
		}()
	}
}
func BenchmarkUncachedPar(b *testing.B) {
	// b.ReportAllocs()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			func() interface{} {
				return "xyz"
			}()
		}
	})
}

func BenchmarkCacheHitsMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheHitsMemPar(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		}
	})
}
func BenchmarkCacheHitsCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheHitsCowPar(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		}
	})
}
func BenchmarkCacheMisses(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Cache("xyz", func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkCacheMissesPar(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		}
	})
}
func BenchmarkCacheBusted(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}
func BenchmarkCacheBustedPar(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Bust(func() {
				cache.Cache("xyz", func() interface{} {
					return "xyz"
				})
			})
		}
	})
}
func BenchmarkCacheBustedMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}
func BenchmarkCacheBustedMemPar(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Bust(func() {
				cache.Cache("xyz", func() interface{} {
					return "xyz"
				})
			})
		}
	})
}
func BenchmarkCacheBustedCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Cache("xyz", func() interface{} {
				return "xyz"
			})
		})
	}
}
func BenchmarkCacheBustedCowPar(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			cache.Bust(func() {
				cache.Cache("xyz", func() interface{} {
					return "xyz"
				})
			})
		}
	})
}

func BenchmarkWrapHitsMem(b *testing.B) {
	cache := NewInMemCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapHitsCow(b *testing.B) {
	cache := New(newCopyOnWriteMap())
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapMisses(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Wrap(func() interface{} {
			return "xyz"
		})
	}
}
func BenchmarkWrapBusted(b *testing.B) {
	cache := nilCache()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		cache.Bust(func() {
			cache.Wrap(func() interface{} {
				return "xyz"
			})
		})
	}
}
<|endoftext|>