<commit_before>package build

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/buildtarget"
	"github.com/goreleaser/goreleaser/internal/testlib"
	"github.com/stretchr/testify/assert"
)

var emptyEnv []string

func TestPipeDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestRun(t *testing.T) {
	assert.NoError(t, run(buildtarget.Runtime, []string{"go", "list", "./..."}, emptyEnv))
}

func TestRunInvalidCommand(t *testing.T) {
	assert.Error(t, run(buildtarget.Runtime, []string{"gggggo", "nope"}, emptyEnv))
}

func TestBuild(t *testing.T) {
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "testing",
				Flags:  "-n",
				Env:    []string{"BLAH=1"},
			},
		},
	}
	var ctx = context.New(config)
	assert.NoError(t, doBuild(ctx, ctx.Config.Builds[0], buildtarget.Runtime))
}

func TestRunFullPipe(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "testing")
	var pre = filepath.Join(folder, "pre")
	var post = filepath.Join(folder, "post")
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "testing",
				Flags:   "-v",
				Ldflags: "-X main.test=testing",
				Hooks: config.Hooks{
					Pre:  "touch " + pre,
					Post: "touch " + post,
				},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary), binary)
	assert.True(t, exists(pre), pre)
	assert.True(t, exists(post), post)
}

func TestRunPipeFormatBinary(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "binary-testing")
	var config = config.Project{
		ProjectName: "testing",
		Dist:        folder,
		Builds: []config.Build{
			{
				Binary: "testing",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
		Archive: config.Archive{
			Format:       "binary",
			NameTemplate: "binary-{{.Binary}}",
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary))
}

func TestRunPipeArmBuilds(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "armtesting")
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "armtesting",
				Flags:   "-v",
				Ldflags: "-X main.test=armtesting",
				Goos: []string{
					"linux",
				},
				Goarch: []string{
					"arm",
					"arm64",
				},
				Goarm: []string{
					"6",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary), binary)
}

func TestBuildFailed(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-flag-that-dont-exists-to-force-failure",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assertContainsError(t, Pipe{}.Run(context.New(config)), `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
}

func TestRunPipeWithInvalidOS(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-v",
				Goos: []string{
					"windows",
				},
				Goarch: []string{
					"arm",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
}

func TestRunInvalidNametemplate(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	for _, format := range []string{"tar.gz", "zip", "binary"} {
		var config = config.Project{
			ProjectName: "nameeeee",
			Builds: []config.Build{
				{
					Binary: "namet{{.est}",
					Flags:  "-v",
					Goos: []string{
						runtime.GOOS,
					},
					Goarch: []string{
						runtime.GOARCH,
					},
				},
			},
			Archive: config.Archive{
				Format:       format,
				NameTemplate: "{{.Binary}",
			},
		}
		assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: nameeeee:1: unexpected "}" in operand`)
	}
}

func TestRunInvalidLdflags(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "nametest",
				Flags:   "-v",
				Ldflags: "-s -w -X main.version={{.Version}",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: ldflags:1: unexpected "}" in operand`)
}

func TestRunPipeFailingHooks(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "hooks",
				Hooks:  config.Hooks{},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	t.Run("pre-hook", func(t *testing.T) {
		var ctx = context.New(config)
		ctx.Config.Builds[0].Hooks.Pre = "exit 1"
		ctx.Config.Builds[0].Hooks.Post = "echo post"
		assert.EqualError(t, Pipe{}.Run(ctx), `pre hook failed: `)
	})
	t.Run("post-hook", func(t *testing.T) {
		var ctx = context.New(config)
		ctx.Config.Builds[0].Hooks.Pre = "echo pre"
		ctx.Config.Builds[0].Hooks.Post = "exit 1"
		assert.EqualError(t, Pipe{}.Run(ctx), `post hook failed: `)
	})
}

func TestRunPipeWithouMainFunc(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeMainWithoutMainFunc(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "no-main",
				Hooks:  config.Hooks{},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
	t.Run("fixed main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "main.go"
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return !os.IsNotExist(err)
}

func writeMainWithoutMainFunc(t *testing.T, folder string) {
	writeFile(t, folder, "package main\nconst a = 2\nfunc notMain() {println(0)}")
}

func writeGoodMain(t *testing.T, folder string) {
	writeFile(t, folder, "package main\nvar a = 1\nfunc main() {println(0)}")
}

func writeFile(t *testing.T, folder, content string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"), []byte(content), 0644),
	)
}

func assertContainsError(t *testing.T, err error, s string) {
	assert.Error(t, err)
	assert.Contains(t, err.Error(), s)
}
<commit_msg>test: added a test case for an invalid main.go<commit_after>package build

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/buildtarget"
	"github.com/goreleaser/goreleaser/internal/testlib"
	"github.com/stretchr/testify/assert"
)

var emptyEnv []string

func TestPipeDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestRun(t *testing.T) {
	assert.NoError(t, run(buildtarget.Runtime, []string{"go", "list", "./..."}, emptyEnv))
}

func TestRunInvalidCommand(t *testing.T) {
	assert.Error(t, run(buildtarget.Runtime, []string{"gggggo", "nope"}, emptyEnv))
}

func TestBuild(t *testing.T) {
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "testing",
				Flags:  "-n",
				Env:    []string{"BLAH=1"},
			},
		},
	}
	var ctx = context.New(config)
	assert.NoError(t, doBuild(ctx, ctx.Config.Builds[0], buildtarget.Runtime))
}

func TestRunFullPipe(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "testing")
	var pre = filepath.Join(folder, "pre")
	var post = filepath.Join(folder, "post")
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "testing",
				Flags:   "-v",
				Ldflags: "-X main.test=testing",
				Hooks: config.Hooks{
					Pre:  "touch " + pre,
					Post: "touch " + post,
				},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary), binary)
	assert.True(t, exists(pre), pre)
	assert.True(t, exists(post), post)
}

func TestRunPipeFormatBinary(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "binary-testing")
	var config = config.Project{
		ProjectName: "testing",
		Dist:        folder,
		Builds: []config.Build{
			{
				Binary: "testing",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
		Archive: config.Archive{
			Format:       "binary",
			NameTemplate: "binary-{{.Binary}}",
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary))
}

func TestRunPipeArmBuilds(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var binary = filepath.Join(folder, "armtesting")
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "armtesting",
				Flags:   "-v",
				Ldflags: "-X main.test=armtesting",
				Goos: []string{
					"linux",
				},
				Goarch: []string{
					"arm",
					"arm64",
				},
				Goarm: []string{
					"6",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
	assert.True(t, exists(binary), binary)
}

func TestBuildFailed(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-flag-that-dont-exists-to-force-failure",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assertContainsError(t, Pipe{}.Run(context.New(config)), `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
}

func TestRunPipeWithInvalidOS(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-v",
				Goos: []string{
					"windows",
				},
				Goarch: []string{
					"arm",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Run(context.New(config)))
}

func TestRunInvalidNametemplate(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	for _, format := range []string{"tar.gz", "zip", "binary"} {
		var config = config.Project{
			ProjectName: "nameeeee",
			Builds: []config.Build{
				{
					Binary: "namet{{.est}",
					Flags:  "-v",
					Goos: []string{
						runtime.GOOS,
					},
					Goarch: []string{
						runtime.GOARCH,
					},
				},
			},
			Archive: config.Archive{
				Format:       format,
				NameTemplate: "{{.Binary}",
			},
		}
		assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: nameeeee:1: unexpected "}" in operand`)
	}
}

func TestRunInvalidLdflags(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "nametest",
				Flags:   "-v",
				Ldflags: "-s -w -X main.version={{.Version}",
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: ldflags:1: unexpected "}" in operand`)
}

func TestRunPipeFailingHooks(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "hooks",
				Hooks:  config.Hooks{},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	t.Run("pre-hook", func(t *testing.T) {
		var ctx = context.New(config)
		ctx.Config.Builds[0].Hooks.Pre = "exit 1"
		ctx.Config.Builds[0].Hooks.Post = "echo post"
		assert.EqualError(t, Pipe{}.Run(ctx), `pre hook failed: `)
	})
	t.Run("post-hook", func(t *testing.T) {
		var ctx = context.New(config)
		ctx.Config.Builds[0].Hooks.Pre = "echo pre"
		ctx.Config.Builds[0].Hooks.Post = "exit 1"
		assert.EqualError(t, Pipe{}.Run(ctx), `post hook failed: `)
	})
}

func TestRunPipeWithouMainFunc(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeMainWithoutMainFunc(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "no-main",
				Hooks:  config.Hooks{},
				Goos: []string{
					runtime.GOOS,
				},
				Goarch: []string{
					runtime.GOARCH,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
	t.Run("not main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "foo.go"
		assert.EqualError(t, Pipe{}.Run(ctx), `failed dir: foo.go: open foo.go: no such file or directory`)
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
	t.Run("fixed main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "main.go"
		assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
	})
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return !os.IsNotExist(err)
}

func writeMainWithoutMainFunc(t *testing.T, folder string) {
	writeFile(t, folder, "package main\nconst a = 2\nfunc notMain() {println(0)}")
}

func writeGoodMain(t *testing.T, folder string) {
	writeFile(t, folder, "package main\nvar a = 1\nfunc main() {println(0)}")
}

func writeFile(t *testing.T, folder, content string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"), []byte(content), 0644),
	)
}

func assertContainsError(t *testing.T, err error, s string) {
	assert.Error(t, err)
	assert.Contains(t, err.Error(), s)
}
<|endoftext|>


<commit_before>/*
 Copyright 2017 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

package upgrade

import (
	log "github.com/Sirupsen/logrus"
	"os"
	"time"

	"github.com/crunchydata/postgres-operator/operator/cluster"
	"github.com/crunchydata/postgres-operator/operator/util"
	"github.com/crunchydata/postgres-operator/tpr"

	"k8s.io/client-go/kubernetes"

	"k8s.io/client-go/pkg/api/errors"
	"k8s.io/client-go/pkg/api/v1"
	v1batch "k8s.io/client-go/pkg/apis/batch/v1"
	"k8s.io/client-go/pkg/fields"
	"k8s.io/client-go/pkg/watch"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

func Process(clientset *kubernetes.Clientset, client *rest.RESTClient, stopchan chan struct{}, namespace string) {

	eventchan := make(chan *tpr.PgUpgrade)

	source := cache.NewListWatchFromClient(client, "pgupgrades", namespace, fields.Everything())

	createAddHandler := func(obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		addUpgrade(clientset, client, job, namespace)
	}
	createDeleteHandler := func(obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		deleteUpgrade(clientset, client, job, namespace)
	}

	updateHandler := func(old interface{}, obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		//log.Info("updating PgUpgrade object")
		//log.Info("updated with Name=" + job.Spec.Name)
	}

	_, controller := cache.NewInformer(
		source,
		&tpr.PgUpgrade{},
		time.Second*10,
		cache.ResourceEventHandlerFuncs{
			AddFunc:    createAddHandler,
			UpdateFunc: updateHandler,
			DeleteFunc: createDeleteHandler,
		})

	go controller.Run(stopchan)

	for {
		select {
		case event := <-eventchan:
			//log.Infof("%#v\n", event)
			if event == nil {
				log.Info("event was null")
			}
		}
	}

}

func addUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, upgrade *tpr.PgUpgrade, namespace string) {
	var err error
	cl := tpr.PgCluster{}

	//not a db so get the pgcluster TPR
	err = tprclient.Get().
		Resource("pgclusters").
		Namespace(namespace).
		Name(upgrade.Spec.Name).
		Do().
		Into(&cl)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Debug("pgcluster " + upgrade.Spec.Name + " not found ")
			return
		} else {
			log.Error("error getting pgcluser " + upgrade.Spec.Name + err.Error())
		}
	}

	err = cluster.AddUpgrade(clientset, tprclient, upgrade, namespace, &cl)
	if err != nil {
		log.Error("error adding upgrade" + err.Error())
	} else {
		//update the upgrade TPR status to submitted
		err = util.Patch(tprclient, "/spec/upgradestatus", tpr.UPGRADE_SUBMITTED_STATUS, "pgupgrades", upgrade.Spec.Name, namespace)
		if err != nil {
			log.Error("error patching upgrade" + err.Error())
		}
	}

}

func deleteUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, upgrade *tpr.PgUpgrade, namespace string) {
	var jobName = "upgrade-" + upgrade.Spec.Name
	log.Debug("deleting Job with Name=" + jobName + " in namespace " + namespace)

	//delete the job
	err := clientset.Batch().Jobs(namespace).Delete(jobName,
		&v1.DeleteOptions{})
	if err != nil {
		log.Error("error deleting Job " + jobName + err.Error())
		return
	}
	log.Debug("deleted Job " + jobName)
}

//process major upgrade completions
//this watcher will look for completed upgrade jobs
//and when this occurs, will update the upgrade TPR status to
//completed and spin up the database or cluster using the newly
//upgraded data files
func MajorUpgradeProcess(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, stopchan chan struct{}, namespace string) {

	log.Info("MajorUpgradeProcess watch starting...")

	lo := v1.ListOptions{LabelSelector: "pgupgrade=true"}
	fw, err := clientset.Batch().Jobs(namespace).Watch(lo)
	if err != nil {
		log.Error("error watching upgrade job" + err.Error())
		os.Exit(2)
	}

	_, err4 := watch.Until(0, fw, func(event watch.Event) (bool, error) {
		log.Infoln("got a pgupgrade job watch event")

		switch event.Type {
		case watch.Added:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job added=%d\n", gotjob.Status.Succeeded)
		case watch.Deleted:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job deleted=%d\n", gotjob.Status.Succeeded)
		case watch.Error:
			log.Infof("pgupgrade job watch error event")
		case watch.Modified:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job modified=%d\n", gotjob.Status.Succeeded)
			if gotjob.Status.Succeeded == 1 {
				log.Infoln("pgupgrade job " + gotjob.Name + " succeeded")
				finishUpgrade(clientset, tprclient, gotjob, namespace)

			}
		default:
			log.Infoln("unknown watch event %v\n", event.Type)
		}

		return false, nil
	})

	if err4 != nil {
		log.Error("erro in major upgrade " + err4.Error())
	}

}

func finishUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, job *v1batch.Job, namespace string) {

	var cl tpr.PgCluster
	var upgrade tpr.PgUpgrade

	//from the job get the db and upgrade TPRs
	//pgdatabase name is from the pg-database label value in the job
	// it represents the cluster name or the database name
	//pgupgrade name is from the pg-database label value in the job
	name := job.ObjectMeta.Labels["pg-database"]
	if name == "" {
		log.Error("name was empty in the pg-database label for the upgrade job")
		return
	}

	err := tprclient.Get().
		Resource("pgupgrades").
		Namespace(namespace).
		Name(name).
		Do().Into(&upgrade)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(name + " pgupgrade tpr is not found")
		} else {
			log.Error("error in tpr get upgrade" + err.Error())
		}
	}
	log.Info(name + " pgupgrade tpr is found")

	err = tprclient.Get().
		Resource("pgclusters").
		Namespace(namespace).
		Name(name).
		Do().Into(&cl)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(name + " pgcluster tpr is not found")
		} else {
			log.Error("error in tpr get cluster" + err.Error())
		}
	}
	log.Info(name + " pgcluster tpr is found")

	var clusterStrategy cluster.ClusterStrategy

	if cl.Spec.STRATEGY == "" {
		cl.Spec.STRATEGY = "1"
		log.Info("using default strategy")
	}

	clusterStrategy, ok := cluster.StrategyMap[cl.Spec.STRATEGY]

	if ok {
		log.Info("strategy found")

	} else {
		log.Error("invalid STRATEGY requested for cluster creation" + cl.Spec.STRATEGY)
		return
	}

	err = clusterStrategy.MajorUpgradeFinalize(clientset, tprclient, &cl, &upgrade, namespace)
	if err != nil {
		log.Error("erro in major upgrade finalize" + err.Error())
	}

	if err == nil {
		//update the upgrade TPR status to completed
		err = util.Patch(tprclient, "/spec/upgradestatus", tpr.UPGRADE_COMPLETED_STATUS, "pgupgrades", upgrade.Spec.Name, namespace)
		if err != nil {
			log.Error("error in patch upgrade " + err.Error())
		}

	}

}
<commit_msg>[typos] "erro in" = > "error in"<commit_after>/*
 Copyright 2017 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

package upgrade

import (
	log "github.com/Sirupsen/logrus"
	"os"
	"time"

	"github.com/crunchydata/postgres-operator/operator/cluster"
	"github.com/crunchydata/postgres-operator/operator/util"
	"github.com/crunchydata/postgres-operator/tpr"

	"k8s.io/client-go/kubernetes"

	"k8s.io/client-go/pkg/api/errors"
	"k8s.io/client-go/pkg/api/v1"
	v1batch "k8s.io/client-go/pkg/apis/batch/v1"
	"k8s.io/client-go/pkg/fields"
	"k8s.io/client-go/pkg/watch"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

func Process(clientset *kubernetes.Clientset, client *rest.RESTClient, stopchan chan struct{}, namespace string) {

	eventchan := make(chan *tpr.PgUpgrade)

	source := cache.NewListWatchFromClient(client, "pgupgrades", namespace, fields.Everything())

	createAddHandler := func(obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		addUpgrade(clientset, client, job, namespace)
	}
	createDeleteHandler := func(obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		deleteUpgrade(clientset, client, job, namespace)
	}

	updateHandler := func(old interface{}, obj interface{}) {
		job := obj.(*tpr.PgUpgrade)
		eventchan <- job
		//log.Info("updating PgUpgrade object")
		//log.Info("updated with Name=" + job.Spec.Name)
	}

	_, controller := cache.NewInformer(
		source,
		&tpr.PgUpgrade{},
		time.Second*10,
		cache.ResourceEventHandlerFuncs{
			AddFunc:    createAddHandler,
			UpdateFunc: updateHandler,
			DeleteFunc: createDeleteHandler,
		})

	go controller.Run(stopchan)

	for {
		select {
		case event := <-eventchan:
			//log.Infof("%#v\n", event)
			if event == nil {
				log.Info("event was null")
			}
		}
	}

}

func addUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, upgrade *tpr.PgUpgrade, namespace string) {
	var err error
	cl := tpr.PgCluster{}

	//not a db so get the pgcluster TPR
	err = tprclient.Get().
		Resource("pgclusters").
		Namespace(namespace).
		Name(upgrade.Spec.Name).
		Do().
		Into(&cl)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Debug("pgcluster " + upgrade.Spec.Name + " not found ")
			return
		} else {
			log.Error("error getting pgcluser " + upgrade.Spec.Name + err.Error())
		}
	}

	err = cluster.AddUpgrade(clientset, tprclient, upgrade, namespace, &cl)
	if err != nil {
		log.Error("error adding upgrade" + err.Error())
	} else {
		//update the upgrade TPR status to submitted
		err = util.Patch(tprclient, "/spec/upgradestatus", tpr.UPGRADE_SUBMITTED_STATUS, "pgupgrades", upgrade.Spec.Name, namespace)
		if err != nil {
			log.Error("error patching upgrade" + err.Error())
		}
	}

}

func deleteUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, upgrade *tpr.PgUpgrade, namespace string) {
	var jobName = "upgrade-" + upgrade.Spec.Name
	log.Debug("deleting Job with Name=" + jobName + " in namespace " + namespace)

	//delete the job
	err := clientset.Batch().Jobs(namespace).Delete(jobName,
		&v1.DeleteOptions{})
	if err != nil {
		log.Error("error deleting Job " + jobName + err.Error())
		return
	}
	log.Debug("deleted Job " + jobName)
}

//process major upgrade completions
//this watcher will look for completed upgrade jobs
//and when this occurs, will update the upgrade TPR status to
//completed and spin up the database or cluster using the newly
//upgraded data files
func MajorUpgradeProcess(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, stopchan chan struct{}, namespace string) {

	log.Info("MajorUpgradeProcess watch starting...")

	lo := v1.ListOptions{LabelSelector: "pgupgrade=true"}
	fw, err := clientset.Batch().Jobs(namespace).Watch(lo)
	if err != nil {
		log.Error("error watching upgrade job" + err.Error())
		os.Exit(2)
	}

	_, err4 := watch.Until(0, fw, func(event watch.Event) (bool, error) {
		log.Infoln("got a pgupgrade job watch event")

		switch event.Type {
		case watch.Added:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job added=%d\n", gotjob.Status.Succeeded)
		case watch.Deleted:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job deleted=%d\n", gotjob.Status.Succeeded)
		case watch.Error:
			log.Infof("pgupgrade job watch error event")
		case watch.Modified:
			gotjob := event.Object.(*v1batch.Job)
			log.Infof("pgupgrade job modified=%d\n", gotjob.Status.Succeeded)
			if gotjob.Status.Succeeded == 1 {
				log.Infoln("pgupgrade job " + gotjob.Name + " succeeded")
				finishUpgrade(clientset, tprclient, gotjob, namespace)

			}
		default:
			log.Infoln("unknown watch event %v\n", event.Type)
		}

		return false, nil
	})

	if err4 != nil {
		log.Error("error in major upgrade " + err4.Error())
	}

}

func finishUpgrade(clientset *kubernetes.Clientset, tprclient *rest.RESTClient, job *v1batch.Job, namespace string) {

	var cl tpr.PgCluster
	var upgrade tpr.PgUpgrade

	//from the job get the db and upgrade TPRs
	//pgdatabase name is from the pg-database label value in the job
	// it represents the cluster name or the database name
	//pgupgrade name is from the pg-database label value in the job
	name := job.ObjectMeta.Labels["pg-database"]
	if name == "" {
		log.Error("name was empty in the pg-database label for the upgrade job")
		return
	}

	err := tprclient.Get().
		Resource("pgupgrades").
		Namespace(namespace).
		Name(name).
		Do().Into(&upgrade)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(name + " pgupgrade tpr is not found")
		} else {
			log.Error("error in tpr get upgrade" + err.Error())
		}
	}
	log.Info(name + " pgupgrade tpr is found")

	err = tprclient.Get().
		Resource("pgclusters").
		Namespace(namespace).
		Name(name).
		Do().Into(&cl)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(name + " pgcluster tpr is not found")
		} else {
			log.Error("error in tpr get cluster" + err.Error())
		}
	}
	log.Info(name + " pgcluster tpr is found")

	var clusterStrategy cluster.ClusterStrategy

	if cl.Spec.STRATEGY == "" {
		cl.Spec.STRATEGY = "1"
		log.Info("using default strategy")
	}

	clusterStrategy, ok := cluster.StrategyMap[cl.Spec.STRATEGY]

	if ok {
		log.Info("strategy found")

	} else {
		log.Error("invalid STRATEGY requested for cluster creation" + cl.Spec.STRATEGY)
		return
	}

	err = clusterStrategy.MajorUpgradeFinalize(clientset, tprclient, &cl, &upgrade, namespace)
	if err != nil {
		log.Error("error in major upgrade finalize" + err.Error())
	}

	if err == nil {
		//update the upgrade TPR status to completed
		err = util.Patch(tprclient, "/spec/upgradestatus", tpr.UPGRADE_COMPLETED_STATUS, "pgupgrades", upgrade.Spec.Name, namespace)
		if err != nil {
			log.Error("error in patch upgrade " + err.Error())
		}

	}

}
<|endoftext|>


<commit_before>package main

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

	"github.com/aqatl/mal/dialog"
	"github.com/aqatl/mal/nyaa_scraper"
	"github.com/fatih/color"
	"github.com/jroimartin/gocui"
	"github.com/urfave/cli"
	"regexp"
)

func malNyaaCui(ctx *cli.Context) error {
	_, list, err := loadMAL(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := list.GetByID(cfg.SelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}
	return startNyaaCui(
		cfg,
		entry.Title,
		fmt.Sprintf("%s %d/%d", entry.Title, entry.WatchedEpisodes, entry.Episodes),
	)
}

func alNyaaCui(ctx *cli.Context) error {
	al, err := loadAniList(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := al.GetMediaListById(cfg.ALSelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}

	searchTerm := entry.Title.UserPreferred
	if ctx.Bool("alt") {
		fmt.Printf("Select desired title\n\n")
		if searchTerm = chooseStrFromSlice(sliceOfEntryTitles(entry)); searchTerm == "" {
			return fmt.Errorf("no alternative titles")
		}
	} else if ctx.NArg() > 0 {
		searchTerm = strings.Join(ctx.Args(), " ")
	}

	return startNyaaCui(
		cfg,
		searchTerm,
		fmt.Sprintf("%s %d/%d", searchTerm, entry.Progress, entry.Episodes),
	)
}

func startNyaaCui(cfg *Config, searchTerm, displayedInfo string) error {
	gui, err := gocui.NewGui(gocui.Output256)
	defer gui.Close()
	if err != nil {
		return fmt.Errorf("gocui error: %v", err)
	}
	nc := &nyaaCui{
		Gui: gui,
		Cfg: cfg,

		SearchTerm:    searchTerm,
		DisplayedInfo: displayedInfo,
		Category:      nyaa_scraper.AnimeEnglishTranslated,
		Filter:        nyaa_scraper.TrustedOnly,
	}
	gui.SetManager(nc)
	nc.setGuiKeyBindings(gui)

	gui.Cursor = false
	gui.Mouse = false
	gui.Highlight = true
	gui.SelFgColor = gocui.ColorGreen

	gui.Update(func(gui *gocui.Gui) error {
		nc.Reload()
		return nil
	})

	if err = gui.MainLoop(); err != nil && err != gocui.ErrQuit {
		return err
	}
	return nil
}

const (
	ncInfoView      = "ncInfoView"
	ncResultsView   = "ncResultsView "
	ncShortcutsView = "ncShortcutsView"
)

type nyaaCui struct {
	Gui *gocui.Gui
	Cfg *Config

	SearchTerm    string
	DisplayedInfo string
	Category      nyaa_scraper.NyaaCategory
	Filter        nyaa_scraper.NyaaFilter

	Results     []nyaa_scraper.NyaaEntry
	MaxResults  int
	MaxPages    int
	LoadedPages int

	TitleFilter *regexp.Regexp

	ResultsView      *gocui.View
	DisplayedIndexes []int
}

var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()

func (nc *nyaaCui) Layout(gui *gocui.Gui) error {
	w, h := gui.Size()

	if v, err := gui.SetView(ncResultsView, 0, 3, w-1, h-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Search results"
		v.SelBgColor = gocui.ColorGreen
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		v.Highlight = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(nc.GetEditor())

		gui.SetCurrentView(ncResultsView)
		nc.ResultsView = v

		//TODO Better/clearer results printing
		nc.DisplayedIndexes = make([]int, 0, len(nc.Results))
		for i, result := range nc.Results {
			if nc.TitleFilter != nil && !nc.TitleFilter.MatchString(result.Title) {
				continue
			}
			fmt.Fprintln(v,
				result.Title,
				red(result.Size),
				cyan(result.DateAdded.Format("15:04 02-01-2006")),
				green(result.Seeders),
				red(result.Leechers),
				blue(result.CompletedDownloads),
			)
			nc.DisplayedIndexes = append(nc.DisplayedIndexes, i)
		}
	}

	if v, err := gui.SetView(ncInfoView, 0, 0, w-1, 2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Info"
		v.Editable = false

		fmt.Fprintf(v, "[%s]: displaying %d out of %d results",
			nc.DisplayedInfo, len(nc.DisplayedIndexes), nc.MaxResults)
	}

	if v, err := gui.SetView(ncShortcutsView, 0, h-3, w-1, h-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Shortcuts"
		v.Editable = false

		c := color.New(color.FgCyan).SprintFunc()
		fmt.Fprintln(v,
			c("d"), "download",
			c("l"), "load next page",
			c("c"), "category",
			c("f"), "filters",
			c("t"), "tags",
			c("r"), "reload",
		)
	}

	return nil
}

func (nc *nyaaCui) GetEditor() func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case key == gocui.KeyArrowDown || ch == 'j':
			_, oy := v.Origin()
			_, y := v.Cursor()
			y += oy
			if y < len(nc.DisplayedIndexes)-1 {
				v.MoveCursor(0, 1, false)
			}
		case key == gocui.KeyArrowUp || ch == 'k':
			v.MoveCursor(0, -1, false)
		case ch == 'g':
			v.SetCursor(0, 0)
			v.SetOrigin(0, 0)
		case ch == 'G':
			_, viewH := v.Size()
			totalH := len(nc.DisplayedIndexes)
			if totalH <= viewH {
				v.SetCursor(0, totalH-1)
			} else {
				v.SetOrigin(0, totalH-viewH)
				v.SetCursor(0, viewH-1)
			}
		case ch == 'd':
			_, y := v.Cursor()
			_, oy := v.Origin()
			y += oy
			nc.Download(y)
		case ch == 'l':
			nc.LoadNextPage()
		case ch == 'c':
			nc.ChangeCategory()
		case ch == 'f':
			nc.ChangeFilter()
		case ch == 't':
			nc.FilterByTag()
		case ch == 'r':
			nc.Reload()
		}
	}
}

func (nc *nyaaCui) Reload() {
	var resultPage nyaa_scraper.NyaaResultPage
	var searchErr error
	f := func() {
		resultPage, searchErr = nyaa_scraper.Search(nc.SearchTerm, nc.Category, nc.Filter)
	}
	jobDone, err := dialog.StuffLoader(dialog.FitMessage(nc.Gui, "Loading "+nc.SearchTerm), f)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		ok := <-jobDone
		if searchErr != nil {
			dialog.JustShowOkDialog(nc.Gui, "Error", searchErr.Error())
			return
		}
		if ok {
			nc.Results = resultPage.Results
			nc.MaxResults = resultPage.DisplayedOutOf
			nc.MaxPages = int(math.Ceil(float64(resultPage.DisplayedOutOf) /
				float64(resultPage.DisplayedTo-resultPage.DisplayedFrom+1)))
			nc.LoadedPages = 1
		}

		nc.Gui.Update(func(gui *gocui.Gui) error {
			gui.DeleteView(ncResultsView)
			gui.DeleteView(ncInfoView)
			return nil
		})
	}()
}

func (nc *nyaaCui) Download(yIdx int) {
	if yIdx >= len(nc.DisplayedIndexes) {
		return
	}

	link := ""
	if entry := nc.Results[nc.DisplayedIndexes[yIdx]]; entry.MagnetLink != "" {
		link = entry.MagnetLink
	} else if entry.TorrentLink != "" {
		link = entry.TorrentLink
	} else {
		dialog.JustShowOkDialog(nc.Gui, "Error", "No link found")
		return
	}

	link = "\"" + link + "\""
	cmd := exec.Command(nc.Cfg.TorrentClientPath, nc.Cfg.TorrentClientArgs...)
	cmd.Args = append(cmd.Args, link)
	cmd.Args = cmd.Args[1:] //Why they include app name in the arguments???
	if err := cmd.Start(); err != nil {
		gocuiReturnError(nc.Gui, err)
	}
}

func (nc *nyaaCui) LoadNextPage() {
	if nc.LoadedPages >= nc.MaxPages {
		return
	}
	nc.LoadedPages++
	go func() {
		resultPage, _ := nyaa_scraper.SearchSpecificPage(
			nc.SearchTerm,
			nc.Category,
			nc.Filter,
			nc.LoadedPages,
		)
		nc.Results = append(nc.Results, resultPage.Results...)
		nc.Gui.Update(func(gui *gocui.Gui) error {
			_, oy := nc.ResultsView.Origin()
			_, y := nc.ResultsView.Cursor()

			gui.DeleteView(ncInfoView)
			gui.DeleteView(ncResultsView)

			nc.Layout(gui)
			nc.ResultsView.SetOrigin(0, oy)
			nc.ResultsView.SetCursor(0, y)

			return nil
		})
	}()
}

func (nc *nyaaCui) ChangeCategory() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select category", nyaa_scraper.Categories)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Category = nyaa_scraper.Categories[idx]
			nc.Reload()
		}
	}()
}

func (nc *nyaaCui) ChangeFilter() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select filter", nyaa_scraper.Filters)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Filter = nyaa_scraper.Filters[idx]
			nc.Reload()
		}
	}()
}

var tagRegex = `(?U)\[(.+)\]`

func (nc *nyaaCui) FilterByTag() {
	tags := make([]string, 1, len(nc.Results))
	tagsDup := make(map[string]struct{})
	re := regexp.MustCompile(tagRegex)
	for _, result := range nc.Results {
		if tsm := re.FindStringSubmatch(result.Title); len(tsm) >= 2 && tsm[1] != "" {
			if _, ok := tagsDup[tsm[1]]; !ok {
				tags = append(tags, tsm[1])
				tagsDup[tsm[1]] = struct{}{}
			}
		}
	}
	tags[0] = "None"

	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select title filter", tags)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			if idx == 0 {
				nc.TitleFilter = nil
			} else {
				regex, err := regexp.Compile("\\[" + regexp.QuoteMeta(tags[idx]) + "\\]")
				if err != nil {
					gocuiReturnError(nc.Gui, err)
				}
				nc.TitleFilter = regex
			}
			nc.Gui.Update(func(gui *gocui.Gui) error {
				gui.DeleteView(ncInfoView)
				gui.DeleteView(ncResultsView)
				return nil
			})
		}
	}()
}

func (nc *nyaaCui) setGuiKeyBindings(gui *gocui.Gui) {
	gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quitGocui)
}

func quitGocui(gui *gocui.Gui, view *gocui.View) error {
	return gocui.ErrQuit
}

func gocuiReturnError(gui *gocui.Gui, err error) {
	gui.Update(func(gui *gocui.Gui) error {
		return err
	})
}
<commit_msg>Fixed running torrent client when no additional arguments are specified<commit_after>package main

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

	"regexp"

	"github.com/aqatl/mal/dialog"
	"github.com/aqatl/mal/nyaa_scraper"
	"github.com/fatih/color"
	"github.com/jroimartin/gocui"
	"github.com/urfave/cli"
)

func malNyaaCui(ctx *cli.Context) error {
	_, list, err := loadMAL(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := list.GetByID(cfg.SelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}
	return startNyaaCui(
		cfg,
		entry.Title,
		fmt.Sprintf("%s %d/%d", entry.Title, entry.WatchedEpisodes, entry.Episodes),
	)
}

func alNyaaCui(ctx *cli.Context) error {
	al, err := loadAniList(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := al.GetMediaListById(cfg.ALSelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}

	searchTerm := entry.Title.UserPreferred
	if ctx.Bool("alt") {
		fmt.Printf("Select desired title\n\n")
		if searchTerm = chooseStrFromSlice(sliceOfEntryTitles(entry)); searchTerm == "" {
			return fmt.Errorf("no alternative titles")
		}
	} else if ctx.NArg() > 0 {
		searchTerm = strings.Join(ctx.Args(), " ")
	}

	return startNyaaCui(
		cfg,
		searchTerm,
		fmt.Sprintf("%s %d/%d", searchTerm, entry.Progress, entry.Episodes),
	)
}

func startNyaaCui(cfg *Config, searchTerm, displayedInfo string) error {
	gui, err := gocui.NewGui(gocui.Output256)
	defer gui.Close()
	if err != nil {
		return fmt.Errorf("gocui error: %v", err)
	}
	nc := &nyaaCui{
		Gui: gui,
		Cfg: cfg,

		SearchTerm:    searchTerm,
		DisplayedInfo: displayedInfo,
		Category:      nyaa_scraper.AnimeEnglishTranslated,
		Filter:        nyaa_scraper.TrustedOnly,
	}
	gui.SetManager(nc)
	nc.setGuiKeyBindings(gui)

	gui.Cursor = false
	gui.Mouse = false
	gui.Highlight = true
	gui.SelFgColor = gocui.ColorGreen

	gui.Update(func(gui *gocui.Gui) error {
		nc.Reload()
		return nil
	})

	if err = gui.MainLoop(); err != nil && err != gocui.ErrQuit {
		return err
	}
	return nil
}

const (
	ncInfoView      = "ncInfoView"
	ncResultsView   = "ncResultsView "
	ncShortcutsView = "ncShortcutsView"
)

type nyaaCui struct {
	Gui *gocui.Gui
	Cfg *Config

	SearchTerm    string
	DisplayedInfo string
	Category      nyaa_scraper.NyaaCategory
	Filter        nyaa_scraper.NyaaFilter

	Results     []nyaa_scraper.NyaaEntry
	MaxResults  int
	MaxPages    int
	LoadedPages int

	TitleFilter *regexp.Regexp

	ResultsView      *gocui.View
	DisplayedIndexes []int
}

var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()

func (nc *nyaaCui) Layout(gui *gocui.Gui) error {
	w, h := gui.Size()

	if v, err := gui.SetView(ncResultsView, 0, 3, w-1, h-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Search results"
		v.SelBgColor = gocui.ColorGreen
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		v.Highlight = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(nc.GetEditor())

		gui.SetCurrentView(ncResultsView)
		nc.ResultsView = v

		//TODO Better/clearer results printing
		nc.DisplayedIndexes = make([]int, 0, len(nc.Results))
		for i, result := range nc.Results {
			if nc.TitleFilter != nil && !nc.TitleFilter.MatchString(result.Title) {
				continue
			}
			fmt.Fprintln(v,
				result.Title,
				red(result.Size),
				cyan(result.DateAdded.Format("15:04 02-01-2006")),
				green(result.Seeders),
				red(result.Leechers),
				blue(result.CompletedDownloads),
			)
			nc.DisplayedIndexes = append(nc.DisplayedIndexes, i)
		}
	}

	if v, err := gui.SetView(ncInfoView, 0, 0, w-1, 2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Info"
		v.Editable = false

		fmt.Fprintf(v, "[%s]: displaying %d out of %d results",
			nc.DisplayedInfo, len(nc.DisplayedIndexes), nc.MaxResults)
	}

	if v, err := gui.SetView(ncShortcutsView, 0, h-3, w-1, h-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Shortcuts"
		v.Editable = false

		c := color.New(color.FgCyan).SprintFunc()
		fmt.Fprintln(v,
			c("d"), "download",
			c("l"), "load next page",
			c("c"), "category",
			c("f"), "filters",
			c("t"), "tags",
			c("r"), "reload",
		)
	}

	return nil
}

func (nc *nyaaCui) GetEditor() func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case key == gocui.KeyArrowDown || ch == 'j':
			_, oy := v.Origin()
			_, y := v.Cursor()
			y += oy
			if y < len(nc.DisplayedIndexes)-1 {
				v.MoveCursor(0, 1, false)
			}
		case key == gocui.KeyArrowUp || ch == 'k':
			v.MoveCursor(0, -1, false)
		case ch == 'g':
			v.SetCursor(0, 0)
			v.SetOrigin(0, 0)
		case ch == 'G':
			_, viewH := v.Size()
			totalH := len(nc.DisplayedIndexes)
			if totalH <= viewH {
				v.SetCursor(0, totalH-1)
			} else {
				v.SetOrigin(0, totalH-viewH)
				v.SetCursor(0, viewH-1)
			}
		case ch == 'd':
			_, y := v.Cursor()
			_, oy := v.Origin()
			y += oy
			nc.Download(y)
		case ch == 'l':
			nc.LoadNextPage()
		case ch == 'c':
			nc.ChangeCategory()
		case ch == 'f':
			nc.ChangeFilter()
		case ch == 't':
			nc.FilterByTag()
		case ch == 'r':
			nc.Reload()
		}
	}
}

func (nc *nyaaCui) Reload() {
	var resultPage nyaa_scraper.NyaaResultPage
	var searchErr error
	f := func() {
		resultPage, searchErr = nyaa_scraper.Search(nc.SearchTerm, nc.Category, nc.Filter)
	}
	jobDone, err := dialog.StuffLoader(dialog.FitMessage(nc.Gui, "Loading "+nc.SearchTerm), f)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		ok := <-jobDone
		if searchErr != nil {
			dialog.JustShowOkDialog(nc.Gui, "Error", searchErr.Error())
			return
		}
		if ok {
			nc.Results = resultPage.Results
			nc.MaxResults = resultPage.DisplayedOutOf
			nc.MaxPages = int(math.Ceil(float64(resultPage.DisplayedOutOf) /
				float64(resultPage.DisplayedTo-resultPage.DisplayedFrom+1)))
			nc.LoadedPages = 1
		}

		nc.Gui.Update(func(gui *gocui.Gui) error {
			gui.DeleteView(ncResultsView)
			gui.DeleteView(ncInfoView)
			return nil
		})
	}()
}

func (nc *nyaaCui) Download(yIdx int) {
	if yIdx >= len(nc.DisplayedIndexes) {
		return
	}

	link := ""
	if entry := nc.Results[nc.DisplayedIndexes[yIdx]]; entry.MagnetLink != "" {
		link = entry.MagnetLink
	} else if entry.TorrentLink != "" {
		link = entry.TorrentLink
	} else {
		dialog.JustShowOkDialog(nc.Gui, "Error", "No link found")
		return
	}

	link = "\"" + link + "\""
	cmd := exec.Command(nc.Cfg.TorrentClientPath, nc.Cfg.TorrentClientArgs...)
	cmd.Args = append(cmd.Args, link)
	if len(nc.Cfg.TorrentClientArgs) > 0 {
		cmd.Args = cmd.Args[1:] //Why they include app name in the arguments???
	}
	if err := cmd.Start(); err != nil {
		gocuiReturnError(nc.Gui, err)
	}
}

func (nc *nyaaCui) LoadNextPage() {
	if nc.LoadedPages >= nc.MaxPages {
		return
	}
	nc.LoadedPages++
	go func() {
		resultPage, _ := nyaa_scraper.SearchSpecificPage(
			nc.SearchTerm,
			nc.Category,
			nc.Filter,
			nc.LoadedPages,
		)
		nc.Results = append(nc.Results, resultPage.Results...)
		nc.Gui.Update(func(gui *gocui.Gui) error {
			_, oy := nc.ResultsView.Origin()
			_, y := nc.ResultsView.Cursor()

			gui.DeleteView(ncInfoView)
			gui.DeleteView(ncResultsView)

			nc.Layout(gui)
			nc.ResultsView.SetOrigin(0, oy)
			nc.ResultsView.SetCursor(0, y)

			return nil
		})
	}()
}

func (nc *nyaaCui) ChangeCategory() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select category", nyaa_scraper.Categories)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Category = nyaa_scraper.Categories[idx]
			nc.Reload()
		}
	}()
}

func (nc *nyaaCui) ChangeFilter() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select filter", nyaa_scraper.Filters)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Filter = nyaa_scraper.Filters[idx]
			nc.Reload()
		}
	}()
}

var tagRegex = `(?U)\[(.+)\]`

func (nc *nyaaCui) FilterByTag() {
	tags := make([]string, 1, len(nc.Results))
	tagsDup := make(map[string]struct{})
	re := regexp.MustCompile(tagRegex)
	for _, result := range nc.Results {
		if tsm := re.FindStringSubmatch(result.Title); len(tsm) >= 2 && tsm[1] != "" {
			if _, ok := tagsDup[tsm[1]]; !ok {
				tags = append(tags, tsm[1])
				tagsDup[tsm[1]] = struct{}{}
			}
		}
	}
	tags[0] = "None"

	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select title filter", tags)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			if idx == 0 {
				nc.TitleFilter = nil
			} else {
				regex, err := regexp.Compile("\\[" + regexp.QuoteMeta(tags[idx]) + "\\]")
				if err != nil {
					gocuiReturnError(nc.Gui, err)
				}
				nc.TitleFilter = regex
			}
			nc.Gui.Update(func(gui *gocui.Gui) error {
				gui.DeleteView(ncInfoView)
				gui.DeleteView(ncResultsView)
				return nil
			})
		}
	}()
}

func (nc *nyaaCui) setGuiKeyBindings(gui *gocui.Gui) {
	gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quitGocui)
}

func quitGocui(gui *gocui.Gui, view *gocui.View) error {
	return gocui.ErrQuit
}

func gocuiReturnError(gui *gocui.Gui, err error) {
	gui.Update(func(gui *gocui.Gui) error {
		return err
	})
}
<|endoftext|>


<commit_before>package qingcloud

import (
	"fmt"
	"log"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	qc "github.com/yunify/qingcloud-sdk-go/service"
)

func TestAccQingcloudVpc_basic(t *testing.T) {
	var vpc qc.DescribeRoutersOutput

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},

		// module name
		IDRefreshName: "qingcloud_vpc.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckVpcDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccVpcConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "vpc_network", "192.168.0.0/16"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "type", "1"),
				),
			},
			resource.TestStep{
				Config: testAccVpcConfigTwo,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "type", "2"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "vpc_network", "172.24.0.0/16"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "description", "test"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "name", "test"),
				),
			},
		},
	})

}

func testAccCheckVpcExists(n string, router *qc.DescribeRoutersOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Vpc ID is set")
		}

		client := testAccProvider.Meta().(*QingCloudClient)
		input := new(qc.DescribeRoutersInput)
		input.Routers = []*string{qc.String(rs.Primary.ID)}
		d, err := client.router.DescribeRouters(input)

		log.Printf("[WARN] router id %#v", rs.Primary.ID)

		if err != nil {
			return err
		}

		if d == nil {
			return fmt.Errorf("Router not found ")
		}

		*router = *d
		return nil
	}
}

func testAccCheckVpcDestroy(s *terraform.State) error {
	return testAccCheckVpcDestroyWithProvider(s, testAccProvider)
}

func testAccCheckVpcDestroyWithProvider(s *terraform.State, provider *schema.Provider) error {
	client := provider.Meta().(*QingCloudClient)
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "qingcloud_vpc" {
			continue
		}

		// Try to find the resource
		input := new(qc.DescribeRoutersInput)
		input.Routers = []*string{qc.String(rs.Primary.ID)}
		output, err := client.router.DescribeRouters(input)
		if err == nil && qc.IntValue(output.RetCode) == 0 {
			if len(output.RouterSet) != 0 && qc.StringValue(output.RouterSet[0].Status) != "deleted" {
				return fmt.Errorf("Found  Router: %s", rs.Primary.ID)
			}
		}
	}
	return nil
}

const testAccVpcConfig = `
resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "192.168.0.0/16"
} `
const testAccVpcConfigTwo = `
resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "172.24.0.0/16"
	name ="test"
	description = "test"
	type = 2
} `
<commit_msg>add vpc with tag test<commit_after>package qingcloud

import (
	"fmt"
	"log"
	"testing"
	"os"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	qc "github.com/yunify/qingcloud-sdk-go/service"

)

func TestAccQingcloudVpc_basic(t *testing.T) {
	var vpc qc.DescribeRoutersOutput

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},

		// module name
		IDRefreshName: "qingcloud_vpc.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckVpcDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccVpcConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "vpc_network", "192.168.0.0/16"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "type", "1"),
				),
			},
			resource.TestStep{
				Config: testAccVpcConfigTwo,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "type", "2"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "vpc_network", "172.24.0.0/16"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "description", "test"),
					resource.TestCheckResourceAttr(
						"qingcloud_vpc.foo", "name", "test"),
				),
			},
		},
	})

}

func TestAccQingcloudVpc_tag(t *testing.T) {
	var vpc qc.DescribeRoutersOutput
	vpcTag1Name := os.Getenv("TRAVIS_BUILD_ID") + "-" + os.Getenv("TRAVIS_JOB_NUMBER") + "-vpc-tag1"
	vpcTag2Name := os.Getenv("TRAVIS_BUILD_ID") + "-" + os.Getenv("TRAVIS_JOB_NUMBER") + "-vpc-tag2"
	testTagNameValue := func(names ...string) resource.TestCheckFunc {
		return func(state *terraform.State) error {
			tags := vpc.RouterSet[0].Tags
			sameCount := 0
			for _, tag := range tags {
				for _, name := range names {
					if qc.StringValue(tag.TagName) == name {
						sameCount++
					}
					if sameCount == len(vpc.RouterSet[0].Tags) {
						return nil
					}
				}
			}
			return fmt.Errorf("tag name error %#v", names)
		}
	}
	testTagDetach := func() resource.TestCheckFunc {
		return func(state *terraform.State) error {
			if len(vpc.RouterSet[0].Tags) != 0 {
				return fmt.Errorf("tag not detach ")
			}
			return nil
		}
	}

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},

		IDRefreshName: "qingcloud_vpc.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckVpcDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: fmt.Sprintf(testAccVpcConfigTagTemplate, vpcTag1Name, vpcTag2Name),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					testTagNameValue(vpcTag1Name, vpcTag2Name),
				),
			},
			resource.TestStep{
				Config: fmt.Sprintf(testAccVpcConfigTagTwoTemplate, vpcTag1Name, vpcTag2Name),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckVpcExists(
						"qingcloud_vpc.foo", &vpc),
					testTagDetach(),
				),
			},
		},
	})

}

func testAccCheckVpcExists(n string, router *qc.DescribeRoutersOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Vpc ID is set")
		}

		client := testAccProvider.Meta().(*QingCloudClient)
		input := new(qc.DescribeRoutersInput)
		input.Routers = []*string{qc.String(rs.Primary.ID)}
		d, err := client.router.DescribeRouters(input)

		log.Printf("[WARN] router id %#v", rs.Primary.ID)

		if err != nil {
			return err
		}

		if d == nil {
			return fmt.Errorf("Router not found ")
		}

		*router = *d
		return nil
	}
}

func testAccCheckVpcDestroy(s *terraform.State) error {
	return testAccCheckVpcDestroyWithProvider(s, testAccProvider)
}

func testAccCheckVpcDestroyWithProvider(s *terraform.State, provider *schema.Provider) error {
	client := provider.Meta().(*QingCloudClient)
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "qingcloud_vpc" {
			continue
		}

		// Try to find the resource
		input := new(qc.DescribeRoutersInput)
		input.Routers = []*string{qc.String(rs.Primary.ID)}
		output, err := client.router.DescribeRouters(input)
		if err == nil && qc.IntValue(output.RetCode) == 0 {
			if len(output.RouterSet) != 0 && qc.StringValue(output.RouterSet[0].Status) != "deleted" {
				return fmt.Errorf("Found  Router: %s", rs.Primary.ID)
			}
		}
	}
	return nil
}

const testAccVpcConfig = `
resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "192.168.0.0/16"
}
`
const testAccVpcConfigTwo = `
resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "172.24.0.0/16"
	name ="test"
	description = "test"
	type = 2
}
`

const testAccVpcConfigTagTemplate = `

resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "192.168.0.0/16"
	tag_ids = ["${qingcloud_tag.test.id}",
				"${qingcloud_tag.test2.id}"]
}
resource "qingcloud_tag" "test"{
	name="%v"
}
resource "qingcloud_tag" "test2"{
	name="%v"
}
`

const testAccVpcConfigTagTwoTemplate = `

resource "qingcloud_security_group" "foo" {
    name = "first_sg"
}
resource "qingcloud_vpc" "foo" {
	security_group_id = "${qingcloud_security_group.foo.id}"
	vpc_network = "192.168.0.0/16"
}
resource "qingcloud_tag" "test"{
	name="%v"
}
resource "qingcloud_tag" "test2"{
	name="%v"
}
`
<|endoftext|>


<commit_before>package aws

import (
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"testing"
)

func TestAccDataSourceAwsLocalGateways_basic(t *testing.T) {
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsLocalGatewaysConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsVpcsDataSourceExists("data.aws_local_gateways.all"),
				),
			},
		},
	})
}

const testAccDataSourceAwsLocalGatewaysConfig = `data "aws_local_gateways" "all" {}`
<commit_msg>Break dependency on VPC tests<commit_after>package aws

import (
	"fmt"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
	"testing"
)

func TestAccDataSourceAwsLocalGateways_basic(t *testing.T) {
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsLocalGatewaysConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLocalGatewaysDataSourceExists("data.aws_local_gateways.all"),
				),
			},
		},
	})
}

func testAccCheckAwsLocalGatewaysDataSourceExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Can't find aws_local_gateways data source: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("aws_local_gateways data source ID not set")
		}
		return nil
	}
}

const testAccDataSourceAwsLocalGatewaysConfig = `data "aws_local_gateways" "all" {}`
<|endoftext|>


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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/coreos/fuze/third_party/github.com/coreos/ignition/config"
	"github.com/coreos/fuze/third_party/github.com/go-yaml/yaml"
)

var (
	flagHelp    = flag.Bool("help", false, "print help and exit")
	flagInFile  = flag.String("in-file", "/dev/stdin", "input file (YAML)")
	flagOutFile = flag.String("out-file", "/dev/stdout", "output file (JSON)")
)

func stderr(f string, a ...interface{}) {
	out := fmt.Sprintf(f, a...)
	fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func main() {
	flag.Parse()

	if *flagHelp {
		flag.Usage()
		return
	}

	cfg := config.Config{}
	dataIn, err := ioutil.ReadFile(*flagInFile)
	if err != nil {
		stderr("Failed to read: %v", err)
		os.Exit(1)
	}

	if err := yaml.Unmarshal(dataIn, &cfg); err != nil {
		stderr("Failed to unmarshal input: %v", err)
		os.Exit(1)
	}

	dataOut, err := json.Marshal(&cfg)
	if err != nil {
		stderr("Failed to marshal output: %v", err)
		os.Exit(1)
	}

	if err := ioutil.WriteFile(*flagOutFile, dataOut, 0640); err != nil {
		stderr("Failed to write: %v", err)
		os.Exit(1)
	}
}
<commit_msg>main: clean up flags<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/coreos/fuze/third_party/github.com/coreos/ignition/config"
	"github.com/coreos/fuze/third_party/github.com/go-yaml/yaml"
)

func stderr(f string, a ...interface{}) {
	out := fmt.Sprintf(f, a...)
	fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func main() {
	flags := struct {
		help    bool
		inFile  string
		outFile string
	}{}

	flag.BoolVar(&flags.help, "help", false, "print help and exit")
	flag.StringVar(&flags.inFile, "in-file", "/dev/stdin", "input file (YAML)")
	flag.StringVar(&flags.outFile, "out-file", "/dev/stdout", "output file (JSON)")

	flag.Parse()

	if flags.help {
		flag.Usage()
		return
	}

	cfg := config.Config{}
	dataIn, err := ioutil.ReadFile(flags.inFile)
	if err != nil {
		stderr("Failed to read: %v", err)
		os.Exit(1)
	}

	if err := yaml.Unmarshal(dataIn, &cfg); err != nil {
		stderr("Failed to unmarshal input: %v", err)
		os.Exit(1)
	}

	dataOut, err := json.Marshal(&cfg)
	if err != nil {
		stderr("Failed to marshal output: %v", err)
		os.Exit(1)
	}

	if err := ioutil.WriteFile(flags.outFile, dataOut, 0640); err != nil {
		stderr("Failed to write: %v", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package dtest

import (
	"fmt"
	"os"
	"time"

	"github.com/datawire/teleproxy/pkg/k8s"
	"github.com/datawire/teleproxy/pkg/kubeapply"
)

// K8sApply applies the supplied manifests to the cluster indicated by
// the supplied kubeconfig.
func K8sApply(files ...string) {
	if os.Getenv("DOCKER_REGISTRY") == "" {
		os.Setenv("DOCKER_REGISTRY", DockerRegistry())
	}
	kubeconfig := Kubeconfig()
	err := kubeapply.Kubeapply(k8s.NewKubeInfo(kubeconfig, "", ""), 120*time.Second, false, false, files...)
	if err != nil {
		fmt.Println()
		fmt.Println(err)
		fmt.Printf(`
  Please note, if this is a timeout, then your kubernetes cluster may not
  exist or may be unreachable. Check access to your cluster with "kubectl --kubeconfig %s".

`, kubeconfig)
		os.Exit(1)
	}
}
<commit_msg>Run `kubectl get (stuff)` when K8sApply fails<commit_after>package dtest

import (
	"fmt"
	"os"
	"time"

	"github.com/datawire/teleproxy/pkg/k8s"
	"github.com/datawire/teleproxy/pkg/kubeapply"
	"github.com/datawire/teleproxy/pkg/supervisor"
)

// K8sApply applies the supplied manifests to the cluster indicated by
// the supplied kubeconfig.
func K8sApply(files ...string) {
	if os.Getenv("DOCKER_REGISTRY") == "" {
		os.Setenv("DOCKER_REGISTRY", DockerRegistry())
	}
	kubeconfig := Kubeconfig()
	err := kubeapply.Kubeapply(k8s.NewKubeInfo(kubeconfig, "", ""), 120*time.Second, false, false, files...)
	if err != nil {
		fmt.Println()
		fmt.Println(err)
		fmt.Printf(`
  Please note, if this is a timeout, then your kubernetes cluster may not
  exist or may be unreachable. Check access to your cluster with "kubectl --kubeconfig %s".

`, kubeconfig)
		fmt.Println()
		cmd := supervisor.Command(
			prefix, "kubectl", "--kubeconfig", kubeconfig,
			"get", "--all-namespaces", "ns,svc,deploy,po",
		)
		_ = cmd.Run() // Command output and any error will be logged

		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package httpproxy

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/rancher/norman/httperror"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/signer/v4"
	"github.com/rancher/types/apis/core/v1"
)

type Signer interface {
	sign(*http.Request, v1.SecretInterface, string) error
}

const ForwardForHeader = "X-Forwarded-For"

func newSigner(auth string) Signer {
	splitAuth := strings.Split(auth, " ")
	switch strings.ToLower(splitAuth[0]) {
	case "awsv4":
		return awsv4{}
	case "bearer":
		return bearer{}
	case "basic":
		return basic{}
	case "digest":
		return digest{}
	case "arbitrary":
		return arbitrary{}
	}
	return nil
}

func (br bearer) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"passwordField", "credID"})
	if err != nil {
		return err
	}
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Bearer", secret[data["passwordField"]]))
	return nil
}

func (b basic) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"usernameField", "passwordField", "credID"})
	if err != nil {
		return err
	}
	key := fmt.Sprintf("%s:%s", secret[data["usernameField"]], secret[data["passwordField"]])
	encoded := base64.URLEncoding.EncodeToString([]byte(key))
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Basic", encoded))
	return nil
}

func (a awsv4) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	_, secret, err := getAuthData(auth, secrets, []string{"credID"})
	if err != nil {
		return err
	}
	service, region := a.getServiceAndRegion(req.URL.Host)
	creds := credentials.NewStaticCredentials(secret["accessKey"], secret["secretKey"], "")
	awsSigner := v4.NewSigner(creds)
	var body []byte
	if req.Body != nil {
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return fmt.Errorf("error reading request body %v", err)
		}
	}
	val := req.Header.Get(ForwardForHeader)
	if val != "" {
		req.Header.Del(ForwardForHeader)
	}
	_, err = awsSigner.Sign(req, bytes.NewReader(body), service, region, time.Now())
	if err != nil {
		return err
	}
	if val != "" {
		req.Header.Add(ForwardForHeader, val)
	}
	return nil
}

func (a awsv4) getServiceAndRegion(host string) (string, string) {
	//format : service.region.amazonaws.com
	parts := strings.Split(host, ".")
	if len(parts) != 4 {
		return "", ""
	}
	return parts[0], parts[1]
}

func (d digest) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"usernameField", "passwordField", "credID"})
	if err != nil {
		return err
	}
	resp, err := doNewRequest(req) // request to get challenge fields from server
	if err != nil {
		return err
	}
	challengeData, err := parseChallenge(resp.Header.Get("WWW-Authenticate"))
	if err != nil {
		return err
	}
	challengeData["username"] = secret[data["usernameField"]]
	challengeData["password"] = secret[data["passwordField"]]
	signature, err := buildSignature(challengeData, req)
	if err != nil {
		return err
	}
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Digest", signature))
	return nil
}

func doNewRequest(req *http.Request) (*http.Response, error) {
	newReq, err := http.NewRequest(req.Method, req.URL.String(), nil)
	if err != nil {
		return nil, err
	}
	newReq.Header.Set("Content-Type", "application/json")
	client := http.Client{}
	resp, err := client.Do(newReq)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != httperror.Unauthorized.Status {
		return nil, fmt.Errorf("expected 401 status code, got %v", resp.StatusCode)
	}
	resp.Body.Close()
	return resp, err
}

func parseChallenge(header string) (map[string]string, error) {
	if header == "" {
		return nil, fmt.Errorf("failed to get WWW-Authenticate header")
	}
	s := strings.Trim(header, " \n\r\t")
	if !strings.HasPrefix(s, "Digest ") {
		return nil, fmt.Errorf("bad challenge %s", header)
	}
	data := map[string]string{}
	s = strings.Trim(s[7:], " \n\r\t")
	terms := strings.Split(s, ", ")
	for _, term := range terms {
		splitTerm := strings.SplitN(term, "=", 2)
		data[splitTerm[0]] = strings.Trim(splitTerm[1], "\"")
	}
	return data, nil
}

func formResponse(qop string, data map[string]string, req *http.Request) (string, string) {
	hash1 := hash(fmt.Sprintf("%s:%s:%s", data["username"], data["realm"], data["password"]))
	hash2 := hash(fmt.Sprintf("%s:%s", req.Method, req.URL.Path))
	if qop == "" {
		return hash(fmt.Sprintf("%s:%s:%s", hash1, data["nonce"], hash2)), ""

	} else if qop == "auth" {
		cnonce := data["cnonce"]
		if cnonce == "" {
			cnonce = getCnonce()
		}
		return hash(fmt.Sprintf("%s:%s:%08x:%s:%s:%s",
			hash1, data["nonce"], 00000001, cnonce, qop, hash2)), cnonce
	}
	return "", ""
}

func buildSignature(data map[string]string, req *http.Request) (string, error) {
	qop, ok := data["qop"]
	if ok && qop != "auth" && qop != "" {
		return "", fmt.Errorf("qop not implemented %s", data["qop"])
	}
	response, cnonce := formResponse(qop, data, req)
	if response == "" {
		return "", fmt.Errorf("error forming response qop: %s", qop)
	}
	auth := []string{fmt.Sprintf(`username="%s"`, data["username"])}
	auth = append(auth, fmt.Sprintf(`realm="%s"`, data["realm"]))
	auth = append(auth, fmt.Sprintf(`nonce="%s"`, data["nonce"]))
	auth = append(auth, fmt.Sprintf(`uri="%s"`, req.URL.Path))
	auth = append(auth, fmt.Sprintf(`response="%s"`, response))
	if val, ok := data["opaque"]; ok && val != "" {
		auth = append(auth, fmt.Sprintf(`opaque="%s"`, data["opaque"]))
	}
	if qop != "" {
		auth = append(auth, fmt.Sprintf("qop=%s", qop))
		auth = append(auth, fmt.Sprintf("nc=%08x", 00000001))
		auth = append(auth, fmt.Sprintf("cnonce=%s", cnonce))
	}
	return strings.Join(auth, ", "), nil
}

func hash(field string) string {
	f := md5.New()
	f.Write([]byte(field))
	return hex.EncodeToString(f.Sum(nil))
}

func getCnonce() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)[:16]
}

func (a arbitrary) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, _, err := getAuthData(auth, secrets, []string{})
	if err != nil {
		return err
	}
	splitHeaders := strings.Split(data["headers"], ",")
	for _, header := range splitHeaders {
		val := strings.SplitN(header, "=", 2)
		req.Header.Set(val[0], val[1])
	}
	return nil
}

type awsv4 struct{}

type bearer struct{}

type basic struct{}

type digest struct{}

type arbitrary struct{}
<commit_msg>consider only required headers for aws signing request<commit_after>package httpproxy

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/rancher/norman/httperror"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/signer/v4"
	"github.com/rancher/types/apis/core/v1"
)

var requiredHeadersForAws = map[string]bool{"host": true,
	"x-amz-content-sha256": true,
	"x-amz-date":           true,
	"x-amz-user-agent":     true}

type Signer interface {
	sign(*http.Request, v1.SecretInterface, string) error
}

func newSigner(auth string) Signer {
	splitAuth := strings.Split(auth, " ")
	switch strings.ToLower(splitAuth[0]) {
	case "awsv4":
		return awsv4{}
	case "bearer":
		return bearer{}
	case "basic":
		return basic{}
	case "digest":
		return digest{}
	case "arbitrary":
		return arbitrary{}
	}
	return nil
}

func (br bearer) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"passwordField", "credID"})
	if err != nil {
		return err
	}
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Bearer", secret[data["passwordField"]]))
	return nil
}

func (b basic) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"usernameField", "passwordField", "credID"})
	if err != nil {
		return err
	}
	key := fmt.Sprintf("%s:%s", secret[data["usernameField"]], secret[data["passwordField"]])
	encoded := base64.URLEncoding.EncodeToString([]byte(key))
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Basic", encoded))
	return nil
}

func (a awsv4) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	_, secret, err := getAuthData(auth, secrets, []string{"credID"})
	if err != nil {
		return err
	}
	service, region := a.getServiceAndRegion(req.URL.Host)
	creds := credentials.NewStaticCredentials(secret["accessKey"], secret["secretKey"], "")
	awsSigner := v4.NewSigner(creds)
	var body []byte
	if req.Body != nil {
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return fmt.Errorf("error reading request body %v", err)
		}
	}
	oldHeader, newHeader := http.Header{}, http.Header{}
	for header, value := range req.Header {
		if _, ok := requiredHeadersForAws[strings.ToLower(header)]; ok {
			newHeader[header] = value
		} else {
			oldHeader[header] = value
		}
	}
	req.Header = newHeader
	_, err = awsSigner.Sign(req, bytes.NewReader(body), service, region, time.Now())
	if err != nil {
		return err
	}
	for key, val := range oldHeader {
		req.Header.Add(key, strings.Join(val, ""))
	}
	return nil
}

func (a awsv4) getServiceAndRegion(host string) (string, string) {
	//format : service.region.amazonaws.com
	parts := strings.Split(host, ".")
	if len(parts) != 4 {
		return "", ""
	}
	return parts[0], parts[1]
}

func (d digest) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, secret, err := getAuthData(auth, secrets, []string{"usernameField", "passwordField", "credID"})
	if err != nil {
		return err
	}
	resp, err := doNewRequest(req) // request to get challenge fields from server
	if err != nil {
		return err
	}
	challengeData, err := parseChallenge(resp.Header.Get("WWW-Authenticate"))
	if err != nil {
		return err
	}
	challengeData["username"] = secret[data["usernameField"]]
	challengeData["password"] = secret[data["passwordField"]]
	signature, err := buildSignature(challengeData, req)
	if err != nil {
		return err
	}
	req.Header.Set(AuthHeader, fmt.Sprintf("%s %s", "Digest", signature))
	return nil
}

func doNewRequest(req *http.Request) (*http.Response, error) {
	newReq, err := http.NewRequest(req.Method, req.URL.String(), nil)
	if err != nil {
		return nil, err
	}
	newReq.Header.Set("Content-Type", "application/json")
	client := http.Client{}
	resp, err := client.Do(newReq)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != httperror.Unauthorized.Status {
		return nil, fmt.Errorf("expected 401 status code, got %v", resp.StatusCode)
	}
	resp.Body.Close()
	return resp, err
}

func parseChallenge(header string) (map[string]string, error) {
	if header == "" {
		return nil, fmt.Errorf("failed to get WWW-Authenticate header")
	}
	s := strings.Trim(header, " \n\r\t")
	if !strings.HasPrefix(s, "Digest ") {
		return nil, fmt.Errorf("bad challenge %s", header)
	}
	data := map[string]string{}
	s = strings.Trim(s[7:], " \n\r\t")
	terms := strings.Split(s, ", ")
	for _, term := range terms {
		splitTerm := strings.SplitN(term, "=", 2)
		data[splitTerm[0]] = strings.Trim(splitTerm[1], "\"")
	}
	return data, nil
}

func formResponse(qop string, data map[string]string, req *http.Request) (string, string) {
	hash1 := hash(fmt.Sprintf("%s:%s:%s", data["username"], data["realm"], data["password"]))
	hash2 := hash(fmt.Sprintf("%s:%s", req.Method, req.URL.Path))
	if qop == "" {
		return hash(fmt.Sprintf("%s:%s:%s", hash1, data["nonce"], hash2)), ""

	} else if qop == "auth" {
		cnonce := data["cnonce"]
		if cnonce == "" {
			cnonce = getCnonce()
		}
		return hash(fmt.Sprintf("%s:%s:%08x:%s:%s:%s",
			hash1, data["nonce"], 00000001, cnonce, qop, hash2)), cnonce
	}
	return "", ""
}

func buildSignature(data map[string]string, req *http.Request) (string, error) {
	qop, ok := data["qop"]
	if ok && qop != "auth" && qop != "" {
		return "", fmt.Errorf("qop not implemented %s", data["qop"])
	}
	response, cnonce := formResponse(qop, data, req)
	if response == "" {
		return "", fmt.Errorf("error forming response qop: %s", qop)
	}
	auth := []string{fmt.Sprintf(`username="%s"`, data["username"])}
	auth = append(auth, fmt.Sprintf(`realm="%s"`, data["realm"]))
	auth = append(auth, fmt.Sprintf(`nonce="%s"`, data["nonce"]))
	auth = append(auth, fmt.Sprintf(`uri="%s"`, req.URL.Path))
	auth = append(auth, fmt.Sprintf(`response="%s"`, response))
	if val, ok := data["opaque"]; ok && val != "" {
		auth = append(auth, fmt.Sprintf(`opaque="%s"`, data["opaque"]))
	}
	if qop != "" {
		auth = append(auth, fmt.Sprintf("qop=%s", qop))
		auth = append(auth, fmt.Sprintf("nc=%08x", 00000001))
		auth = append(auth, fmt.Sprintf("cnonce=%s", cnonce))
	}
	return strings.Join(auth, ", "), nil
}

func hash(field string) string {
	f := md5.New()
	f.Write([]byte(field))
	return hex.EncodeToString(f.Sum(nil))
}

func getCnonce() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)[:16]
}

func (a arbitrary) sign(req *http.Request, secrets v1.SecretInterface, auth string) error {
	data, _, err := getAuthData(auth, secrets, []string{})
	if err != nil {
		return err
	}
	splitHeaders := strings.Split(data["headers"], ",")
	for _, header := range splitHeaders {
		val := strings.SplitN(header, "=", 2)
		req.Header.Set(val[0], val[1])
	}
	return nil
}

type awsv4 struct{}

type bearer struct{}

type basic struct{}

type digest struct{}

type arbitrary struct{}
<|endoftext|>


<commit_before>// problem60.go
//
// The primes 3, 7, 109, and 673, are quite remarkable. By taking any two
// primes and concatenating them in any order the result will always be prime.
// For example, taking 7 and 109, both 7109 and 1097 are prime. The sum of
// these four primes, 792, represents the lowest sum for a set of four primes
// with this property.
//
// Find the lowest sum for a set of five primes for which any two primes
// concatenate to produce another prime.

package main

import (
	"euler/tools"
	"fmt"
	"strconv"
	"sync"
)

type cache struct {
	sync.Mutex
	m map[string]bool
}

func newCache() *cache {
	return &cache{sync.Mutex{}, make(map[string]bool)}
}

func (c *cache) concatsToPrime(x, y int) bool {
	key := strconv.Itoa(x) + strconv.Itoa(y)
	c.Lock()
	defer c.Unlock()
	// Try to find the answer in the cache.
	if val, ok := c.m[key]; ok {
		return val
	}
	// Otherwise find it manually and add to the cache.
	xstr, ystr := strconv.Itoa(x), strconv.Itoa(y)
	a, _ := strconv.Atoi(xstr + ystr)
	b, _ := strconv.Atoi(ystr + xstr)
	val := tools.IsPrime(a) && tools.IsPrime(b)
	c.m[key] = val
	return val
}

func (c *cache) allConcatToPrime(n node) bool {
	for _, x := range n {
		for _, y := range n {
			if x != y && x < y {
				if c.concatsToPrime(x, y) == false {
					return false
				}
			}
		}
	}
	return true
}

// A node is a candidate solution to the problem.
type node []int

func (n node) max() int {
	if len(n) == 0 {
		panic("max: node has 0 elements")
	}
	x := n[0]
	for _, y := range n[1:] {
		if y > x {
			x = y
		}
	}
	return x
}

func (n node) sum() int {
	res := 0
	for _, x := range n {
		res += x
	}
	return res
}

// Stack is a simple stack implementation.
type stack []node

// Pop a node off the stack. Panics if s is empty.
func (s *stack) pop() node {
	x := (*s)[len(*s)-1]
	*s = (*s)[:len(*s)-1]

	// Shrink the underlying array if the slice length <= 1/4 its capacity.
	if len(*s) <= cap(*s)/4 {
		*s = append([]node{}, *s...)
	}
	return x
}

// Push a node onto the stack.
func (s *stack) push(x node) {
	*s = append(*s, x)
}

// We are going to use a concurrent depth-first search with a worker goroutine
// pool of 4. Each goroutine will search for a solution from a different
// starting prime. As soon as a solution is found, we return from the function.
// Otherwise, we wait for all starting primes to be checked, and return an
// error.
func problem60() (int, error) {
	// It's not clear how many primes to search through. Experimentation
	// suggests a limit of 9000 produces the correct answer: 26033. Note
	// our algorithm does not guarantee the solution is the smallest
	// possible, but as a matter of fact, it is. We could verify our
	// answer by raising the limit to 26033, searching exhaustively, and
	// observing that no smaller solutions are found.
	limit := 9000
	var primes []int
	for i := 0; i < limit; i++ {
		if tools.IsPrime(i) {
			primes = append(primes, i)
		}
	}

	c := newCache()
	ans := make(chan int)   // Used to send the answer.
	done := make(chan bool) // Used to signal that all worker goroutines are done.
	pchan := make(chan int) // Used to send worker goroutines a starting prime to search.
	var wg sync.WaitGroup

	// Woker goroutine pool of 4.
	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			for {
				p, ok := <-pchan
				if !ok {
					wg.Done()
					return
				}

				// Perform depth-first search starting at the given prime.
				var frontier stack
				frontier.push(node{p})

				for len(frontier) != 0 {
					n := frontier.pop()
					if len(n) == 5 {
						ans <- n.sum()
					}
					for _, prime := range primes {
						child := append(append(*new(node), n...), prime)
						if prime > n.max() && c.allConcatToPrime(child) {
							frontier.push(child)
						}
					}
				}
			}
		}()
	}

	go func() {
		for _, p := range primes {
			pchan <- p
		}
		close(pchan)
		wg.Wait()    // Wait for all workers to complete their search
		done <- true // before sending completion signal.
	}()

	for {
		select {
		case x := <-ans:
			return x, nil
		case <-done:
			return -1, fmt.Errorf("problem60: no solution found with limit %v", limit)
		}
	}
}

func main() {
	ans, err := problem60()
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(ans)
	}
}
<commit_msg>Use generic Stack from tools.go in problem60.go<commit_after>// problem60.go
//
// The primes 3, 7, 109, and 673, are quite remarkable. By taking any two
// primes and concatenating them in any order the result will always be prime.
// For example, taking 7 and 109, both 7109 and 1097 are prime. The sum of
// these four primes, 792, represents the lowest sum for a set of four primes
// with this property.
//
// Find the lowest sum for a set of five primes for which any two primes
// concatenate to produce another prime.

package main

import (
	"euler/tools"
	"fmt"
	"strconv"
	"sync"
)

type cache struct {
	sync.Mutex
	m map[string]bool
}

func newCache() *cache {
	return &cache{sync.Mutex{}, make(map[string]bool)}
}

func (c *cache) concatsToPrime(x, y int) bool {
	key := strconv.Itoa(x) + strconv.Itoa(y)
	c.Lock()
	defer c.Unlock()
	// Try to find the answer in the cache.
	if val, ok := c.m[key]; ok {
		return val
	}
	// Otherwise find it manually and add to the cache.
	xstr, ystr := strconv.Itoa(x), strconv.Itoa(y)
	a, _ := strconv.Atoi(xstr + ystr)
	b, _ := strconv.Atoi(ystr + xstr)
	val := tools.IsPrime(a) && tools.IsPrime(b)
	c.m[key] = val
	return val
}

func (c *cache) allConcatToPrime(n Node) bool {
	for _, x := range n {
		for _, y := range n {
			if x != y && x < y {
				if c.concatsToPrime(x, y) == false {
					return false
				}
			}
		}
	}
	return true
}

// A Node is a candidate solution to the problem.
type Node []int

// We are going to use a concurrent depth-first search with a worker goroutine
// pool of 4. Each goroutine will search for a solution from a different
// starting prime. As soon as a solution is found, we return from the function.
// Otherwise, we wait for all starting primes to be checked, and return an
// error.
func problem60() (int, error) {
	// It's not clear how many primes to search through. Experimentation
	// suggests a limit of 9000 produces the correct answer: 26033. Note
	// our algorithm does not guarantee the solution is the smallest
	// possible, but as a matter of fact, it is. We could verify our
	// answer by raising the limit to 26033, searching exhaustively, and
	// observing that no smaller solutions are found.
	limit := 9000
	var primes []int
	for i := 0; i < limit; i++ {
		if tools.IsPrime(i) {
			primes = append(primes, i)
		}
	}

	c := newCache()
	ans := make(chan int)   // Used to send the answer.
	done := make(chan bool) // Used to signal that all worker goroutines are done.
	pchan := make(chan int) // Used to send worker goroutines a starting prime to search.
	var wg sync.WaitGroup

	// Woker goroutine pool of 4.
	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			for {
				p, ok := <-pchan
				if !ok {
					wg.Done()
					return
				}

				// Perform depth-first search starting at the given prime.
				var frontier tools.Stack
				frontier.Push(Node{p})

				for len(frontier) != 0 {
					n := frontier.Pop().(Node)
					if len(n) == 5 {
						ans <- tools.Sum(n...)
					}
					for _, prime := range primes {
						child := append(append(*new(Node), n...), prime)
						if prime > tools.Max(n...) && c.allConcatToPrime(child) {
							frontier.Push(child)
						}
					}
				}
			}
		}()
	}

	go func() {
		for _, p := range primes {
			pchan <- p
		}
		close(pchan)
		wg.Wait()    // Wait for all workers to complete their search
		done <- true // before sending completion signal.
	}()

	for {
		select {
		case x := <-ans:
			return x, nil
		case <-done:
			return -1, fmt.Errorf("problem60: no solution found with limit %v", limit)
		}
	}
}

func main() {
	ans, err := problem60()
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(ans)
	}
}
<|endoftext|>