<commit_before>/*
Copyright 2013 The Perkeep Authors

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

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

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

package integration

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	"perkeep.org/pkg/test"
)

// Test that running:
//   $ camput permanode
// ... creates and uploads a permanode, and that we can camget it back.
func TestCamputPermanode(t *testing.T) {
	w := test.GetWorld(t)
	br := w.NewPermanode(t)

	out := test.MustRunCmd(t, w.Cmd("camget", br.String()))
	mustHave := []string{
		`{"camliVersion": 1,`,
		`"camliSigner": "`,
		`"camliType": "permanode",`,
		`random": "`,
		`,"camliSig":"`,
	}
	for _, str := range mustHave {
		if !strings.Contains(out, str) {
			t.Errorf("Expected permanode response to contain %q; it didn't. Got: %s", str, out)
		}
	}
}

func TestWebsocketQuery(t *testing.T) {
	w := test.GetWorld(t)
	pn := w.NewPermanode(t)
	test.MustRunCmd(t, w.Cmd("camput", "attr", pn.String(), "tag", "foo"))

	check := func(err error) {
		if err != nil {
			t.Fatal(err)
		}
	}

	const bufSize = 1 << 20

	c, err := net.Dial("tcp", w.Addr())
	if err != nil {
		t.Fatalf("Dial: %v", err)
	}
	defer c.Close()

	wc, _, err := websocket.NewClient(c, &url.URL{Host: w.Addr(), Path: w.SearchHandlerPath() + "ws"}, nil, bufSize, bufSize)
	check(err)

	msg, err := wc.NextWriter(websocket.TextMessage)
	check(err)

	_, err = msg.Write([]byte(`{"tag": "foo", "query": { "expression": "tag:foo" }}`))
	check(err)
	check(msg.Close())

	errc := make(chan error, 1)
	go func() {
		inType, inMsg, err := wc.ReadMessage()
		if err != nil {
			errc <- err
			return
		}
		if !strings.HasPrefix(string(inMsg), `{"tag":"_status"`) {
			errc <- fmt.Errorf("unexpected message type=%d msg=%q, wanted status update", inType, inMsg)
			return
		}
		inType, inMsg, err = wc.ReadMessage()
		if err != nil {
			errc <- err
			return
		}
		if strings.Contains(string(inMsg), pn.String()) {
			errc <- nil
			return
		}
		errc <- fmt.Errorf("unexpected message type=%d msg=%q", inType, inMsg)
	}()
	select {
	case err := <-errc:
		if err != nil {
			t.Error(err)
		}
	case <-time.After(5 * time.Second):
		t.Error("timeout")
	}
}

func TestInternalHandler(t *testing.T) {
	w := test.GetWorld(t)
	tests := map[string]int{
		"/no-http-storage/":                                                    401,
		"/no-http-handler/":                                                    401,
		"/good-status/":                                                        200,
		"/bs-and-maybe-also-index/camli":                                       400,
		"/bs/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8":              404,
		"/no-http-storage/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8": 401,
	}
	for suffix, want := range tests {
		res, err := http.Get(w.ServerBaseURL() + suffix)
		if err != nil {
			t.Fatalf("On %s: %v", suffix, err)
		}
		if res.StatusCode != want {
			t.Errorf("For %s: Status = %d; want %d", suffix, res.StatusCode, want)
		}
		res.Body.Close()
	}
}

func TestNoTestingLinking(t *testing.T) {
	w, err := test.NewWorld()
	if err != nil {
		t.Fatal(err)
	}
	help, err := w.Help()
	if err != nil {
		t.Fatalf("Error running camlistored -help: %v, %v", string(help), err)
	}
	sc := bufio.NewScanner(bytes.NewReader(help))
	for sc.Scan() {
		l := strings.TrimSpace(sc.Text())
		if strings.HasPrefix(l, "-test.") {
			t.Fatal("test flag detected in help output of camlistored, because testing pkg got linked into binary")
		}
	}
	if err := sc.Err(); err != nil {
		t.Fatal(err)
	}
}

func mustTempDir(t *testing.T) (name string, cleanup func()) {
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	return dir, func() { os.RemoveAll(dir) }
}

func mustWriteFile(t *testing.T, path, contents string) {
	err := ioutil.WriteFile(path, []byte(contents), 0644)
	if err != nil {
		t.Fatal(err)
	}
}

// Run camput in the environment it runs in under the Android app.
// This matches how camput is used in UploadThread.java.
func TestAndroidCamputFile(t *testing.T) {
	w := test.GetWorld(t)
	// UploadThread.java sets:
	//   CAMLI_AUTH (set by w.CmdWithEnv)
	//   CAMLI_TRUSTED_CERT (not needed)
	//   CAMLI_CACHE_DIR
	//   CAMPUT_ANDROID_OUTPUT=1
	cacheDir, clean := mustTempDir(t)
	defer clean()
	env := []string{
		"CAMPUT_ANDROID_OUTPUT=1",
		"CAMLI_CACHE_DIR=" + cacheDir,
	}
	cmd := w.CmdWithEnv("camput",
		env,
		"--server="+w.ServerBaseURL(),
		"file",
		"-stdinargs",
		"-vivify")
	cmd.Stderr = os.Stderr
	in, err := cmd.StdinPipe()
	if err != nil {
		t.Fatal(err)
	}
	out, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatal(err)
	}
	if err := w.Ping(); err != nil {
		t.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}
	defer cmd.Process.Kill()

	srcDir, clean := mustTempDir(t)
	defer clean()

	file1 := filepath.Join(srcDir, "file1.txt")
	mustWriteFile(t, file1, "contents 1")
	file2 := filepath.Join(srcDir, "file2.txt")
	mustWriteFile(t, file2, "contents 2 longer length")

	go func() {
		fmt.Fprintf(in, "%s\n", file1)
		fmt.Fprintf(in, "%s\n", file2)
	}()

	waitc := make(chan error)
	go func() {
		sc := bufio.NewScanner(out)
		fileUploaded := 0
		for sc.Scan() {
			t.Logf("Got: %q", sc.Text())
			f := strings.Fields(sc.Text())
			if len(f) == 0 {
				t.Logf("empty text?")
				continue
			}
			if f[0] == "FILE_UPLOADED" {
				fileUploaded++
				if fileUploaded == 2 {
					break
				}
			}
		}
		in.Close()
		if err := sc.Err(); err != nil {
			t.Error(err)
		}
	}()

	defer cmd.Process.Kill()
	go func() {
		waitc <- cmd.Wait()
	}()
	select {
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for camput to end")
	case err := <-waitc:
		if err != nil {
			t.Errorf("camput exited uncleanly: %v", err)
		}
	}
}
<commit_msg>pkg/test/integration: fix websocket integration test failures<commit_after>/*
Copyright 2013 The Perkeep Authors

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

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

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

package integration

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	"perkeep.org/pkg/test"
)

// Test that running:
//   $ camput permanode
// ... creates and uploads a permanode, and that we can camget it back.
func TestCamputPermanode(t *testing.T) {
	w := test.GetWorld(t)
	br := w.NewPermanode(t)

	out := test.MustRunCmd(t, w.Cmd("camget", br.String()))
	mustHave := []string{
		`{"camliVersion": 1,`,
		`"camliSigner": "`,
		`"camliType": "permanode",`,
		`random": "`,
		`,"camliSig":"`,
	}
	for _, str := range mustHave {
		if !strings.Contains(out, str) {
			t.Errorf("Expected permanode response to contain %q; it didn't. Got: %s", str, out)
		}
	}
}

func TestWebsocketQuery(t *testing.T) {
	w := test.GetWorld(t)
	pn := w.NewPermanode(t)
	test.MustRunCmd(t, w.Cmd("camput", "attr", pn.String(), "tag", "foo"))

	check := func(err error) {
		if err != nil {
			t.Fatalf("%v", err)
		}
	}

	const bufSize = 1 << 20

	dialer := websocket.Dialer{
		ReadBufferSize:  bufSize,
		WriteBufferSize: bufSize,
	}

	searchURL := (&url.URL{Scheme: "ws", Host: w.Addr(), Path: w.SearchHandlerPath() + "ws"}).String()
	wsHeaders := http.Header{
		"Origin": {"http://" + w.Addr()},
	}

	wc, _, err := dialer.Dial(searchURL, wsHeaders)
	check(err)

	msg, err := wc.NextWriter(websocket.TextMessage)
	check(err)

	_, err = msg.Write([]byte(`{"tag": "foo", "query": { "expression": "tag:foo" }}`))
	check(err)
	check(msg.Close())

	errc := make(chan error, 1)
	go func() {
		inType, inMsg, err := wc.ReadMessage()
		if err != nil {
			errc <- err
			return
		}
		if !strings.HasPrefix(string(inMsg), `{"tag":"_status"`) {
			errc <- fmt.Errorf("unexpected message type=%d msg=%q, wanted status update", inType, inMsg)
			return
		}
		inType, inMsg, err = wc.ReadMessage()
		if err != nil {
			errc <- err
			return
		}
		if strings.Contains(string(inMsg), pn.String()) {
			errc <- nil
			return
		}
		errc <- fmt.Errorf("unexpected message type=%d msg=%q", inType, inMsg)
	}()
	select {
	case err := <-errc:
		if err != nil {
			t.Error(err)
		}
	case <-time.After(5 * time.Second):
		t.Error("timeout")
	}
}

func TestInternalHandler(t *testing.T) {
	w := test.GetWorld(t)
	tests := map[string]int{
		"/no-http-storage/":                                                    401,
		"/no-http-handler/":                                                    401,
		"/good-status/":                                                        200,
		"/bs-and-maybe-also-index/camli":                                       400,
		"/bs/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8":              404,
		"/no-http-storage/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8": 401,
	}
	for suffix, want := range tests {
		res, err := http.Get(w.ServerBaseURL() + suffix)
		if err != nil {
			t.Fatalf("On %s: %v", suffix, err)
		}
		if res.StatusCode != want {
			t.Errorf("For %s: Status = %d; want %d", suffix, res.StatusCode, want)
		}
		res.Body.Close()
	}
}

func TestNoTestingLinking(t *testing.T) {
	w, err := test.NewWorld()
	if err != nil {
		t.Fatal(err)
	}
	help, err := w.Help()
	if err != nil {
		t.Fatalf("Error running camlistored -help: %v, %v", string(help), err)
	}
	sc := bufio.NewScanner(bytes.NewReader(help))
	for sc.Scan() {
		l := strings.TrimSpace(sc.Text())
		if strings.HasPrefix(l, "-test.") {
			t.Fatal("test flag detected in help output of camlistored, because testing pkg got linked into binary")
		}
	}
	if err := sc.Err(); err != nil {
		t.Fatal(err)
	}
}

func mustTempDir(t *testing.T) (name string, cleanup func()) {
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	return dir, func() { os.RemoveAll(dir) }
}

func mustWriteFile(t *testing.T, path, contents string) {
	err := ioutil.WriteFile(path, []byte(contents), 0644)
	if err != nil {
		t.Fatal(err)
	}
}

// Run camput in the environment it runs in under the Android app.
// This matches how camput is used in UploadThread.java.
func TestAndroidCamputFile(t *testing.T) {
	w := test.GetWorld(t)
	// UploadThread.java sets:
	//   CAMLI_AUTH (set by w.CmdWithEnv)
	//   CAMLI_TRUSTED_CERT (not needed)
	//   CAMLI_CACHE_DIR
	//   CAMPUT_ANDROID_OUTPUT=1
	cacheDir, clean := mustTempDir(t)
	defer clean()
	env := []string{
		"CAMPUT_ANDROID_OUTPUT=1",
		"CAMLI_CACHE_DIR=" + cacheDir,
	}
	cmd := w.CmdWithEnv("camput",
		env,
		"--server="+w.ServerBaseURL(),
		"file",
		"-stdinargs",
		"-vivify")
	cmd.Stderr = os.Stderr
	in, err := cmd.StdinPipe()
	if err != nil {
		t.Fatal(err)
	}
	out, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatal(err)
	}
	if err := w.Ping(); err != nil {
		t.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}
	defer cmd.Process.Kill()

	srcDir, clean := mustTempDir(t)
	defer clean()

	file1 := filepath.Join(srcDir, "file1.txt")
	mustWriteFile(t, file1, "contents 1")
	file2 := filepath.Join(srcDir, "file2.txt")
	mustWriteFile(t, file2, "contents 2 longer length")

	go func() {
		fmt.Fprintf(in, "%s\n", file1)
		fmt.Fprintf(in, "%s\n", file2)
	}()

	waitc := make(chan error)
	go func() {
		sc := bufio.NewScanner(out)
		fileUploaded := 0
		for sc.Scan() {
			t.Logf("Got: %q", sc.Text())
			f := strings.Fields(sc.Text())
			if len(f) == 0 {
				t.Logf("empty text?")
				continue
			}
			if f[0] == "FILE_UPLOADED" {
				fileUploaded++
				if fileUploaded == 2 {
					break
				}
			}
		}
		in.Close()
		if err := sc.Err(); err != nil {
			t.Error(err)
		}
	}()

	defer cmd.Process.Kill()
	go func() {
		waitc <- cmd.Wait()
	}()
	select {
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for camput to end")
	case err := <-waitc:
		if err != nil {
			t.Errorf("camput exited uncleanly: %v", err)
		}
	}
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/boskos/client"
	"k8s.io/test-infra/boskos/common"
	"k8s.io/test-infra/boskos/mason"

	"istio.io/test-infra/boskos/gcp"
)

const (
	defaultBoskosRetryPeriod = 10 * time.Second
	// Large enought such that Reaper does not take resource away from us
	defaultBoskosSyncPeriod = 10 * time.Minute
	defaultTimeout          = "10m"
)

func defaultKubeconfig() string {
	home := os.Getenv("HOME")
	if home == "" {
		return ""
	}
	return fmt.Sprintf("%s/.kube/config", home)
}

var (
	owner       = flag.String("owner", "", "")
	rType       = flag.String("type", "", "Type of resource to acquire")
	timeoutStr  = flag.String("timeout", defaultTimeout, "Timeout ")
	kubecfgPath = flag.String("kubeconfig-save", defaultKubeconfig(), "Path to write kubeconfig file to")
	infoSave    = flag.String("info-save", "", "Path to save info")
	boskosURL   = flag.String("boskos-url", "http://boskos", "Boskos Server URL")
)

type masonClient struct {
	mason *mason.Client
	wg    sync.WaitGroup
}

func (m *masonClient) acquire(ctx context.Context, rtype, state string) (*common.Resource, error) {
	for {
		select {
		case <-time.After(defaultBoskosRetryPeriod):
			logrus.Infof("Attempting to acquire resource")
			res, err := m.mason.Acquire(rtype, common.Free, state)
			if err == nil {
				logrus.Infof("Resource %s acquired", res.Name)
				return res, nil
			}
			logrus.Infof("Failed to acquire resource")
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

func (m *masonClient) release(res common.Resource) {
	if err := m.mason.ReleaseOne(res.Name, common.Dirty); err != nil {
		logrus.WithError(err).Warningf("unable to release resource %s", res.Name)
		return
	}
	logrus.Infof("Released resource %s", res.Name)
}

func (m *masonClient) update(ctx context.Context, state string) {
	updateTick := time.NewTicker(defaultBoskosSyncPeriod).C
	go func() {
		for {
			select {
			case <-updateTick:
				if err := m.mason.UpdateAll(state); err != nil {
					logrus.WithError(err).Warningf("unable to update resources to state %s", state)
				}
				logrus.Infof("Updated resources")
			case <-ctx.Done():
				m.wg.Done()
				return
			}
		}
	}()
	m.wg.Add(1)
}

func saveUserdataToFile(ud *common.UserData, key, path string) error {
	v, ok := ud.Load(key)
	if !ok {
		return nil
	}
	return ioutil.WriteFile(path, []byte(v.(string)), 0644)
}

func wait() {
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop
}

func main() {
	flag.Parse()
	if *rType == "" {
		logrus.Errorf("flag --type must be set")
		flag.Usage()
		return
	}
	if *owner == "" {
		logrus.Errorf("flag --owner must be set")
		flag.Usage()
		return
	}
	timeout, err := time.ParseDuration(*timeoutStr)
	if err != nil {
		logrus.Errorf("unable to parse --timeout %s", *timeoutStr)
		flag.Usage()
		return
	}
	client := masonClient{mason: mason.NewClient(client.NewClient(*owner, *boskosURL))}
	if *kubecfgPath == "" {
		logrus.Panic("flag --type must be set")
	}
	c1, acquireCancel := context.WithTimeout(context.Background(), timeout)
	defer acquireCancel()
	res, err := client.acquire(c1, *rType, common.Busy)
	if err != nil {
		logrus.WithError(err).Panicf("unable to find a resource")
	}
	defer client.release(*res)
	c2, updateCancel := context.WithCancel(context.Background())
	defer updateCancel()
	client.update(c2, common.Busy)

loop:
	for cType := range res.UserData.ToMap() {
		switch cType {
		case gcp.ResourceConfigType:
			if *kubecfgPath != "" {
				var info gcp.ResourceInfo
				if err := res.UserData.Extract(gcp.ResourceConfigType, &info); err != nil {
					logrus.WithError(err).Panicf("unable to parse %s", gcp.ResourceConfigType)
				}
				if err := info.Install(*kubecfgPath); err != nil {
					logrus.WithError(err).Panicf("unable to install %s", gcp.ResourceConfigType)
				}
			}
			if *infoSave != "" {
				if err := saveUserdataToFile(res.UserData, gcp.ResourceConfigType, *infoSave); err != nil {
					logrus.WithError(err).Panicf("unable to save info to %s", *infoSave)
				}
				logrus.Infof("Saved user data to %s", *infoSave)
			}
			break loop
		}
	}
	logrus.Infof("READY")
	logrus.Infof("Type CTRL-C to interrupt")
	wait()
	updateCancel()
	client.wg.Wait()
}
<commit_msg>Set boskos sync period to 15 seconds (#1782)<commit_after>// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/boskos/client"
	"k8s.io/test-infra/boskos/common"
	"k8s.io/test-infra/boskos/mason"

	"istio.io/test-infra/boskos/gcp"
)

const (
	defaultBoskosRetryPeriod = 10 * time.Second
	// Large enought such that Reaper does not take resource away from us
	defaultBoskosSyncPeriod = 15 * time.Second
	defaultTimeout          = "10m"
)

func defaultKubeconfig() string {
	home := os.Getenv("HOME")
	if home == "" {
		return ""
	}
	return fmt.Sprintf("%s/.kube/config", home)
}

var (
	owner       = flag.String("owner", "", "")
	rType       = flag.String("type", "", "Type of resource to acquire")
	timeoutStr  = flag.String("timeout", defaultTimeout, "Timeout ")
	kubecfgPath = flag.String("kubeconfig-save", defaultKubeconfig(), "Path to write kubeconfig file to")
	infoSave    = flag.String("info-save", "", "Path to save info")
	boskosURL   = flag.String("boskos-url", "http://boskos", "Boskos Server URL")
)

type masonClient struct {
	mason *mason.Client
	wg    sync.WaitGroup
}

func (m *masonClient) acquire(ctx context.Context, rtype, state string) (*common.Resource, error) {
	for {
		select {
		case <-time.After(defaultBoskosRetryPeriod):
			logrus.Infof("Attempting to acquire resource")
			res, err := m.mason.Acquire(rtype, common.Free, state)
			if err == nil {
				logrus.Infof("Resource %s acquired", res.Name)
				return res, nil
			}
			logrus.Infof("Failed to acquire resource")
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

func (m *masonClient) release(res common.Resource) {
	if err := m.mason.ReleaseOne(res.Name, common.Dirty); err != nil {
		logrus.WithError(err).Warningf("unable to release resource %s", res.Name)
		return
	}
	logrus.Infof("Released resource %s", res.Name)
}

func (m *masonClient) update(ctx context.Context, state string) {
	updateTick := time.NewTicker(defaultBoskosSyncPeriod).C
	go func() {
		for {
			select {
			case <-updateTick:
				if err := m.mason.UpdateAll(state); err != nil {
					logrus.WithError(err).Warningf("unable to update resources to state %s", state)
				}
				logrus.Infof("Updated resources")
			case <-ctx.Done():
				m.wg.Done()
				return
			}
		}
	}()
	m.wg.Add(1)
}

func saveUserdataToFile(ud *common.UserData, key, path string) error {
	v, ok := ud.Load(key)
	if !ok {
		return nil
	}
	return ioutil.WriteFile(path, []byte(v.(string)), 0644)
}

func wait() {
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop
}

func main() {
	flag.Parse()
	if *rType == "" {
		logrus.Errorf("flag --type must be set")
		flag.Usage()
		return
	}
	if *owner == "" {
		logrus.Errorf("flag --owner must be set")
		flag.Usage()
		return
	}
	timeout, err := time.ParseDuration(*timeoutStr)
	if err != nil {
		logrus.Errorf("unable to parse --timeout %s", *timeoutStr)
		flag.Usage()
		return
	}
	client := masonClient{mason: mason.NewClient(client.NewClient(*owner, *boskosURL))}
	if *kubecfgPath == "" {
		logrus.Panic("flag --type must be set")
	}
	c1, acquireCancel := context.WithTimeout(context.Background(), timeout)
	defer acquireCancel()
	res, err := client.acquire(c1, *rType, common.Busy)
	if err != nil {
		logrus.WithError(err).Panicf("unable to find a resource")
	}
	defer client.release(*res)
	c2, updateCancel := context.WithCancel(context.Background())
	defer updateCancel()
	client.update(c2, common.Busy)

loop:
	for cType := range res.UserData.ToMap() {
		switch cType {
		case gcp.ResourceConfigType:
			if *kubecfgPath != "" {
				var info gcp.ResourceInfo
				if err := res.UserData.Extract(gcp.ResourceConfigType, &info); err != nil {
					logrus.WithError(err).Panicf("unable to parse %s", gcp.ResourceConfigType)
				}
				if err := info.Install(*kubecfgPath); err != nil {
					logrus.WithError(err).Panicf("unable to install %s", gcp.ResourceConfigType)
				}
			}
			if *infoSave != "" {
				if err := saveUserdataToFile(res.UserData, gcp.ResourceConfigType, *infoSave); err != nil {
					logrus.WithError(err).Panicf("unable to save info to %s", *infoSave)
				}
				logrus.Infof("Saved user data to %s", *infoSave)
			}
			break loop
		}
	}
	logrus.Infof("READY")
	logrus.Infof("Type CTRL-C to interrupt")
	wait()
	updateCancel()
	client.wg.Wait()
}
<|endoftext|>


<commit_before>package gtf

import (
	"bytes"
	"testing"
)

func AssertEqual(t *testing.T, buffer *bytes.Buffer, testString string) {
	if buffer.String() != testString {
		t.Errorf("Expected %s, got %s", testString, buffer.String())
	}
	buffer.Reset()
}

func ParseTest(buffer *bytes.Buffer, body string, data interface{}) {
	tpl := New("test").Funcs(GtfFuncMap)
	tpl.Parse(body)
	tpl.Execute(buffer, data)
}

func TestGtfFuncMap(t *testing.T) {
	var buffer bytes.Buffer

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | replace \" \" }}", "")
	AssertEqual(t, &buffer, "TheGoProgrammingLanguage")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | default \"default value\" }}", "")
	AssertEqual(t, &buffer, "The Go Programming Language")

	ParseTest(&buffer, "{{ \"\" | default \"default value\" }}", "")
	AssertEqual(t, &buffer, "default value")

	ParseTest(&buffer, "{{ . | default \"default value\" }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "[go python ruby]")

	ParseTest(&buffer, "{{ . | default 10 }}", []int{})
	AssertEqual(t, &buffer, "10")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | length }}", "")
	AssertEqual(t, &buffer, "27")

	ParseTest(&buffer, "{{ \"안녕하세요\" | length }}", "")
	AssertEqual(t, &buffer, "5")

	ParseTest(&buffer, "{{ . | length }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "3")

	ParseTest(&buffer, "{{ . | length }}", false)
	AssertEqual(t, &buffer, "0")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | lower }}", "")
	AssertEqual(t, &buffer, "the go programming language")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | upper }}", "")
	AssertEqual(t, &buffer, "THE GO PROGRAMMING LANGUAGE")

	ParseTest(&buffer, "{{ \"안녕하세요. 반갑습니다.\" | truncatechars 12 }}", "")
	AssertEqual(t, &buffer, "안녕하세요. 반갑...")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | truncatechars 12 }}", "")
	AssertEqual(t, &buffer, "The Go Pr...")

	ParseTest(&buffer, "{{ \"안녕하세요. The Go Programming Language\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "안녕하세요. The Go Programming L...")

	ParseTest(&buffer, "{{ \"The\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "The")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | truncatechars 3 }}", "")
	AssertEqual(t, &buffer, "The")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 6 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 0 }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars -1 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"http://www.example.org/foo?a=b&c=d\" | urlencode }}", "")
	AssertEqual(t, &buffer, "http%3A%2F%2Fwww.example.org%2Ffoo%3Fa%3Db%26c%3Dd")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | wordcount }}", "")
	AssertEqual(t, &buffer, "4")

	ParseTest(&buffer, "{{ \"      The      Go       Programming      Language        \" | wordcount }}", "")
	AssertEqual(t, &buffer, "4")

	ParseTest(&buffer, "{{ 21 | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 21 | divisibleby 4 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ 3.0 | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 3.0 | divisibleby 1.5 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | divisibleby 1.5 }}", uint(300))
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 12 | divisibleby . }}", uint(3))
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 21 | divisibleby 4 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ false | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ 3 | divisibleby false }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ \"Go\" | lengthis 2 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ \"안녕하세요.\" | lengthis 6 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ \"안녕하세요. Go!\" | lengthis 10 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | lengthis 3 }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | lengthis 3 }}", false)
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ \"       The Go Programming Language     \" | trim }}", "")
	AssertEqual(t, &buffer, "The Go Programming Language")

	ParseTest(&buffer, "{{ \"the go programming language\" | capfirst }}", "")
	AssertEqual(t, &buffer, "The go programming language")

	ParseTest(&buffer, "You have 0 message{{ 0 | pluralize \"s\" }}", "")
	AssertEqual(t, &buffer, "You have 0 messages")

	ParseTest(&buffer, "You have 1 message{{ 1 | pluralize \"s\" }}", "")
	AssertEqual(t, &buffer, "You have 1 message")

	ParseTest(&buffer, "0 cand{{ 0 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "0 candies")

	ParseTest(&buffer, "1 cand{{ 1 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "1 candy")

	ParseTest(&buffer, "2 cand{{ 2 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "2 candies")

	ParseTest(&buffer, "{{ 2 | pluralize \"y,ies,s\" }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "2 cand{{ . | pluralize \"y,ies\" }}", uint(2))
	AssertEqual(t, &buffer, "2 candies")

	ParseTest(&buffer, "1 cand{{ . | pluralize \"y,ies\" }}", uint(1))
	AssertEqual(t, &buffer, "1 candy")

	ParseTest(&buffer, "{{ . | pluralize \"y,ies\" }}", "test")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ true | yesno \"yes~\" \"no~\" }}", "")
	AssertEqual(t, &buffer, "yes~")

	ParseTest(&buffer, "{{ false | yesno \"yes~\" \"no~\" }}", "")
	AssertEqual(t, &buffer, "no~")

	ParseTest(&buffer, "{{ \"Go\" | rjust 10 }}", "")
	AssertEqual(t, &buffer, "        Go")

	ParseTest(&buffer, "{{ \"안녕하세요\" | rjust 10 }}", "")
	AssertEqual(t, &buffer, "     안녕하세요")

	ParseTest(&buffer, "{{ \"Go\" | ljust 10 }}", "")
	AssertEqual(t, &buffer, "Go        ")

	ParseTest(&buffer, "{{ \"안녕하세요\" | ljust 10 }}", "")
	AssertEqual(t, &buffer, "안녕하세요     ")

	ParseTest(&buffer, "{{ \"Go\" | center 10 }}", "")
	AssertEqual(t, &buffer, "    Go    ")

	ParseTest(&buffer, "{{ \"안녕하세요\" | center 10 }}", "")
	AssertEqual(t, &buffer, "  안녕하세요   ")

	ParseTest(&buffer, "{{ 123456789 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "117.7 MB")

	ParseTest(&buffer, "{{ 234 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "234 bytes")

	ParseTest(&buffer, "{{ 12345 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "12.1 KB")

	ParseTest(&buffer, "{{ 554832114 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "529.1 MB")

	ParseTest(&buffer, "{{ 1048576 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "1 MB")

	ParseTest(&buffer, "{{ 14868735121 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "13.8 GB")

	ParseTest(&buffer, "{{ 14868735121365 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "13.5 TB")

	ParseTest(&buffer, "{{ 1486873512136523 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "1.3 PB")

	ParseTest(&buffer, "{{ 12345.35335 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "12.1 KB")

	ParseTest(&buffer, "{{ 4294967293 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "4 GB")

	ParseTest(&buffer, "{{ \"Go\" | filesizeformat }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ . | filesizeformat }}", uint(500))
	AssertEqual(t, &buffer, "500 bytes")
}
<commit_msg>Improve test coverage.<commit_after>package gtf

import (
	"bytes"
	"testing"
)

func AssertEqual(t *testing.T, buffer *bytes.Buffer, testString string) {
	if buffer.String() != testString {
		t.Errorf("Expected %s, got %s", testString, buffer.String())
	}
	buffer.Reset()
}

func ParseTest(buffer *bytes.Buffer, body string, data interface{}) {
	tpl := New("test").Funcs(GtfFuncMap)
	tpl.Parse(body)
	tpl.Execute(buffer, data)
}

func TestGtfFuncMap(t *testing.T) {
	var buffer bytes.Buffer

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | replace \" \" }}", "")
	AssertEqual(t, &buffer, "TheGoProgrammingLanguage")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | default \"default value\" }}", "")
	AssertEqual(t, &buffer, "The Go Programming Language")

	ParseTest(&buffer, "{{ \"\" | default \"default value\" }}", "")
	AssertEqual(t, &buffer, "default value")

	ParseTest(&buffer, "{{ . | default \"default value\" }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "[go python ruby]")

	ParseTest(&buffer, "{{ . | default 10 }}", []int{})
	AssertEqual(t, &buffer, "10")

	ParseTest(&buffer, "{{ . | default \"empty\" }}", false)
	AssertEqual(t, &buffer, "empty")

	ParseTest(&buffer, "{{ . | default \"empty\" }}", 1)
	AssertEqual(t, &buffer, "1")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | length }}", "")
	AssertEqual(t, &buffer, "27")

	ParseTest(&buffer, "{{ \"안녕하세요\" | length }}", "")
	AssertEqual(t, &buffer, "5")

	ParseTest(&buffer, "{{ . | length }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "3")

	ParseTest(&buffer, "{{ . | length }}", false)
	AssertEqual(t, &buffer, "0")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | lower }}", "")
	AssertEqual(t, &buffer, "the go programming language")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | upper }}", "")
	AssertEqual(t, &buffer, "THE GO PROGRAMMING LANGUAGE")

	ParseTest(&buffer, "{{ \"안녕하세요. 반갑습니다.\" | truncatechars 12 }}", "")
	AssertEqual(t, &buffer, "안녕하세요. 반갑...")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | truncatechars 12 }}", "")
	AssertEqual(t, &buffer, "The Go Pr...")

	ParseTest(&buffer, "{{ \"안녕하세요. The Go Programming Language\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "안녕하세요. The Go Programming L...")

	ParseTest(&buffer, "{{ \"The\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "The")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | truncatechars 3 }}", "")
	AssertEqual(t, &buffer, "The")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 6 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 30 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars 0 }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ \"The Go\" | truncatechars -1 }}", "")
	AssertEqual(t, &buffer, "The Go")

	ParseTest(&buffer, "{{ \"http://www.example.org/foo?a=b&c=d\" | urlencode }}", "")
	AssertEqual(t, &buffer, "http%3A%2F%2Fwww.example.org%2Ffoo%3Fa%3Db%26c%3Dd")

	ParseTest(&buffer, "{{ \"The Go Programming Language\" | wordcount }}", "")
	AssertEqual(t, &buffer, "4")

	ParseTest(&buffer, "{{ \"      The      Go       Programming      Language        \" | wordcount }}", "")
	AssertEqual(t, &buffer, "4")

	ParseTest(&buffer, "{{ 21 | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 21 | divisibleby 4 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ 3.0 | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 3.0 | divisibleby 1.5 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | divisibleby 1.5 }}", uint(300))
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 12 | divisibleby . }}", uint(3))
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ 21 | divisibleby 4 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ false | divisibleby 3 }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ 3 | divisibleby false }}", "")
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ \"Go\" | lengthis 2 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ \"안녕하세요.\" | lengthis 6 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ \"안녕하세요. Go!\" | lengthis 10 }}", "")
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | lengthis 3 }}", []string{"go", "python", "ruby"})
	AssertEqual(t, &buffer, "true")

	ParseTest(&buffer, "{{ . | lengthis 3 }}", false)
	AssertEqual(t, &buffer, "false")

	ParseTest(&buffer, "{{ \"       The Go Programming Language     \" | trim }}", "")
	AssertEqual(t, &buffer, "The Go Programming Language")

	ParseTest(&buffer, "{{ \"the go programming language\" | capfirst }}", "")
	AssertEqual(t, &buffer, "The go programming language")

	ParseTest(&buffer, "You have 0 message{{ 0 | pluralize \"s\" }}", "")
	AssertEqual(t, &buffer, "You have 0 messages")

	ParseTest(&buffer, "You have 1 message{{ 1 | pluralize \"s\" }}", "")
	AssertEqual(t, &buffer, "You have 1 message")

	ParseTest(&buffer, "0 cand{{ 0 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "0 candies")

	ParseTest(&buffer, "1 cand{{ 1 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "1 candy")

	ParseTest(&buffer, "2 cand{{ 2 | pluralize \"y,ies\" }}", "")
	AssertEqual(t, &buffer, "2 candies")

	ParseTest(&buffer, "{{ 2 | pluralize \"y,ies,s\" }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "2 cand{{ . | pluralize \"y,ies\" }}", uint(2))
	AssertEqual(t, &buffer, "2 candies")

	ParseTest(&buffer, "1 cand{{ . | pluralize \"y,ies\" }}", uint(1))
	AssertEqual(t, &buffer, "1 candy")

	ParseTest(&buffer, "{{ . | pluralize \"y,ies\" }}", "test")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ true | yesno \"yes~\" \"no~\" }}", "")
	AssertEqual(t, &buffer, "yes~")

	ParseTest(&buffer, "{{ false | yesno \"yes~\" \"no~\" }}", "")
	AssertEqual(t, &buffer, "no~")

	ParseTest(&buffer, "{{ \"Go\" | rjust 10 }}", "")
	AssertEqual(t, &buffer, "        Go")

	ParseTest(&buffer, "{{ \"안녕하세요\" | rjust 10 }}", "")
	AssertEqual(t, &buffer, "     안녕하세요")

	ParseTest(&buffer, "{{ \"Go\" | ljust 10 }}", "")
	AssertEqual(t, &buffer, "Go        ")

	ParseTest(&buffer, "{{ \"안녕하세요\" | ljust 10 }}", "")
	AssertEqual(t, &buffer, "안녕하세요     ")

	ParseTest(&buffer, "{{ \"Go\" | center 10 }}", "")
	AssertEqual(t, &buffer, "    Go    ")

	ParseTest(&buffer, "{{ \"안녕하세요\" | center 10 }}", "")
	AssertEqual(t, &buffer, "  안녕하세요   ")

	ParseTest(&buffer, "{{ 123456789 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "117.7 MB")

	ParseTest(&buffer, "{{ 234 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "234 bytes")

	ParseTest(&buffer, "{{ 12345 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "12.1 KB")

	ParseTest(&buffer, "{{ 554832114 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "529.1 MB")

	ParseTest(&buffer, "{{ 1048576 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "1 MB")

	ParseTest(&buffer, "{{ 14868735121 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "13.8 GB")

	ParseTest(&buffer, "{{ 14868735121365 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "13.5 TB")

	ParseTest(&buffer, "{{ 1486873512136523 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "1.3 PB")

	ParseTest(&buffer, "{{ 12345.35335 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "12.1 KB")

	ParseTest(&buffer, "{{ 4294967293 | filesizeformat }}", "")
	AssertEqual(t, &buffer, "4 GB")

	ParseTest(&buffer, "{{ \"Go\" | filesizeformat }}", "")
	AssertEqual(t, &buffer, "")

	ParseTest(&buffer, "{{ . | filesizeformat }}", uint(500))
	AssertEqual(t, &buffer, "500 bytes")
}
<|endoftext|>


<commit_before>package gold

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

var (
	TimeFormat = "January 2, 2006"
	readMore   = "<!--readmore-->"
)

type Articles []*Article
type Article struct {
	Date     time.Time
	Title    string
	Slug     string
	Body     string
	Tags     Tags
	Enabled  bool
	Author   string
	Comments Comments
}

type YearMap map[int]Articles
type MonthMap map[int]Articles

func (a *Article) makeSlug() {
	r := strings.NewReplacer(" ", "-")
	a.Slug = r.Replace(strings.TrimSpace(a.Title))
}

func (a *Article) Publish() {
	a.Date = time.Now()
	a.Enabled = true
}

func (a *Article) Suppress() {
	a.Enabled = false
}

func (a *Article) AddComment(c *Comment) {
	a.Comments.Add(c)
}

func (a Articles) Len() int           { return len(a) }
func (a Articles) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a Articles) Less(i, j int) bool { return a[i].Date.Before(a[j].Date) }

func (a *Articles) Add(article *Article) error {
	article.Date = time.Now()
	if article.Slug == "" {
		article.makeSlug()
	}
	_, err := a.Find(article.Slug)
	if err == nil {
		return errors.New("duplicate slug " + article.Slug)
	}
	*a = append(*a, article)
	return nil
}

func (a Articles) Find(slug string) (*Article, error) {
	for i, _ := range a {
		if a[i].Slug == slug {
			return a[i], nil
		}
	}
	return nil, errors.New("not found")
}

func (a Articles) Page(page, app int) (Articles, int, int) {
	var next, prev int

	lastpage := len(a)/app + 1

	if page <= 1 {
		page = 1
	} else {
		prev = page - 1
	}

	if page >= lastpage {
		page = lastpage
	} else {
		next = page + 1
	}

	from := (page - 1) * app
	to := from + app - 1
	if to > len(a) {
		to = len(a)
	}

	return a[from:to], next, prev
}

func (a Article) PostDate() string {
	return a.Date.Local().Format(TimeFormat)
}

func (a Article) RssDate() string {
	return a.Date.Local().Format(time.RFC1123Z)
}

func (a Article) ReadMore() string {
	if i := strings.Index(a.Body, readMore); i > 0 {
		return a.Body[:i]
	}
	return a.Body
}

func (a Article) HasMore() bool {
	return strings.Contains(a.Body, readMore)
}

func (a Article) Year() int {
	return a.Date.Year()
}

func (a Article) Month() time.Month {
	return a.Date.Month()
}

func (a Articles) Year(year int) (A Articles) {
	if year == 0 {
		year = time.Now().Year()
	}
	for _, v := range a {
		if v.Date.Year() == year {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) Month(month time.Month) (A Articles) {
	if month == 0 {
		month = time.Now().Month()
	}
	for _, v := range a {
		if v.Date.Month() == month {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) Enabled() (A Articles) {
	for _, v := range a {
		if v.Enabled {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) YearMap() YearMap {
	ym := make(YearMap)
	for _, v := range a {
		y := v.Date.Year()
		ym[y] = append(ym[y], v)
	}
	return ym
}

func (a Articles) MonthMap() MonthMap {
	mm := make(MonthMap)
	for _, v := range a {
		m := int(v.Date.Month())
		mm[m] = append(mm[m], v)
	}
	return mm
}

func (a Article) FullPath() string {
	return fmt.Sprintf("/%.4d/%.2d/%s", a.Date.Year(), a.Date.Month(), a.Slug)
}
<commit_msg>add update article<commit_after>package gold

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

var (
	TimeFormat = "January 2, 2006"
	readMore   = "<!--readmore-->"
)

type Articles []*Article
type Article struct {
	Date     time.Time
	Title    string
	Slug     string
	Body     string
	Tags     Tags
	Enabled  bool
	Author   string
	Comments Comments
}

type YearMap map[int]Articles
type MonthMap map[int]Articles

func (a *Article) makeSlug() {
	r := strings.NewReplacer(" ", "-")
	a.Slug = r.Replace(strings.TrimSpace(a.Title))
}

func (a *Article) Publish() {
	a.Date = time.Now()
	a.Enabled = true
}

func (a *Article) Suppress() {
	a.Enabled = false
}

func (a *Article) AddComment(c *Comment) {
	a.Comments.Add(c)
}

func (a Articles) Len() int           { return len(a) }
func (a Articles) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a Articles) Less(i, j int) bool { return a[i].Date.Before(a[j].Date) }

func (a *Articles) Add(article *Article) error {
	article.Date = time.Now()
	if article.Slug == "" {
		article.makeSlug()
	}
	_, err := a.Find(article.Slug)
	if err == nil {
		return errors.New("duplicate slug " + article.Slug)
	}
	*a = append(*a, article)
	return nil
}

func (a *Articles) Update(article *Article) error {
	article.Date = time.Now()
	if article.Slug == "" {
		article.makeSlug()
	}
	/* FIXME: find and update */
	for i, _ := range *a {
		if (*a)[i].Slug == article.Slug {
			(*a)[i] = article
			return nil
		}
	}
	return errors.New("not found " + article.Slug)
}

func (a Articles) Find(slug string) (*Article, error) {
	for i, _ := range a {
		if a[i].Slug == slug {
			return a[i], nil
		}
	}
	return nil, errors.New("not found")
}

func (a Articles) Page(page, app int) (Articles, int, int) {
	var next, prev int

	lastpage := len(a)/app + 1

	if page <= 1 {
		page = 1
	} else {
		prev = page - 1
	}

	if page >= lastpage {
		page = lastpage
	} else {
		next = page + 1
	}

	from := (page - 1) * app
	to := from + app - 1
	if to > len(a) {
		to = len(a)
	}

	return a[from:to], next, prev
}

func (a Article) PostDate() string {
	return a.Date.Local().Format(TimeFormat)
}

func (a Article) RssDate() string {
	return a.Date.Local().Format(time.RFC1123Z)
}

func (a Article) ReadMore() string {
	if i := strings.Index(a.Body, readMore); i > 0 {
		return a.Body[:i]
	}
	return a.Body
}

func (a Article) HasMore() bool {
	return strings.Contains(a.Body, readMore)
}

func (a Article) Year() int {
	return a.Date.Year()
}

func (a Article) Month() time.Month {
	return a.Date.Month()
}

func (a Articles) Year(year int) (A Articles) {
	if year == 0 {
		year = time.Now().Year()
	}
	for _, v := range a {
		if v.Date.Year() == year {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) Month(month time.Month) (A Articles) {
	if month == 0 {
		month = time.Now().Month()
	}
	for _, v := range a {
		if v.Date.Month() == month {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) Enabled() (A Articles) {
	for _, v := range a {
		if v.Enabled {
			A = append(A, v)
		}
	}
	return A
}

func (a Articles) YearMap() YearMap {
	ym := make(YearMap)
	for _, v := range a {
		y := v.Date.Year()
		ym[y] = append(ym[y], v)
	}
	return ym
}

func (a Articles) MonthMap() MonthMap {
	mm := make(MonthMap)
	for _, v := range a {
		m := int(v.Date.Month())
		mm[m] = append(mm[m], v)
	}
	return mm
}

func (a Article) FullPath() string {
	return fmt.Sprintf("/%.4d/%.2d/%s", a.Date.Year(), a.Date.Month(), a.Slug)
}
<|endoftext|>


<commit_before>package brocketchat

import (
	"errors"
	"sync"

	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/42wim/matterbridge/hook/rockethook"
	"github.com/42wim/matterbridge/matterhook"
	lru "github.com/hashicorp/golang-lru"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/realtime"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/rest"
)

type Brocketchat struct {
	mh    *matterhook.Client
	rh    *rockethook.Client
	c     *realtime.Client
	r     *rest.Client
	cache *lru.Cache
	*bridge.Config
	messageChan chan models.Message
	channelMap  map[string]string
	user        *models.User
	sync.RWMutex
}

func New(cfg *bridge.Config) bridge.Bridger {
	newCache, err := lru.New(100)
	if err != nil {
		cfg.Log.Fatalf("Could not create LRU cache for rocketchat bridge: %v", err)
	}
	b := &Brocketchat{
		Config:      cfg,
		messageChan: make(chan models.Message),
		channelMap:  make(map[string]string),
		cache:       newCache,
	}
	b.Log.Debugf("enabling rocketchat")
	return b
}

func (b *Brocketchat) Command(cmd string) string {
	return ""
}

func (b *Brocketchat) Connect() error {
	if b.GetString("WebhookBindAddress") != "" {
		if err := b.doConnectWebhookBind(); err != nil {
			return err
		}
		go b.handleRocket()
		return nil
	}
	switch {
	case b.GetString("WebhookURL") != "":
		if err := b.doConnectWebhookURL(); err != nil {
			return err
		}
		go b.handleRocket()
		return nil
	case b.GetString("Login") != "":
		b.Log.Info("Connecting using login/password (sending and receiving)")
		err := b.apiLogin()
		if err != nil {
			return err
		}
		go b.handleRocket()
	}
	if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" &&
		b.GetString("Login") == "" {
		return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Login/Password/Server configured")
	}
	return nil
}

func (b *Brocketchat) Disconnect() error {
	return nil
}

func (b *Brocketchat) JoinChannel(channel config.ChannelInfo) error {
	if b.c == nil {
		return nil
	}
	id, err := b.c.GetChannelId(channel.Name)
	if err != nil {
		return err
	}
	b.Lock()
	b.channelMap[id] = channel.Name
	b.Unlock()
	mychannel := &models.Channel{ID: id, Name: channel.Name}
	if err := b.c.JoinChannel(id); err != nil {
		return err
	}
	if err := b.c.SubscribeToMessageStream(mychannel, b.messageChan); err != nil {
		return err
	}
	return nil
}

func (b *Brocketchat) Send(msg config.Message) (string, error) {
	channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}

	// Delete message
	if msg.Event == config.EventMsgDelete {
		if msg.ID == "" {
			return "", nil
		}
		return msg.ID, b.c.DeleteMessage(&models.Message{ID: msg.ID})
	}

	// Use webhook to send the message
	if b.GetString("WebhookURL") != "" {
		return "", b.sendWebhook(&msg)
	}

	// Prepend nick if configured
	if b.GetBool("PrefixMessagesWithNick") {
		msg.Text = msg.Username + msg.Text
	}

	// Edit message if we have an ID
	if msg.ID != "" {
		return msg.ID, b.c.EditMessage(&models.Message{ID: msg.ID, Msg: msg.Text, RoomID: b.getChannelID(msg.Channel)})
	}

	// Upload a file if it exists
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			smsg := &models.Message{
				RoomID: b.getChannelID(rmsg.Channel),
				Msg:    rmsg.Username + rmsg.Text,
				PostMessage: models.PostMessage{
					Avatar: rmsg.Avatar,
					Alias:  rmsg.Username,
				},
			}
			if _, err := b.c.SendMessage(smsg); err != nil {
				b.Log.Errorf("SendMessage failed: %s", err)
			}
		}
		if len(msg.Extra["file"]) > 0 {
			return "", b.handleUploadFile(&msg)
		}
	}

	smsg := &models.Message{
		RoomID: channel.ID,
		Msg:    msg.Text,
		PostMessage: models.PostMessage{
			Avatar: msg.Avatar,
			Alias:  msg.Username,
		},
	}

	rmsg, err := b.c.SendMessage(smsg)
	if rmsg == nil {
		return "", err
	}
	return rmsg.ID, err
}
<commit_msg>Allow the # in rocketchat channels (backward compatible) (#769)<commit_after>package brocketchat

import (
	"errors"
	"strings"
	"sync"

	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/42wim/matterbridge/hook/rockethook"
	"github.com/42wim/matterbridge/matterhook"
	lru "github.com/hashicorp/golang-lru"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/realtime"
	"github.com/matterbridge/Rocket.Chat.Go.SDK/rest"
)

type Brocketchat struct {
	mh    *matterhook.Client
	rh    *rockethook.Client
	c     *realtime.Client
	r     *rest.Client
	cache *lru.Cache
	*bridge.Config
	messageChan chan models.Message
	channelMap  map[string]string
	user        *models.User
	sync.RWMutex
}

func New(cfg *bridge.Config) bridge.Bridger {
	newCache, err := lru.New(100)
	if err != nil {
		cfg.Log.Fatalf("Could not create LRU cache for rocketchat bridge: %v", err)
	}
	b := &Brocketchat{
		Config:      cfg,
		messageChan: make(chan models.Message),
		channelMap:  make(map[string]string),
		cache:       newCache,
	}
	b.Log.Debugf("enabling rocketchat")
	return b
}

func (b *Brocketchat) Command(cmd string) string {
	return ""
}

func (b *Brocketchat) Connect() error {
	if b.GetString("WebhookBindAddress") != "" {
		if err := b.doConnectWebhookBind(); err != nil {
			return err
		}
		go b.handleRocket()
		return nil
	}
	switch {
	case b.GetString("WebhookURL") != "":
		if err := b.doConnectWebhookURL(); err != nil {
			return err
		}
		go b.handleRocket()
		return nil
	case b.GetString("Login") != "":
		b.Log.Info("Connecting using login/password (sending and receiving)")
		err := b.apiLogin()
		if err != nil {
			return err
		}
		go b.handleRocket()
	}
	if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" &&
		b.GetString("Login") == "" {
		return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Login/Password/Server configured")
	}
	return nil
}

func (b *Brocketchat) Disconnect() error {
	return nil
}

func (b *Brocketchat) JoinChannel(channel config.ChannelInfo) error {
	if b.c == nil {
		return nil
	}
	id, err := b.c.GetChannelId(strings.TrimPrefix(channel.Name, "#"))
	if err != nil {
		return err
	}
	b.Lock()
	b.channelMap[id] = channel.Name
	b.Unlock()
	mychannel := &models.Channel{ID: id, Name: strings.TrimPrefix(channel.Name, "#")}
	if err := b.c.JoinChannel(id); err != nil {
		return err
	}
	if err := b.c.SubscribeToMessageStream(mychannel, b.messageChan); err != nil {
		return err
	}
	return nil
}

func (b *Brocketchat) Send(msg config.Message) (string, error) {
	// strip the # if people has set this
	msg.Channel = strings.TrimPrefix(msg.Channel, "#")
	channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}

	// Delete message
	if msg.Event == config.EventMsgDelete {
		if msg.ID == "" {
			return "", nil
		}
		return msg.ID, b.c.DeleteMessage(&models.Message{ID: msg.ID})
	}

	// Use webhook to send the message
	if b.GetString("WebhookURL") != "" {
		return "", b.sendWebhook(&msg)
	}

	// Prepend nick if configured
	if b.GetBool("PrefixMessagesWithNick") {
		msg.Text = msg.Username + msg.Text
	}

	// Edit message if we have an ID
	if msg.ID != "" {
		return msg.ID, b.c.EditMessage(&models.Message{ID: msg.ID, Msg: msg.Text, RoomID: b.getChannelID(msg.Channel)})
	}

	// Upload a file if it exists
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			// strip the # if people has set this
			rmsg.Channel = strings.TrimPrefix(rmsg.Channel, "#")
			smsg := &models.Message{
				RoomID: b.getChannelID(rmsg.Channel),
				Msg:    rmsg.Username + rmsg.Text,
				PostMessage: models.PostMessage{
					Avatar: rmsg.Avatar,
					Alias:  rmsg.Username,
				},
			}
			if _, err := b.c.SendMessage(smsg); err != nil {
				b.Log.Errorf("SendMessage failed: %s", err)
			}
		}
		if len(msg.Extra["file"]) > 0 {
			return "", b.handleUploadFile(&msg)
		}
	}

	smsg := &models.Message{
		RoomID: channel.ID,
		Msg:    msg.Text,
		PostMessage: models.PostMessage{
			Avatar: msg.Avatar,
			Alias:  msg.Username,
		},
	}

	rmsg, err := b.c.SendMessage(smsg)
	if rmsg == nil {
		return "", err
	}
	return rmsg.ID, err
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 app implements a Server object for running the scheduler.
package app

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/algorithmprovider"
	schedulerapi "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/api"
	latestschedulerapi "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"

	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/spf13/pflag"
)

// SchedulerServer has all the context and params needed to run a Scheduler
type SchedulerServer struct {
	Port              int
	Address           util.IP
	ClientConfig      client.Config
	AlgorithmProvider string
	PolicyConfigFile  string
	EnableProfiling   bool
}

// NewSchedulerServer creates a new SchedulerServer with default parameters
func NewSchedulerServer() *SchedulerServer {
	s := SchedulerServer{
		Port:              ports.SchedulerPort,
		Address:           util.IP(net.ParseIP("127.0.0.1")),
		AlgorithmProvider: factory.DefaultProvider,
	}
	return &s
}

// AddFlags adds flags for a specific SchedulerServer to the specified FlagSet
func (s *SchedulerServer) AddFlags(fs *pflag.FlagSet) {
	fs.IntVar(&s.Port, "port", s.Port, "The port that the scheduler's http service runs on")
	fs.Var(&s.Address, "address", "The IP address to serve on (set to 0.0.0.0 for all interfaces)")
	client.BindClientConfigFlags(fs, &s.ClientConfig)
	fs.StringVar(&s.AlgorithmProvider, "algorithm_provider", s.AlgorithmProvider, "The scheduling algorithm provider to use")
	fs.StringVar(&s.PolicyConfigFile, "policy_config_file", s.PolicyConfigFile, "File with scheduler policy configuration")
	fs.BoolVar(&s.EnableProfiling, "profiling", false, "Enable profiling via web interface host:port/debug/pprof/")
}

// Run runs the specified SchedulerServer.  This should never exit.
func (s *SchedulerServer) Run(_ []string) error {
	kubeClient, err := client.New(&s.ClientConfig)
	if err != nil {
		glog.Fatalf("Invalid API configuration: %v", err)
	}

	go func() {
		if s.EnableProfiling {
			http.HandleFunc("/debug/pprof/", pprof.Index)
			http.HandleFunc("/debug/pprof/profile", pprof.Profile)
			http.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
		}
		http.Handle("/metrics", prometheus.Handler())
		http.ListenAndServe(net.JoinHostPort(s.Address.String(), strconv.Itoa(s.Port)), nil)
	}()

	configFactory := factory.NewConfigFactory(kubeClient)
	config, err := s.createConfig(configFactory)
	if err != nil {
		glog.Fatalf("Failed to create scheduler configuration: %v", err)
	}

	eventBroadcaster := record.NewBroadcaster()
	config.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"})
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))

	sched := scheduler.New(config)
	sched.Run()

	select {}
}

func (s *SchedulerServer) createConfig(configFactory *factory.ConfigFactory) (*scheduler.Config, error) {
	var policy schedulerapi.Policy
	var configData []byte

	if _, err := os.Stat(s.PolicyConfigFile); err == nil {
		configData, err = ioutil.ReadFile(s.PolicyConfigFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to read policy config: %v", err)
		}
		err = latestschedulerapi.Codec.DecodeInto(configData, &policy)
		if err != nil {
			return nil, fmt.Errorf("Invalid configuration: %v", err)
		}

		return configFactory.CreateFromConfig(policy)
	}

	// if the config file isn't provided, use the specified (or default) provider
	// check of algorithm provider is registered and fail fast
	_, err := factory.GetAlgorithmProvider(s.AlgorithmProvider)
	if err != nil {
		return nil, err
	}

	return configFactory.CreateFromProvider(s.AlgorithmProvider)
}
<commit_msg>Fix scheduler profiling<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 app implements a Server object for running the scheduler.
package app

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/algorithmprovider"
	schedulerapi "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/api"
	latestschedulerapi "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"

	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/spf13/pflag"
)

// SchedulerServer has all the context and params needed to run a Scheduler
type SchedulerServer struct {
	Port              int
	Address           util.IP
	ClientConfig      client.Config
	AlgorithmProvider string
	PolicyConfigFile  string
	EnableProfiling   bool
}

// NewSchedulerServer creates a new SchedulerServer with default parameters
func NewSchedulerServer() *SchedulerServer {
	s := SchedulerServer{
		Port:              ports.SchedulerPort,
		Address:           util.IP(net.ParseIP("127.0.0.1")),
		AlgorithmProvider: factory.DefaultProvider,
	}
	return &s
}

// AddFlags adds flags for a specific SchedulerServer to the specified FlagSet
func (s *SchedulerServer) AddFlags(fs *pflag.FlagSet) {
	fs.IntVar(&s.Port, "port", s.Port, "The port that the scheduler's http service runs on")
	fs.Var(&s.Address, "address", "The IP address to serve on (set to 0.0.0.0 for all interfaces)")
	client.BindClientConfigFlags(fs, &s.ClientConfig)
	fs.StringVar(&s.AlgorithmProvider, "algorithm_provider", s.AlgorithmProvider, "The scheduling algorithm provider to use")
	fs.StringVar(&s.PolicyConfigFile, "policy_config_file", s.PolicyConfigFile, "File with scheduler policy configuration")
	fs.BoolVar(&s.EnableProfiling, "profiling", true, "Enable profiling via web interface host:port/debug/pprof/")
}

// Run runs the specified SchedulerServer.  This should never exit.
func (s *SchedulerServer) Run(_ []string) error {
	kubeClient, err := client.New(&s.ClientConfig)
	if err != nil {
		glog.Fatalf("Invalid API configuration: %v", err)
	}

	go func() {
		mux := http.NewServeMux()
		if s.EnableProfiling {
			mux.HandleFunc("/debug/pprof/", pprof.Index)
			mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
			mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
		}
		mux.Handle("/metrics", prometheus.Handler())

		server := &http.Server{
			Addr:    net.JoinHostPort(s.Address.String(), strconv.Itoa(s.Port)),
			Handler: mux,
		}
		glog.Fatal(server.ListenAndServe())
	}()

	configFactory := factory.NewConfigFactory(kubeClient)
	config, err := s.createConfig(configFactory)
	if err != nil {
		glog.Fatalf("Failed to create scheduler configuration: %v", err)
	}

	eventBroadcaster := record.NewBroadcaster()
	config.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"})
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))

	sched := scheduler.New(config)
	sched.Run()

	select {}
}

func (s *SchedulerServer) createConfig(configFactory *factory.ConfigFactory) (*scheduler.Config, error) {
	var policy schedulerapi.Policy
	var configData []byte

	if _, err := os.Stat(s.PolicyConfigFile); err == nil {
		configData, err = ioutil.ReadFile(s.PolicyConfigFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to read policy config: %v", err)
		}
		err = latestschedulerapi.Codec.DecodeInto(configData, &policy)
		if err != nil {
			return nil, fmt.Errorf("Invalid configuration: %v", err)
		}

		return configFactory.CreateFromConfig(policy)
	}

	// if the config file isn't provided, use the specified (or default) provider
	// check of algorithm provider is registered and fail fast
	_, err := factory.GetAlgorithmProvider(s.AlgorithmProvider)
	if err != nil {
		return nil, err
	}

	return configFactory.CreateFromProvider(s.AlgorithmProvider)
}
<|endoftext|>


<commit_before>package docker

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

type DockerDriver struct {
	Ui  packer.Ui
	Ctx *interpolate.Context

	l sync.Mutex
}

func (d *DockerDriver) DeleteImage(id string) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "rmi", id)
	cmd.Stderr = &stderr

	log.Printf("Deleting image: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error deleting image: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Commit(id string, author string, changes []string, message string) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	args := []string{"commit"}
	if author != "" {
		args = append(args, "--author", author)
	}
	for _, change := range changes {
		args = append(args, "--change", change)
	}
	if message != "" {
		args = append(args, "--message", message)
	}
	args = append(args, id)

	log.Printf("Committing container with args: %v", args)
	cmd := exec.Command("docker", args...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return "", err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error committing container: %s\nStderr: %s",
			err, stderr.String())
		return "", err
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) Export(id string, dst io.Writer) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "export", id)
	cmd.Stdout = dst
	cmd.Stderr = &stderr

	log.Printf("Exporting container: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Import(path string, changes []string, repo string) (string, error) {
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	stdin, err := cmd.StdinPipe()

  if err != nil {
		return "", err
	}

	args := []string{"import"}

  for _, change := range changes {
    args = append(args, "--change", change)
  }

  args = append(args, "-")
	args = append(args, repo)
  cmd := exec.Command("docker", args...)

	// There should be only one artifact of the Docker builder
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

  log.Printf("Importing container with args: %v", args)

	if err := cmd.Start(); err != nil {
		return "", err
	}

	go func() {
		defer stdin.Close()
		io.Copy(stdin, file)
	}()

	if err := cmd.Wait(); err != nil {
		return "", fmt.Errorf("Error importing container: %s\n\nStderr: %s", err, stderr.String())
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) IPAddress(id string) (string, error) {
	var stderr, stdout bytes.Buffer
	cmd := exec.Command(
		"docker",
		"inspect",
		"--format",
		"{{ .NetworkSettings.IPAddress }}",
		id)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error: %s\n\nStderr: %s", err, stderr.String())
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) Login(repo, user, pass string) error {
	d.l.Lock()

	version_running, err := d.Version()
	if err != nil {
		d.l.Unlock()
		return err
	}

	// Version 17.07.0 of Docker adds support for the new
	// `--password-stdin` option which can be used to offer
	// password via the standard input, rather than passing
	// the password and/or token using a command line switch.
	constraint, err := version.NewConstraint(">= 17.07.0")
	if err != nil {
		d.l.Unlock()
		return err
	}

	cmd := exec.Command("docker")
	cmd.Args = append(cmd.Args, "login")

	if user != "" {
		cmd.Args = append(cmd.Args, "-u", user)
	}

	if pass != "" {
		if constraint.Check(version_running) {
			cmd.Args = append(cmd.Args, "--password-stdin")

			stdin, err := cmd.StdinPipe()
			if err != nil {
				d.l.Unlock()
				return err
			}
			io.WriteString(stdin, pass)
			stdin.Close()
		} else {
			cmd.Args = append(cmd.Args, "-p", pass)
		}
	}

	if repo != "" {
		cmd.Args = append(cmd.Args, repo)
	}

	err = runAndStream(cmd, d.Ui)
	if err != nil {
		d.l.Unlock()
		return err
	}

	return nil
}

func (d *DockerDriver) Logout(repo string) error {
	args := []string{"logout"}
	if repo != "" {
		args = append(args, repo)
	}

	cmd := exec.Command("docker", args...)
	err := runAndStream(cmd, d.Ui)
	d.l.Unlock()
	return err
}

func (d *DockerDriver) Pull(image string) error {
	cmd := exec.Command("docker", "pull", image)
	return runAndStream(cmd, d.Ui)
}

func (d *DockerDriver) Push(name string) error {
	cmd := exec.Command("docker", "push", name)
	return runAndStream(cmd, d.Ui)
}

func (d *DockerDriver) SaveImage(id string, dst io.Writer) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "save", id)
	cmd.Stdout = dst
	cmd.Stderr = &stderr

	log.Printf("Exporting image: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
	// Build up the template data
	var tplData startContainerTemplate
	tplData.Image = config.Image
	ctx := *d.Ctx
	ctx.Data = &tplData

	// Args that we're going to pass to Docker
	args := []string{"run"}
	if config.Privileged {
		args = append(args, "--privileged")
	}
	for host, guest := range config.Volumes {
		if runtime.GOOS == "windows" {
			// docker-toolbox can't handle the normal C:\filepath format in CLI
			host = strings.Replace(host, "\\", "/", -1)
			host = strings.Replace(host, "C:/", "/c/", 1)
		}
		args = append(args, "-v", fmt.Sprintf("%s:%s", host, guest))
	}
	for _, v := range config.RunCommand {
		v, err := interpolate.Render(v, &ctx)
		if err != nil {
			return "", err
		}

		args = append(args, v)
	}
	d.Ui.Message(fmt.Sprintf(
		"Run command: docker %s", strings.Join(args, " ")))

	// Start the container
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("docker", args...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	log.Printf("Starting container with args: %v", args)
	if err := cmd.Start(); err != nil {
		return "", err
	}

	log.Println("Waiting for container to finish starting")
	if err := cmd.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); ok {
			err = fmt.Errorf("Docker exited with a non-zero exit status.\nStderr: %s",
				stderr.String())
		}

		return "", err
	}

	// Capture the container ID, which is alone on stdout
	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) StopContainer(id string) error {
	if err := exec.Command("docker", "kill", id).Run(); err != nil {
		return err
	}

	return exec.Command("docker", "rm", id).Run()
}

func (d *DockerDriver) TagImage(id string, repo string, force bool) error {
	args := []string{"tag"}

	// detect running docker version before tagging
	// flag `force` for docker tagging was removed after Docker 1.12.0
	// to keep its backward compatibility, we are not going to remove `force`
	// option, but to ignore it when Docker version >= 1.12.0
	//
	// for more detail, please refer to the following links:
	// - https://docs.docker.com/engine/deprecated/#/f-flag-on-docker-tag
	// - https://github.com/docker/docker/pull/23090
	version_running, err := d.Version()
	if err != nil {
		return err
	}

	version_deprecated, err := version.NewVersion("1.12.0")
	if err != nil {
		// should never reach this line
		return err
	}

	if force {
		if version_running.LessThan(version_deprecated) {
			args = append(args, "-f")
		} else {
			// do nothing if Docker version >= 1.12.0
			log.Printf("[WARN] option: \"force\" will be ignored here")
			log.Printf("since it was removed after Docker 1.12.0 released")
		}
	}
	args = append(args, id, repo)

	var stderr bytes.Buffer
	cmd := exec.Command("docker", args...)
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error tagging image: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Verify() error {
	if _, err := exec.LookPath("docker"); err != nil {
		return err
	}

	return nil
}

func (d *DockerDriver) Version() (*version.Version, error) {
	output, err := exec.Command("docker", "-v").Output()
	if err != nil {
		return nil, err
	}

	match := regexp.MustCompile(version.VersionRegexpRaw).FindSubmatch(output)
	if match == nil {
		return nil, fmt.Errorf("unknown version: %s", output)
	}

	return version.NewVersion(string(match[0]))
}
<commit_msg>Adding driver changes.<commit_after>package docker

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

type DockerDriver struct {
	Ui  packer.Ui
	Ctx *interpolate.Context

	l sync.Mutex
}

func (d *DockerDriver) DeleteImage(id string) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "rmi", id)
	cmd.Stderr = &stderr

	log.Printf("Deleting image: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error deleting image: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Commit(id string, author string, changes []string, message string) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	args := []string{"commit"}
	if author != "" {
		args = append(args, "--author", author)
	}
	for _, change := range changes {
		args = append(args, "--change", change)
	}
	if message != "" {
		args = append(args, "--message", message)
	}
	args = append(args, id)

	log.Printf("Committing container with args: %v", args)
	cmd := exec.Command("docker", args...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return "", err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error committing container: %s\nStderr: %s",
			err, stderr.String())
		return "", err
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) Export(id string, dst io.Writer) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "export", id)
	cmd.Stdout = dst
	cmd.Stderr = &stderr

	log.Printf("Exporting container: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Import(path string, changes []string, repo string) (string, error) {
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	stdin, err := cmd.StdinPipe()

	if err != nil {
		return "", err
	}

	args := []string{"import"}

	for _, change := range changes {
		args = append(args, "--change", change)
	}

	args = append(args, "-")
	args = append(args, repo)
	cmd := exec.Command("docker", args...)

	// There should be only one artifact of the Docker builder
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	log.Printf("Importing container with args: %v", args)

	if err := cmd.Start(); err != nil {
		return "", err
	}

	go func() {
		defer stdin.Close()
		io.Copy(stdin, file)
	}()

	if err := cmd.Wait(); err != nil {
		return "", fmt.Errorf("Error importing container: %s\n\nStderr: %s", err, stderr.String())
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) IPAddress(id string) (string, error) {
	var stderr, stdout bytes.Buffer
	cmd := exec.Command(
		"docker",
		"inspect",
		"--format",
		"{{ .NetworkSettings.IPAddress }}",
		id)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error: %s\n\nStderr: %s", err, stderr.String())
	}

	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) Login(repo, user, pass string) error {
	d.l.Lock()

	version_running, err := d.Version()
	if err != nil {
		d.l.Unlock()
		return err
	}

	// Version 17.07.0 of Docker adds support for the new
	// `--password-stdin` option which can be used to offer
	// password via the standard input, rather than passing
	// the password and/or token using a command line switch.
	constraint, err := version.NewConstraint(">= 17.07.0")
	if err != nil {
		d.l.Unlock()
		return err
	}

	cmd := exec.Command("docker")
	cmd.Args = append(cmd.Args, "login")

	if user != "" {
		cmd.Args = append(cmd.Args, "-u", user)
	}

	if pass != "" {
		if constraint.Check(version_running) {
			cmd.Args = append(cmd.Args, "--password-stdin")

			stdin, err := cmd.StdinPipe()
			if err != nil {
				d.l.Unlock()
				return err
			}
			io.WriteString(stdin, pass)
			stdin.Close()
		} else {
			cmd.Args = append(cmd.Args, "-p", pass)
		}
	}

	if repo != "" {
		cmd.Args = append(cmd.Args, repo)
	}

	err = runAndStream(cmd, d.Ui)
	if err != nil {
		d.l.Unlock()
		return err
	}

	return nil
}

func (d *DockerDriver) Logout(repo string) error {
	args := []string{"logout"}
	if repo != "" {
		args = append(args, repo)
	}

	cmd := exec.Command("docker", args...)
	err := runAndStream(cmd, d.Ui)
	d.l.Unlock()
	return err
}

func (d *DockerDriver) Pull(image string) error {
	cmd := exec.Command("docker", "pull", image)
	return runAndStream(cmd, d.Ui)
}

func (d *DockerDriver) Push(name string) error {
	cmd := exec.Command("docker", "push", name)
	return runAndStream(cmd, d.Ui)
}

func (d *DockerDriver) SaveImage(id string, dst io.Writer) error {
	var stderr bytes.Buffer
	cmd := exec.Command("docker", "save", id)
	cmd.Stdout = dst
	cmd.Stderr = &stderr

	log.Printf("Exporting image: %s", id)
	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
	// Build up the template data
	var tplData startContainerTemplate
	tplData.Image = config.Image
	ctx := *d.Ctx
	ctx.Data = &tplData

	// Args that we're going to pass to Docker
	args := []string{"run"}
	if config.Privileged {
		args = append(args, "--privileged")
	}
	for host, guest := range config.Volumes {
		if runtime.GOOS == "windows" {
			// docker-toolbox can't handle the normal C:\filepath format in CLI
			host = strings.Replace(host, "\\", "/", -1)
			host = strings.Replace(host, "C:/", "/c/", 1)
		}
		args = append(args, "-v", fmt.Sprintf("%s:%s", host, guest))
	}
	for _, v := range config.RunCommand {
		v, err := interpolate.Render(v, &ctx)
		if err != nil {
			return "", err
		}

		args = append(args, v)
	}
	d.Ui.Message(fmt.Sprintf(
		"Run command: docker %s", strings.Join(args, " ")))

	// Start the container
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("docker", args...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	log.Printf("Starting container with args: %v", args)
	if err := cmd.Start(); err != nil {
		return "", err
	}

	log.Println("Waiting for container to finish starting")
	if err := cmd.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); ok {
			err = fmt.Errorf("Docker exited with a non-zero exit status.\nStderr: %s",
				stderr.String())
		}

		return "", err
	}

	// Capture the container ID, which is alone on stdout
	return strings.TrimSpace(stdout.String()), nil
}

func (d *DockerDriver) StopContainer(id string) error {
	if err := exec.Command("docker", "kill", id).Run(); err != nil {
		return err
	}

	return exec.Command("docker", "rm", id).Run()
}

func (d *DockerDriver) TagImage(id string, repo string, force bool) error {
	args := []string{"tag"}

	// detect running docker version before tagging
	// flag `force` for docker tagging was removed after Docker 1.12.0
	// to keep its backward compatibility, we are not going to remove `force`
	// option, but to ignore it when Docker version >= 1.12.0
	//
	// for more detail, please refer to the following links:
	// - https://docs.docker.com/engine/deprecated/#/f-flag-on-docker-tag
	// - https://github.com/docker/docker/pull/23090
	version_running, err := d.Version()
	if err != nil {
		return err
	}

	version_deprecated, err := version.NewVersion("1.12.0")
	if err != nil {
		// should never reach this line
		return err
	}

	if force {
		if version_running.LessThan(version_deprecated) {
			args = append(args, "-f")
		} else {
			// do nothing if Docker version >= 1.12.0
			log.Printf("[WARN] option: \"force\" will be ignored here")
			log.Printf("since it was removed after Docker 1.12.0 released")
		}
	}
	args = append(args, id, repo)

	var stderr bytes.Buffer
	cmd := exec.Command("docker", args...)
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		err = fmt.Errorf("Error tagging image: %s\nStderr: %s",
			err, stderr.String())
		return err
	}

	return nil
}

func (d *DockerDriver) Verify() error {
	if _, err := exec.LookPath("docker"); err != nil {
		return err
	}

	return nil
}

func (d *DockerDriver) Version() (*version.Version, error) {
	output, err := exec.Command("docker", "-v").Output()
	if err != nil {
		return nil, err
	}

	match := regexp.MustCompile(version.VersionRegexpRaw).FindSubmatch(output)
	if match == nil {
		return nil, fmt.Errorf("unknown version: %s", output)
	}

	return version.NewVersion(string(match[0]))
}
<|endoftext|>


<commit_before>package main

// autotest github.com/a8n [paths...] [packages...] [testflags]
//  - skip modified files based on regexp
//  - new module for log colorization
//  - use StringArray

import (
	"fmt"
	"github.com/go-fsnotify/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"syscall"
	"time"
)

type watcher struct {
	// Finished is signaled when the watcher is closed.
	Finished chan bool

	// SettleTime indicates how long to wait after the last file system change before launching.
	SettleTime time.Duration

	// IgnoreDirs lists the names of directories that should not be watched for changes.
	IgnoreDirs map[string]bool

	// TestFlags contains optional arguments for 'go test'.
	TestFlags []string

	debug       bool
	fs          *fsnotify.Watcher
	done        chan bool
	gosrc       string
	paths       []string
	timeSuccess time.Time
	timeFailure time.Time
	lastState   int
}

// Values for lastState
const (
	starting = iota
	working
	failing
)

func round(duration, interval time.Duration) time.Duration {
	var t int64 = int64(duration) + int64(interval)/2
	return time.Duration(t - t%int64(interval))
}

func newWatcher() (*watcher, error) {
	fs, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	self := &watcher{
		Finished:   make(chan bool),
		SettleTime: 2 * time.Second,
		IgnoreDirs: map[string]bool{".git": true},
		TestFlags:  make([]string, 0),
		debug:      false,
		fs:         fs,
		done:       make(chan bool),
		gosrc:      filepath.Join(os.Getenv("GOPATH"), "src"),
		paths:      make([]string, 0),
		lastState:  starting,
	}
	return self, nil
}

func (self *watcher) Close() error {
	return self.fs.Close()
}

func (self *watcher) Start() {
	go self.monitorChanges()
}

func (self *watcher) Stop() {
	self.done <- true
}

func (self *watcher) Add(path string) error {
	// watch the file system path
	err := self.fs.Add(path)
	if err != nil {
		log.Fatal(err)
	}
	self.paths = append(self.paths, path)

	// is it a package dir (under $GOPATH/src?)
	if pkg := self.getPackageName(path); pkg != "" && self.debug {
		log.Println("package:", pkg, "in path:", path)
	}

	log.Println("watching for changes:", path)
	return err
}

func (self *watcher) Remove(path string) error {
	// find path in self.paths, remove the entry
	for i, val := range self.paths {
		if val == path {
			// delete entry at position i
			copy(self.paths[i:], self.paths[i+1:])
			self.paths = self.paths[0 : len(self.paths)-1]
			break
		}
	}
	return self.fs.Remove(path)
}

// AddRecursive walks a directory recursively, and watches all subdirectories.
func (self *watcher) AddRecursive(path string) error {
	return filepath.Walk(path, func(subpath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if _, ignore := self.IgnoreDirs[info.Name()]; ignore {
			return filepath.SkipDir
		}
		if info.IsDir() {
			return self.Add(subpath)
		}
		return nil
	})
}

// RunTests invokes the 'go test' tool for all monitored packages.
func (self *watcher) RunTests() {
	if err := self.handleModifications(); err != nil {
		msg := "error: " + err.Error()
		if self.lastState != failing {
			self.timeFailure = time.Now()
		}
		if self.lastState == working {
			msg += fmt.Sprintf(" (%s success)", round(time.Since(self.timeSuccess), time.Second))
		}
		self.lastState = failing
		log.Println("\u001b[31m" + msg + "\u001b[0m")
	} else {
		msg := ""
		if self.lastState != working {
			self.timeSuccess = time.Now()
		}
		if self.lastState == failing {
			msg = fmt.Sprintf("success after %s failures", round(time.Since(self.timeFailure), time.Second))
		}
		self.lastState = working
		if len(msg) != 0 {
			log.Println("\u001b[32m" + msg + "\u001b[0m")
		}
	}
}

// monitorChanges is the main processing loop for file system notifications.
func (self *watcher) monitorChanges() {
	modified := false
	for {
		select {
		case <-self.done:
			self.Finished <- true
			return

		case err := <-self.fs.Errors:
			log.Println("error:", err)

		case event := <-self.fs.Events:
			mod, err := self.handleEvent(event)
			if err != nil {
				log.Println("error:", err)
			} else if mod {
				modified = true
			}

		case <-time.After(self.SettleTime):
			if modified {
				self.RunTests()
				modified = false
			}
		}
	}
}

// handleEvent handles a file system change notification.
func (self *watcher) handleEvent(event fsnotify.Event) (bool, error) {
	filename := event.Name
	modified := false

	if event.Op&fsnotify.Create != 0 {
		info, err := os.Stat(filename)
		if err != nil {
			return false, err
		}
		if info.IsDir() {
			self.Add(filename)
		} else {
			if self.debug {
				log.Println("created:", filename)
			}
			modified = true
		}
	}
	if event.Op&fsnotify.Remove != 0 {
		self.Remove(filename)
		if self.debug {
			log.Println("removed:", filename)
		}
		modified = true
	}
	if event.Op&fsnotify.Write != 0 {
		// TODO: match against a list?
		if matched, _ := regexp.MatchString(`\..*\.swp`, filepath.Base(filename)); matched {
			//log.Println("skipping:", filename)
			// skip this file
		} else {
			if self.debug {
				log.Println("modified:", filename)
			}
			modified = true
		}
	}
	return modified, nil
}

// handleModifications launches 'go test'.
func (self *watcher) handleModifications() error {
	args := make([]string, 1+len(self.TestFlags))
	args[0] = "test"
	copy(args[1:], self.TestFlags)
	npkg := 0
	for _, path := range self.paths {
		if pkg := self.getPackageName(path); pkg != "" {
			args = append(args, pkg)
			npkg++
		}
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("running go test with %d packages\n", npkg)
	return cmd.Run()
}

// getPackageName returns the go package name for a path, or "" if not a package dir.
func (self *watcher) getPackageName(path string) string {
	if pkg, err := filepath.Rel(self.gosrc, path); err == nil {
		return pkg
	}
	return ""
}

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

func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	return cwd
}

// findPackage looks for path in the current directory, and any go source dirs,
// and returns the resolved path or an empty string if not found.
func findPackage(path string) string {
	// check relative to current directory first
	if stat, err := os.Stat(path); err == nil && stat.IsDir() {
		if !filepath.IsAbs(path) {
			path = filepath.Join(getCwd(), path)
		}
		return path
	}

	// check GOROOT / GOPATH
	for _, srcDir := range build.Default.SrcDirs() {
		pkg, err := build.Default.Import(path, srcDir, build.FindOnly)
		if err == nil {
			return pkg.Dir
		}
	}

	log.Println("package not found:", path)
	return ""
}

func main() {
	for _, arg := range os.Args[1:] {
		if arg == "-h" || arg == "--help" {
			fmt.Printf(`Monitors the file system and automatically runs 'go test' on changes.

usage: %s [-h | --help] [testflags] [path...] [package...]

options:
  -h, --help   print this message
  testflags    flags supported by 'go test'; see 'go help testflag'
  path...      filesystem path, monitored recursively
  package...   go package name for which 'go test' will be issued
`, os.Args[0])
			os.Exit(0)
		}
	}
	if os.Getenv("GOPATH") == "" {
		log.Fatalln("GOPATH is not set")
	}

	w, err := newWatcher()
	if err != nil {
		log.Fatal(err)
	}
	w.SettleTime = 500 * time.Millisecond

	// signals used to stop
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func() {
		//signal := <-signals
		//log.Println("got signal:", signal)
		<-signals
		w.Stop()
	}()

	// monitor paths
	gotOne := false
	for _, arg := range os.Args[1:] {
		if arg[0] == '-' {
			w.TestFlags = append(w.TestFlags, arg)
		} else if path := findPackage(arg); path != "" {
			if err := w.AddRecursive(path); err != nil {
				log.Fatal(err)
			} else {
				gotOne = true
			}
		}
	}

	if !gotOne {
		log.Fatalln("no paths to watch")
	}

	w.Start()
	w.RunTests()
	<-w.Finished
	w.Close()

	log.Println("exiting")
}
<commit_msg>Skip modified files based on a list of regular expressions<commit_after>package main

// autotest github.com/a8n [paths...] [packages...] [testflags]
//  - new module for log colorization
//  - use StringArray

import (
	"fmt"
	"github.com/go-fsnotify/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"syscall"
	"time"
)

type watcher struct {
	// Finished is signaled when the watcher is closed.
	Finished chan bool

	// SettleTime indicates how long to wait after the last file system change before launching.
	SettleTime time.Duration

	// IgnoreDirs lists the names of directories that should not be watched for changes.
	IgnoreDirs map[string]bool

	// IgnoreFiles is a list of regular expression patterns for files that should be ignored.
	IgnoreFiles []*regexp.Regexp

	// TestFlags contains optional arguments for 'go test'.
	TestFlags []string

	debug       bool
	fs          *fsnotify.Watcher
	done        chan bool
	gosrc       string
	paths       []string
	timeSuccess time.Time
	timeFailure time.Time
	lastState   int
}

// Values for lastState
const (
	starting = iota
	working
	failing
)

func round(duration, interval time.Duration) time.Duration {
	var t int64 = int64(duration) + int64(interval)/2
	return time.Duration(t - t%int64(interval))
}

func newWatcher() (*watcher, error) {
	fs, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	self := &watcher{
		Finished:   make(chan bool),
		SettleTime: 2 * time.Second,
		IgnoreDirs: map[string]bool{".git": true},
		IgnoreFiles: []*regexp.Regexp{
			regexp.MustCompile(`\..*\.swp$`),
		},
		TestFlags: make([]string, 0),
		debug:     false,
		fs:        fs,
		done:      make(chan bool),
		gosrc:     filepath.Join(os.Getenv("GOPATH"), "src"),
		paths:     make([]string, 0),
		lastState: starting,
	}
	return self, nil
}

func (self *watcher) Close() error {
	return self.fs.Close()
}

func (self *watcher) Start() {
	go self.monitorChanges()
}

func (self *watcher) Stop() {
	self.done <- true
}

func (self *watcher) Add(path string) error {
	// watch the file system path
	err := self.fs.Add(path)
	if err != nil {
		log.Fatal(err)
	}
	self.paths = append(self.paths, path)

	// is it a package dir (under $GOPATH/src?)
	if pkg := self.getPackageName(path); pkg != "" && self.debug {
		log.Println("package:", pkg, "in path:", path)
	}

	log.Println("watching for changes:", path)
	return err
}

func (self *watcher) Remove(path string) error {
	// find path in self.paths, remove the entry
	for i, val := range self.paths {
		if val == path {
			// delete entry at position i
			copy(self.paths[i:], self.paths[i+1:])
			self.paths = self.paths[0 : len(self.paths)-1]
			break
		}
	}
	return self.fs.Remove(path)
}

// AddRecursive walks a directory recursively, and watches all subdirectories.
func (self *watcher) AddRecursive(path string) error {
	return filepath.Walk(path, func(subpath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if _, ignore := self.IgnoreDirs[info.Name()]; ignore {
			return filepath.SkipDir
		}
		if info.IsDir() {
			return self.Add(subpath)
		}
		return nil
	})
}

// RunTests invokes the 'go test' tool for all monitored packages.
func (self *watcher) RunTests() {
	if err := self.handleModifications(); err != nil {
		msg := "error: " + err.Error()
		if self.lastState != failing {
			self.timeFailure = time.Now()
		}
		if self.lastState == working {
			msg += fmt.Sprintf(" (%s success)", round(time.Since(self.timeSuccess), time.Second))
		}
		self.lastState = failing
		log.Println("\u001b[31m" + msg + "\u001b[0m")
	} else {
		msg := ""
		if self.lastState != working {
			self.timeSuccess = time.Now()
		}
		if self.lastState == failing {
			msg = fmt.Sprintf("success after %s failures", round(time.Since(self.timeFailure), time.Second))
		}
		self.lastState = working
		if len(msg) != 0 {
			log.Println("\u001b[32m" + msg + "\u001b[0m")
		}
	}
}

// monitorChanges is the main processing loop for file system notifications.
func (self *watcher) monitorChanges() {
	modified := false
	for {
		select {
		case <-self.done:
			self.Finished <- true
			return

		case err := <-self.fs.Errors:
			log.Println("error:", err)

		case event := <-self.fs.Events:
			mod, err := self.handleEvent(event)
			if err != nil {
				log.Println("error:", err)
			} else if mod {
				modified = true
			}

		case <-time.After(self.SettleTime):
			if modified {
				self.RunTests()
				modified = false
			}
		}
	}
}

// handleEvent handles a file system change notification.
func (self *watcher) handleEvent(event fsnotify.Event) (bool, error) {
	filename := event.Name
	modified := false

	if event.Op&fsnotify.Create != 0 {
		info, err := os.Stat(filename)
		if err != nil {
			return false, err
		}
		if info.IsDir() {
			self.Add(filename)
		} else {
			if self.debug {
				log.Println("created:", filename)
			}
			modified = true
		}
	}
	if event.Op&fsnotify.Remove != 0 {
		self.Remove(filename)
		if self.debug {
			log.Println("removed:", filename)
		}
		modified = true
	}
	if event.Op&fsnotify.Write != 0 {
		// skip file if it matches any regexp in IgnoreFiles
		skip := false
		base := filepath.Base(filename)
		for _, re := range self.IgnoreFiles {
			if re.MatchString(base) {
				skip = true
				break
			}
		}
		if skip {
			if self.debug {
				log.Println("skipping:", filename)
			}
		} else {
			if self.debug {
				log.Println("modified:", filename)
			}
			modified = true
		}
	}
	return modified, nil
}

// handleModifications launches 'go test'.
func (self *watcher) handleModifications() error {
	args := make([]string, 1+len(self.TestFlags))
	args[0] = "test"
	copy(args[1:], self.TestFlags)
	npkg := 0
	for _, path := range self.paths {
		if pkg := self.getPackageName(path); pkg != "" {
			args = append(args, pkg)
			npkg++
		}
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("running go test with %d packages\n", npkg)
	return cmd.Run()
}

// getPackageName returns the go package name for a path, or "" if not a package dir.
func (self *watcher) getPackageName(path string) string {
	if pkg, err := filepath.Rel(self.gosrc, path); err == nil {
		return pkg
	}
	return ""
}

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

func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	return cwd
}

// findPackage looks for path in the current directory, and any go source dirs,
// and returns the resolved path or an empty string if not found.
func findPackage(path string) string {
	// check relative to current directory first
	if stat, err := os.Stat(path); err == nil && stat.IsDir() {
		if !filepath.IsAbs(path) {
			path = filepath.Join(getCwd(), path)
		}
		return path
	}

	// check GOROOT / GOPATH
	for _, srcDir := range build.Default.SrcDirs() {
		pkg, err := build.Default.Import(path, srcDir, build.FindOnly)
		if err == nil {
			return pkg.Dir
		}
	}

	log.Println("package not found:", path)
	return ""
}

func main() {
	for _, arg := range os.Args[1:] {
		if arg == "-h" || arg == "--help" {
			fmt.Printf(`Monitors the file system and automatically runs 'go test' on changes.

usage: %s [-h | --help] [testflags] [path...] [package...]

options:
  -h, --help   print this message
  testflags    flags supported by 'go test'; see 'go help testflag'
  path...      filesystem path, monitored recursively
  package...   go package name for which 'go test' will be issued
`, os.Args[0])
			os.Exit(0)
		}
	}
	if os.Getenv("GOPATH") == "" {
		log.Fatalln("GOPATH is not set")
	}

	w, err := newWatcher()
	if err != nil {
		log.Fatal(err)
	}
	w.SettleTime = 500 * time.Millisecond

	// signals used to stop
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func() {
		//signal := <-signals
		//log.Println("got signal:", signal)
		<-signals
		w.Stop()
	}()

	// monitor paths
	gotOne := false
	for _, arg := range os.Args[1:] {
		if arg[0] == '-' {
			w.TestFlags = append(w.TestFlags, arg)
		} else if path := findPackage(arg); path != "" {
			if err := w.AddRecursive(path); err != nil {
				log.Fatal(err)
			} else {
				gotOne = true
			}
		}
	}

	if !gotOne {
		log.Fatalln("no paths to watch")
	}

	w.Start()
	w.RunTests()
	<-w.Finished
	w.Close()

	log.Println("exiting")
}
<|endoftext|>