<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 runner_test

import (
	"encoding/xml"
	"io/ioutil"
	"os"
	"os/exec"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/phst/runfiles"
)

func Test(t *testing.T) {
	bin, err := runfiles.Path("phst_rules_elisp/elisp/ert/test_test")
	if err != nil {
		t.Fatal(err)
	}
	runfilesEnv, err := runfiles.Env()
	if err != nil {
		t.Fatal(err)
	}
	reportFile, err := ioutil.TempFile(os.Getenv("TEST_TMPDIR"), "report-*.xml")
	if err != nil {
		t.Fatal(err)
	}
	reportName := reportFile.Name()
	// The test recreates the file in exclusive mode, so delete it now.
	if err := os.Remove(reportName); err != nil {
		t.Error(err)
	}
	if err := reportFile.Close(); err != nil {
		t.Error(err)
	}

	cmd := exec.Command(bin)
	// See
	// https://docs.bazel.build/versions/3.1.0/test-encyclopedia.html#initial-conditions.
	cmd.Env = append(os.Environ(), append(runfilesEnv, "XML_OUTPUT_FILE="+reportName)...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	switch err := cmd.Run().(type) {
	case nil:
		t.Error("test binary succeeded unexpectedly")
	case *exec.ExitError:
		if err.ExitCode() != 1 {
			t.Errorf("test binary: got exit code %d, want 1", err.ExitCode())
		}
	default:
		t.Error(err)
	}

	b, err := ioutil.ReadFile(reportName)
	if err != nil {
		t.Fatal(err)
	}
	type testCase struct {
		Name      string  `xml:"name,attr"`
		ClassName string  `xml:"classname,attr"`
		Status    string  `xml:"status,attr"`
		Time      float64 `xml:"time,attr"`
	}
	type report struct {
		XMLName   xml.Name
		Name      string     `xml:"name,attr"`
		Tests     int        `xml:"tests,attr"`
		Errors    int        `xml:"errors,attr"`
		Failures  int        `xml:"failures,attr"`
		Skipped   int        `xml:"skipped,attr"`
		Time      float64    `xml:"time,attr"`
		Timestamp timestamp  `xml:"timestamp,attr"`
		TestCases []testCase `xml:"testcase"`
	}
	var got report
	if err := xml.Unmarshal(b, &got); err != nil {
		t.Error(err)
	}
	// Margin for time comparisons.  One hour is excessive, but we only
	// care about catching obvious bugs here.
	const margin = time.Hour
	// This, together with the EquateApprox below, ensures that the elapsed
	// time is nonnegative and below the margin.
	wantElapsed := margin.Seconds() / 2
	want := report{
		XMLName:   xml.Name{"", "testsuite"},
		Name:      "ERT",
		Tests:     9,
		Errors:    0,
		Failures:  6,
		Skipped:   1,
		Time:      wantElapsed,
		Timestamp: timestamp(time.Now()),
		TestCases: []testCase{
			{Name: "abort", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "error", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "expect-failure", ClassName: "ERT", Status: "failed", Time: wantElapsed},
			{Name: "expect-failure-but-pass", ClassName: "ERT", Status: "PASSED", Time: wantElapsed},
			{Name: "fail", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "filter", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "pass", ClassName: "ERT", Status: "passed", Time: wantElapsed},
			{Name: "skip", ClassName: "ERT", Status: "skipped", Time: wantElapsed},
			{Name: "throw", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
		},
	}
	if diff := cmp.Diff(got, want, cmp.Transformer("time.Time", toTime), cmpopts.EquateApprox(0, wantElapsed), cmpopts.EquateApproxTime(margin)); diff != "" {
		t.Errorf("-got +want:\n%s", diff)
	}
}

type timestamp time.Time

func (t *timestamp) UnmarshalText(b []byte) error {
	// The XML report format doesn’t allow timezones in timestamps, so
	// time.Time.UnmarshalText doesn’t work.
	u, err := time.Parse("2006-01-02T15:04:05", string(b))
	*t = timestamp(u)
	return err
}

func toTime(t timestamp) time.Time { return time.Time(t) }
<commit_msg>test filtering<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 runner_test

import (
	"encoding/xml"
	"io/ioutil"
	"os"
	"os/exec"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/phst/runfiles"
)

func Test(t *testing.T) {
	bin, err := runfiles.Path("phst_rules_elisp/elisp/ert/test_test")
	if err != nil {
		t.Fatal(err)
	}
	runfilesEnv, err := runfiles.Env()
	if err != nil {
		t.Fatal(err)
	}
	reportFile, err := ioutil.TempFile(os.Getenv("TEST_TMPDIR"), "report-*.xml")
	if err != nil {
		t.Fatal(err)
	}
	reportName := reportFile.Name()
	// The test recreates the file in exclusive mode, so delete it now.
	if err := os.Remove(reportName); err != nil {
		t.Error(err)
	}
	if err := reportFile.Close(); err != nil {
		t.Error(err)
	}

	cmd := exec.Command(bin)
	// See
	// https://docs.bazel.build/versions/3.1.0/test-encyclopedia.html#initial-conditions.
	cmd.Env = append(os.Environ(), append(runfilesEnv, "XML_OUTPUT_FILE="+reportName, "TESTBRIDGE_TEST_ONLY=(not (tag skip))")...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	switch err := cmd.Run().(type) {
	case nil:
		t.Error("test binary succeeded unexpectedly")
	case *exec.ExitError:
		if err.ExitCode() != 1 {
			t.Errorf("test binary: got exit code %d, want 1", err.ExitCode())
		}
	default:
		t.Error(err)
	}

	b, err := ioutil.ReadFile(reportName)
	if err != nil {
		t.Fatal(err)
	}
	type testCase struct {
		Name      string  `xml:"name,attr"`
		ClassName string  `xml:"classname,attr"`
		Status    string  `xml:"status,attr"`
		Time      float64 `xml:"time,attr"`
	}
	type report struct {
		XMLName   xml.Name
		Name      string     `xml:"name,attr"`
		Tests     int        `xml:"tests,attr"`
		Errors    int        `xml:"errors,attr"`
		Failures  int        `xml:"failures,attr"`
		Skipped   int        `xml:"skipped,attr"`
		Time      float64    `xml:"time,attr"`
		Timestamp timestamp  `xml:"timestamp,attr"`
		TestCases []testCase `xml:"testcase"`
	}
	var got report
	if err := xml.Unmarshal(b, &got); err != nil {
		t.Error(err)
	}
	// Margin for time comparisons.  One hour is excessive, but we only
	// care about catching obvious bugs here.
	const margin = time.Hour
	// This, together with the EquateApprox below, ensures that the elapsed
	// time is nonnegative and below the margin.
	wantElapsed := margin.Seconds() / 2
	want := report{
		XMLName:   xml.Name{"", "testsuite"},
		Name:      "ERT",
		Tests:     8,
		Errors:    0,
		Failures:  5,
		Skipped:   1,
		Time:      wantElapsed,
		Timestamp: timestamp(time.Now()),
		TestCases: []testCase{
			{Name: "abort", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "error", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "expect-failure", ClassName: "ERT", Status: "failed", Time: wantElapsed},
			{Name: "expect-failure-but-pass", ClassName: "ERT", Status: "PASSED", Time: wantElapsed},
			{Name: "fail", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
			{Name: "pass", ClassName: "ERT", Status: "passed", Time: wantElapsed},
			{Name: "skip", ClassName: "ERT", Status: "skipped", Time: wantElapsed},
			{Name: "throw", ClassName: "ERT", Status: "FAILED", Time: wantElapsed},
		},
	}
	if diff := cmp.Diff(got, want, cmp.Transformer("time.Time", toTime), cmpopts.EquateApprox(0, wantElapsed), cmpopts.EquateApproxTime(margin)); diff != "" {
		t.Errorf("-got +want:\n%s", diff)
	}
}

type timestamp time.Time

func (t *timestamp) UnmarshalText(b []byte) error {
	// The XML report format doesn’t allow timezones in timestamps, so
	// time.Time.UnmarshalText doesn’t work.
	u, err := time.Parse("2006-01-02T15:04:05", string(b))
	*t = timestamp(u)
	return err
}

func toTime(t timestamp) time.Time { return time.Time(t) }
<|endoftext|>


<commit_before>// Package uasurfer provides fast and reliable abstraction
// of HTTP User-Agent strings. The BrowserProfile struct contains browser name
// (string), browser version (int), platform name (string), os name (string),
// os version (int), device type (string). The philosophy is to identify only
// technology that holds >1% market share, and to avoid expending resources
// and accuracy on guessing at esoteric UA strings.
// TODO: Go package names are usually short avoid underscore. Best to rename it to something like useragent
package uasurfer

//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go

import (
	"strings"
)

// The BrowserProfile type contains all the attributes parsed and inferred from the User-Agent string.
// type BrowserProfile struct {
// 	UA             string
// 	Browser        BrowserName
// 	BrowserVersion int
// 	Platform       Platform
// 	OS             OSName
// 	OSVersion      int
// 	DeviceType     DeviceType
// }

// DeviceType (int) returns a constant.
type DeviceType int

const (
	DeviceUnknown DeviceType = iota
	DeviceComputer
	DeviceTablet
	DevicePhone
	DeviceConsole
	DeviceWearable
	DeviceTV
)

// BrowserName (int) returns a constant.
type BrowserName int

const (
	BrowserUnknown BrowserName = iota
	BrowserChrome
	BrowserIE
	BrowserSafari
	BrowserFirefox
	BrowserAndroid
	BrowserOpera
	BrowserBlackberry
	BrowserUCBrowser
	BrowserSilk
	BrowserNokia
	BrowserSpotify
	BrowserBot
)

// OSName (int) returns a constant.
type OSName int

const (
	OSUnknown OSName = iota
	OSWindowsPhone
	OSWindows
	OSMacOSX
	OSiOS
	OSAndroid
	OSBlackberry
	OSChromeOS
	OSKindle
	OSWebOS
	OSLinux
	OSPlaystation
	OSXbox
	OSNintendo
	OSBot
)

// Platform (int) returns a constant.
type Platform int

const (
	PlatformUnknown Platform = iota
	PlatformWindows
	PlatformMac
	PlatformLinux
	PlatformiPad
	PlatformiPhone
	PlatformBlackberry
	PlatformWindowsPhone
	PlatformPlaystation
	PlatformXbox
	PlatformNintendo
	PlatformBot
)

// func (b *BrowserProfile) initialize() {
// 	b.UA = ""
// 	b.Browser.Name = BrowserUnknown
// 	b.Browser.Version = 0
// 	b.Platform = PlatformUnknown
// 	b.OS.Name = OSUnknown
// 	b.OS.Version = 0
// 	b.DeviceType = DeviceUnknown
// }

// Parse accepts a raw user agent (string) and returns the
// browser name (int), browser version
// (int), platform (int), OS name (int), OS version (int),
// device type (int), and raw user agent (string).
func Parse(ua string) (BrowserName, int, Platform, OSName, int, DeviceType, string) {
	ua = strings.ToLower(ua)

	platform, osName, osVersion := evalSystem(ua)
	browserName := evalBrowserName(ua)
	browserVersion := evalBrowserVersion(ua, browserName)
	deviceType := evalDevice(ua, osName, platform, browserName)

	return browserName, browserVersion, platform, osName, osVersion, deviceType, ua
}
<commit_msg>+ intl browser constants<commit_after>// Package uasurfer provides fast and reliable abstraction
// of HTTP User-Agent strings. The BrowserProfile struct contains browser name
// (string), browser version (int), platform name (string), os name (string),
// os version (int), device type (string). The philosophy is to identify only
// technology that holds >1% market share, and to avoid expending resources
// and accuracy on guessing at esoteric UA strings.
// TODO: Go package names are usually short avoid underscore. Best to rename it to something like useragent
package uasurfer

//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go

import (
	"strings"
)

// The BrowserProfile type contains all the attributes parsed and inferred from the User-Agent string.
// type BrowserProfile struct {
// 	UA             string
// 	Browser        BrowserName
// 	BrowserVersion int
// 	Platform       Platform
// 	OS             OSName
// 	OSVersion      int
// 	DeviceType     DeviceType
// }

// DeviceType (int) returns a constant.
type DeviceType int

const (
	DeviceUnknown DeviceType = iota
	DeviceComputer
	DeviceTablet
	DevicePhone
	DeviceConsole
	DeviceWearable
	DeviceTV
)

// BrowserName (int) returns a constant.
type BrowserName int

const (
	BrowserUnknown BrowserName = iota
	BrowserChrome
	BrowserIE
	BrowserSafari
	BrowserFirefox
	BrowserAndroid
	BrowserOpera
	BrowserBlackberry
	BrowserUCBrowser
	BrowserSilk
	BrowserNokia
	BrowserNetFront
	BrowserQQ
	BrowserMaxthon
	BrowserSogouExplorer
	BrowserSpotify
	BrowserBot
)

// OSName (int) returns a constant.
type OSName int

const (
	OSUnknown OSName = iota
	OSWindowsPhone
	OSWindows
	OSMacOSX
	OSiOS
	OSAndroid
	OSBlackberry
	OSChromeOS
	OSKindle
	OSWebOS
	OSLinux
	OSPlaystation
	OSXbox
	OSNintendo
	OSBot
)

// Platform (int) returns a constant.
type Platform int

const (
	PlatformUnknown Platform = iota
	PlatformWindows
	PlatformMac
	PlatformLinux
	PlatformiPad
	PlatformiPhone
	PlatformBlackberry
	PlatformWindowsPhone
	PlatformPlaystation
	PlatformXbox
	PlatformNintendo
	PlatformBot
)

// func (b *BrowserProfile) initialize() {
// 	b.UA = ""
// 	b.Browser.Name = BrowserUnknown
// 	b.Browser.Version = 0
// 	b.Platform = PlatformUnknown
// 	b.OS.Name = OSUnknown
// 	b.OS.Version = 0
// 	b.DeviceType = DeviceUnknown
// }

// Parse accepts a raw user agent (string) and returns the
// browser name (int), browser version
// (int), platform (int), OS name (int), OS version (int),
// device type (int), and raw user agent (string).
func Parse(ua string) (BrowserName, int, Platform, OSName, int, DeviceType, string) {
	ua = strings.ToLower(ua)

	platform, osName, osVersion := evalSystem(ua)
	browserName := evalBrowserName(ua)
	browserVersion := evalBrowserVersion(ua, browserName)
	deviceType := evalDevice(ua, osName, platform, browserName)

	return browserName, browserVersion, platform, osName, osVersion, deviceType, ua
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 example

import (
	"github.com/n3wscott/rigging"
	"testing"
	"time"
)

// SmokeTestImpl makes sure an DuckType goes ready.
func SmokeTestImpl(t *testing.T) {
	opts := []rigging.Option{}

	rig, err := rigging.NewInstall(opts, []string{"smoke"}, map[string]string{})
	if err != nil {
		t.Fatalf("failed to create rig, %s", err)
	}
	t.Logf("Created a new testing rig at namespace %s.", rig.Namespace())

	// Uninstall deferred.
	defer func() {
		if err := rig.Uninstall(); err != nil {
			t.Errorf("failed to uninstall, %s", err)
		}
	}()

	refs := rig.Objects()
	for _, r := range refs {
		_, err := rig.WaitForReadyOrDone(r, 45*time.Second)
		if err != nil {
			t.Fatalf("failed to wait for ready or done, %s", err)
		}
		// Pass!
	}
}
<commit_msg>[master] golang format tools (#57)<commit_after>/*
Copyright 2020 The Knative 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 example

import (
	"testing"
	"time"

	"github.com/n3wscott/rigging"
)

// SmokeTestImpl makes sure an DuckType goes ready.
func SmokeTestImpl(t *testing.T) {
	opts := []rigging.Option{}

	rig, err := rigging.NewInstall(opts, []string{"smoke"}, map[string]string{})
	if err != nil {
		t.Fatalf("failed to create rig, %s", err)
	}
	t.Logf("Created a new testing rig at namespace %s.", rig.Namespace())

	// Uninstall deferred.
	defer func() {
		if err := rig.Uninstall(); err != nil {
			t.Errorf("failed to uninstall, %s", err)
		}
	}()

	refs := rig.Objects()
	for _, r := range refs {
		_, err := rig.WaitForReadyOrDone(r, 45*time.Second)
		if err != nil {
			t.Fatalf("failed to wait for ready or done, %s", err)
		}
		// Pass!
	}
}
<|endoftext|>


<commit_before>package monitor

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"github.com/cloudfoundry-incubator/switchboard/domain"
	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . UrlGetter
type UrlGetter interface {
	Get(url string) (*http.Response, error)
}

func HttpUrlGetterProvider(healthcheckTimeout time.Duration) UrlGetter {
	return &http.Client{
		Timeout: healthcheckTimeout,
	}
}

var UrlGetterProvider = HttpUrlGetterProvider

type backendMonitor struct {
	backend  *domain.Backend
	healthy  bool
	counters *DecisionCounters
}

type Cluster struct {
	backends           []*domain.Backend
	logger             lager.Logger
	healthcheckTimeout time.Duration
	arpManager         ArpManager
	activeBackendChan  chan<- domain.IBackend
}

func NewCluster(backends []*domain.Backend, healthcheckTimeout time.Duration, logger lager.Logger, arpManager ArpManager, activeBackendChan chan<- domain.IBackend) *Cluster {
	return &Cluster{
		backends:           backends,
		logger:             logger,
		healthcheckTimeout: healthcheckTimeout,
		arpManager:         arpManager,
		activeBackendChan:  activeBackendChan,
	}
}

func (c *Cluster) Monitor(stopChan <-chan interface{}) {
	client := UrlGetterProvider(c.healthcheckTimeout)

	var backendHealth []*backendMonitor

	for _, backend := range c.backends {
		backendHealth = append(backendHealth,
			&backendMonitor{
				backend:  backend,
				counters: c.setupCounters(),
			})
	}

	go func() {
		var activeBackend domain.IBackend
		for {

			select {
			case <-time.After(c.healthcheckTimeout / 5):
				for _, healthMonitor := range backendHealth {
					backend := healthMonitor.backend

					healthMonitor.counters.IncrementCount("dial")
					shouldLog := healthMonitor.counters.Should("log")

					url := backend.HealthcheckUrl()
					resp, err := client.Get(url)

					if err == nil && resp.StatusCode == http.StatusOK {
						backend.SetHealthy()
						healthMonitor.healthy = true
						healthMonitor.counters.ResetCount("consecutiveUnhealthyChecks")

						if shouldLog {
							c.logger.Debug("Healthcheck succeeded", lager.Data{"endpoint": url})
						}
					} else {
						backend.SetUnhealthy()
						healthMonitor.healthy = false
						healthMonitor.counters.IncrementCount("consecutiveUnhealthyChecks")

						if shouldLog {
							c.logger.Error(
								"Healthcheck failed on backend",
								fmt.Errorf("Non-200 status code from healthcheck"),
								lager.Data{
									"backend":  backend.AsJSON(),
									"endpoint": url,
									"resp":     fmt.Sprintf("%#v", resp),
									"err":      err,
								},
							)
						}
					}

					if healthMonitor.counters.Should("clearArp") {
						backendHost := backend.AsJSON().Host

						if c.arpManager.IsCached(backendHost) {
							err = c.arpManager.ClearCache(backendHost)
							if err != nil {
								c.logger.Error("Failed to clear arp cache", err)
							}
						}
					}
				}

				var anyHealthy bool
				for _, healthMonitor := range backendHealth {
					backend := healthMonitor.backend

					if healthMonitor.healthy {
						anyHealthy = true
					}

					if healthMonitor.healthy && activeBackend == backend {
						break
					}

					if healthMonitor.healthy && activeBackend != backend {
						c.logger.Info("New active backend", lager.Data{"backend": backend.AsJSON()})
						activeBackend = backend
						c.activeBackendChan <- activeBackend
						break
					}
				}

				if !anyHealthy {
					c.logger.Info("No active backends.")
					c.activeBackendChan <- nil
				}

			case <-stopChan:
				return
			}

		}

	}()

}

func (c *Cluster) setupCounters() *DecisionCounters {
	counters := NewDecisionCounters()
	logFreq := uint64(5)
	clearArpFreq := uint64(5)

	//used to make logs less noisy
	counters.AddCondition("log", func() bool {
		return (counters.GetCount("dial") % logFreq) == 0
	})

	//only clear ARP cache after X consecutive unhealthy dials
	counters.AddCondition("clearArp", func() bool {
		// golang makes it difficult to tell whether the value of an interface is nil
		if reflect.ValueOf(c.arpManager).IsNil() {
			return false
		} else {
			checks := counters.GetCount("consecutiveUnhealthyChecks")
			return (checks > 0) && (checks%clearArpFreq) == 0
		}
	})

	return counters
}
<commit_msg>Parallelize the healthcheck<commit_after>package monitor

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"github.com/cloudfoundry-incubator/switchboard/domain"
	"github.com/pivotal-golang/lager"
	"sync"
)

//go:generate counterfeiter . UrlGetter
type UrlGetter interface {
	Get(url string) (*http.Response, error)
}

func HttpUrlGetterProvider(healthcheckTimeout time.Duration) UrlGetter {
	return &http.Client{
		Timeout: healthcheckTimeout,
	}
}

var UrlGetterProvider = HttpUrlGetterProvider

type backendMonitor struct {
	backend  *domain.Backend
	healthy  bool
	counters *DecisionCounters
}

type Cluster struct {
	backends           []*domain.Backend
	logger             lager.Logger
	healthcheckTimeout time.Duration
	arpManager         ArpManager
	activeBackendChan  chan<- domain.IBackend
}

func NewCluster(backends []*domain.Backend, healthcheckTimeout time.Duration, logger lager.Logger, arpManager ArpManager, activeBackendChan chan<- domain.IBackend) *Cluster {
	return &Cluster{
		backends:           backends,
		logger:             logger,
		healthcheckTimeout: healthcheckTimeout,
		arpManager:         arpManager,
		activeBackendChan:  activeBackendChan,
	}
}

func (c *Cluster) Monitor(stopChan <-chan interface{}) {
	client := UrlGetterProvider(c.healthcheckTimeout)

	var backendHealth []*backendMonitor

	for _, backend := range c.backends {
		backendHealth = append(backendHealth,
			&backendMonitor{
				backend:  backend,
				counters: c.setupCounters(),
			})
	}

	go func() {
		var activeBackend domain.IBackend

		for {

			select {
			case <-time.After(c.healthcheckTimeout / 5):
				var wg sync.WaitGroup

				for _, healthMonitor := range backendHealth {
					wg.Add(1)
					go func(healthMonitor *backendMonitor) {
						defer wg.Done()

						backend := healthMonitor.backend

						healthMonitor.counters.IncrementCount("dial")
						shouldLog := healthMonitor.counters.Should("log")

						url := backend.HealthcheckUrl()
						resp, err := client.Get(url)

						if err == nil && resp.StatusCode == http.StatusOK {
							backend.SetHealthy()
							healthMonitor.healthy = true
							healthMonitor.counters.ResetCount("consecutiveUnhealthyChecks")

							if shouldLog {
								c.logger.Debug("Healthcheck succeeded", lager.Data{"endpoint": url})
							}
						} else {
							backend.SetUnhealthy()
							healthMonitor.healthy = false
							healthMonitor.counters.IncrementCount("consecutiveUnhealthyChecks")

							if shouldLog {
								c.logger.Error(
									"Healthcheck failed on backend",
									fmt.Errorf("Non-200 status code from healthcheck"),
									lager.Data{
										"backend":  backend.AsJSON(),
										"endpoint": url,
										"resp":     fmt.Sprintf("%#v", resp),
										"err":      err,
									},
								)
							}
						}

						if healthMonitor.counters.Should("clearArp") {
							backendHost := backend.AsJSON().Host

							if c.arpManager.IsCached(backendHost) {
								err = c.arpManager.ClearCache(backendHost)
								if err != nil {
									c.logger.Error("Failed to clear arp cache", err)
								}
							}
						}
					}(healthMonitor)

				}

				wg.Wait()
				var anyHealthy bool
				for _, healthMonitor := range backendHealth {
					backend := healthMonitor.backend

					if healthMonitor.healthy {
						anyHealthy = true
					}

					if healthMonitor.healthy && activeBackend == backend {
						break
					}

					if healthMonitor.healthy && activeBackend != backend {
						c.logger.Info("New active backend", lager.Data{"backend": backend.AsJSON()})
						activeBackend = backend
						c.activeBackendChan <- activeBackend
						break
					}
				}

				if !anyHealthy {
					c.logger.Info("No active backends.")
					c.activeBackendChan <- nil
				}

			case <-stopChan:
				return
			}

		}

	}()

}

func (c *Cluster) setupCounters() *DecisionCounters {
	counters := NewDecisionCounters()
	logFreq := uint64(5)
	clearArpFreq := uint64(5)

	//used to make logs less noisy
	counters.AddCondition("log", func() bool {
		return (counters.GetCount("dial") % logFreq) == 0
	})

	//only clear ARP cache after X consecutive unhealthy dials
	counters.AddCondition("clearArp", func() bool {
		// golang makes it difficult to tell whether the value of an interface is nil
		if reflect.ValueOf(c.arpManager).IsNil() {
			return false
		} else {
			checks := counters.GetCount("consecutiveUnhealthyChecks")
			return (checks > 0) && (checks%clearArpFreq) == 0
		}
	})

	return counters
}
<|endoftext|>


<commit_before>package prototime

import (
	"time"

	"go.pedge.io/google-protobuf"
)

// TimeToTimestamp converts a go Time to a protobuf Timestamp.
func TimeToTimestamp(t time.Time) *google_protobuf.Timestamp {
	return &google_protobuf.Timestamp{
		Seconds: t.UnixNano() / int64(time.Second),
		Nanos:   int32(t.UnixNano() % int64(time.Second)),
	}
}

// TimestampToTime converts a protobuf Timestamp to a go Time.
func TimestampToTime(timestamp *google_protobuf.Timestamp) time.Time {
	return time.Unix(
		timestamp.Seconds,
		int64(timestamp.Nanos),
	).UTC()
}

// TimestampLess returns true if i is before j.
func TimestampLess(i *google_protobuf.Timestamp, j *google_protobuf.Timestamp) bool {
	if i == nil {
		return true
	}
	if j == nil {
		return false
	}
	if i.Seconds < j.Seconds {
		return true
	}
	if i.Seconds > j.Seconds {
		return false
	}
	return i.Nanos < j.Nanos
}

// DurationToProto converts a go Duration to a protobuf Duration.
func DurationToProto(d time.Duration) *google_protobuf.Duration {
	return &google_protobuf.Duration{
		Seconds: int64(d) / int64(time.Second),
		Nanos:   int32(int64(d) % int64(time.Second)),
	}
}

// DurationFromProto converts a protobuf Duration to a go Duration.
func DurationFromProto(duration *google_protobuf.Duration) time.Duration {
	return time.Duration((duration.Seconds * int64(time.Second)) + int64(duration.Nanos))
}
<commit_msg>update vendor<commit_after>package prototime

import (
	"time"

	"go.pedge.io/google-protobuf"
)

// TimeToTimestamp converts a go Time to a protobuf Timestamp.
func TimeToTimestamp(t time.Time) *google_protobuf.Timestamp {
	return &google_protobuf.Timestamp{
		Seconds: t.UnixNano() / int64(time.Second),
		Nanos:   int32(t.UnixNano() % int64(time.Second)),
	}
}

// TimestampToTime converts a protobuf Timestamp to a go Time.
func TimestampToTime(timestamp *google_protobuf.Timestamp) time.Time {
	if timestamp == nil {
		return time.Unix(0, 0).UTC()
	}
	return time.Unix(
		timestamp.Seconds,
		int64(timestamp.Nanos),
	).UTC()
}

// TimestampLess returns true if i is before j.
func TimestampLess(i *google_protobuf.Timestamp, j *google_protobuf.Timestamp) bool {
	if i == nil {
		return true
	}
	if j == nil {
		return false
	}
	if i.Seconds < j.Seconds {
		return true
	}
	if i.Seconds > j.Seconds {
		return false
	}
	return i.Nanos < j.Nanos
}

// DurationToProto converts a go Duration to a protobuf Duration.
func DurationToProto(d time.Duration) *google_protobuf.Duration {
	return &google_protobuf.Duration{
		Seconds: int64(d) / int64(time.Second),
		Nanos:   int32(int64(d) % int64(time.Second)),
	}
}

// DurationFromProto converts a protobuf Duration to a go Duration.
func DurationFromProto(duration *google_protobuf.Duration) time.Duration {
	if duration == nil {
		return 0
	}
	return time.Duration((duration.Seconds * int64(time.Second)) + int64(duration.Nanos))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/breunigs/frank/frank"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

const (
	//~ instaJoin = "#chaos-hd"
	instaJoin    = "#test"
	nickServPass = ""
	ircServer    = "irc.twice-irc.de"
	botNick      = "frank"
)

func main() {
	flag.Parse() // parses the logging flags. TODO

	cfg := irc.NewConfig(botNick, botNick, "Frank Böterrich der Zweite")
	cfg.SSL = true
	cfg.Server = ircServer
	cfg.NewNick = func(n string) string { return n + "_" }
	c := irc.Client(cfg)

	// connect
	c.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Connected as: %s\n", conn.Me().Nick)
			conn.Privmsg("nickserv", "identify "+nickServPass)
			for _, cn := range strings.Split(instaJoin, " ") {
				if cn != "" {
					conn.Join(cn)
				}
			}

			// handle RSS
			frank.Rss(conn)
		})

	// react
	c.HandleFunc("PRIVMSG",
		func(conn *irc.Conn, line *irc.Line) {
			//~ tgt := line.Args[0]
			//~ msg := line.Args[1]

			// ignore eicar, the bot we love to hate
			if line.Nick == "eicar" {
				return
			}

			go frank.RaumBang(conn, line)
			go frank.UriFind(conn, line)
			go frank.Lmgtfy(conn, line)
			go frank.Karma(conn, line)
			go frank.Help(conn, line)

			//~ log.Printf("      Debug: tgt: %s, msg: %s\n", tgt, msg)
		})

	// auto follow invites
	c.HandleFunc("INVITE",
		func(conn *irc.Conn, line *irc.Line) {
			tgt := line.Args[0]
			cnnl := line.Args[1]
			if conn.Me().Nick != tgt {
				log.Printf("WTF: received invite for %s but target was %s\n")
				return
			}

			log.Printf("Following invite for channel: %s\n", cnnl)
			conn.Join(cnnl)
		})

	// auto deop frank
	c.HandleFunc("MODE",
		func(conn *irc.Conn, line *irc.Line) {
			if len(line.Args) != 3 {
				// mode statement cannot be not in a channel, so ignore
				return
			}

			if line.Args[2] != conn.Me().Nick {
				// not referring to us
				return
			}

			if line.Args[1] != "+o" {
				// not relevant
				return
			}

			cn := line.Args[0]
			conn.Mode(cn, "+v", conn.Me().Nick)
			conn.Mode(cn, "-o", conn.Me().Nick)
			conn.Privmsg(cn, line.Nick+": SKYNET® Protection activated")
		})

	// disconnect
	quit := make(chan bool)
	c.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	// go go GO!
	if err := c.Connect(); err != nil {
		log.Printf("Connection error: %s\n", err)
	}

	log.Printf("Frank has booted\n")

	// Wait for disconnect
	<-quit
}
<commit_msg>add production variable toggle; don’t follow invites in production<commit_after>package main

import (
	"flag"
	"github.com/breunigs/frank/frank"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

const (
	production          = false
	instaJoinProduction = "#chaos-hd #i3 #noname-ev"
	instaJoinDebug      = "#test"
	nickServPass        = ""
	ircServer           = "irc.twice-irc.de"
	botNick             = "frank"
)

func main() {
	flag.Parse() // parses the logging flags. TODO

	cfg := irc.NewConfig(botNick, botNick, "Frank Böterrich der Zweite")
	cfg.SSL = true
	cfg.Server = ircServer
	cfg.NewNick = func(n string) string { return n + "_" }
	c := irc.Client(cfg)

	// connect
	c.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Connected as: %s\n", conn.Me().Nick)
			conn.Privmsg("nickserv", "identify "+nickServPass)

			var instaJoin string
			if production {
				instaJoin = instaJoinProduction
			} else {
				instaJoin = instaJoinDebug
			}

			log.Printf("AutoJoining: %s\n", instaJoin)

			for _, cn := range strings.Split(instaJoin, " ") {
				if cn != "" {
					conn.Join(cn)
				}
			}

			// handle RSS
			frank.Rss(conn)
		})

	// react
	c.HandleFunc("PRIVMSG",
		func(conn *irc.Conn, line *irc.Line) {
			//~ tgt := line.Args[0]
			//~ msg := line.Args[1]

			// ignore eicar, the bot we love to hate
			if line.Nick == "eicar" {
				return
			}

			go frank.RaumBang(conn, line)
			go frank.UriFind(conn, line)
			go frank.Lmgtfy(conn, line)
			go frank.Karma(conn, line)
			go frank.Help(conn, line)

			//~ log.Printf("      Debug: tgt: %s, msg: %s\n", tgt, msg)
		})

	// auto follow invites only in debug mode
	if !production {
		c.HandleFunc("INVITE",
			func(conn *irc.Conn, line *irc.Line) {
				tgt := line.Args[0]
				cnnl := line.Args[1]
				if conn.Me().Nick != tgt {
					log.Printf("WTF: received invite for %s but target was %s\n")
					return
				}

				log.Printf("Following invite for channel: %s\n", cnnl)
				conn.Join(cnnl)
			})
	}

	// auto deop frank
	c.HandleFunc("MODE",
		func(conn *irc.Conn, line *irc.Line) {
			if len(line.Args) != 3 {
				// mode statement cannot be not in a channel, so ignore
				return
			}

			if line.Args[2] != conn.Me().Nick {
				// not referring to us
				return
			}

			if line.Args[1] != "+o" {
				// not relevant
				return
			}

			cn := line.Args[0]
			conn.Mode(cn, "+v", conn.Me().Nick)
			conn.Mode(cn, "-o", conn.Me().Nick)
			conn.Privmsg(cn, line.Nick+": SKYNET® Protection activated")
		})

	// disconnect
	quit := make(chan bool)
	c.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	// go go GO!
	if err := c.Connect(); err != nil {
		log.Printf("Connection error: %s\n", err)
	}

	log.Printf("Frank has booted\n")

	// Wait for disconnect
	<-quit
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 unixchild

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

type UcAcceptError struct {
	Text string
}

func (err *UcAcceptError) Error() string {
	return err.Text
}

func NewUcAcceptError(text string) *UcAcceptError {
	return &UcAcceptError{
		Text: text,
	}
}

func IsUcAcceptError(err error) bool {
	_, ok := err.(*UcAcceptError)
	return ok
}

type Config struct {
	SockPath      string
	ChildPath     string
	ChildArgs     []string
	Depth         int
	MaxMsgSz      int
	AcceptTimeout time.Duration
	Restart       bool
}

type clientState uint32

const (
	CLIENT_STATE_STOPPED clientState = iota
	CLIENT_STATE_STARTING
	CLIENT_STATE_STARTED
	CLIENT_STATE_STOPPING
)

type Client struct {
	FromChild     chan []byte
	ToChild       chan []byte
	ErrChild      chan error
	childPath     string
	sockPath      string
	childArgs     []string
	maxMsgSz      int
	acceptTimeout time.Duration
	restart       bool
	stop          chan bool
	stopped       chan bool
	state         clientState
	stateMutex    sync.Mutex
}

func New(conf Config) *Client {
	c := &Client{
		childPath:     conf.ChildPath,
		sockPath:      conf.SockPath,
		childArgs:     conf.ChildArgs,
		maxMsgSz:      conf.MaxMsgSz,
		FromChild:     make(chan []byte, conf.Depth),
		ToChild:       make(chan []byte, conf.Depth),
		ErrChild:      make(chan error),
		acceptTimeout: conf.AcceptTimeout,
		restart:       conf.Restart,
		stop:          make(chan bool),
		stopped:       make(chan bool),
	}

	if c.maxMsgSz == 0 {
		c.maxMsgSz = 1024
	}

	return c
}

func (c *Client) getState() clientState {
	c.stateMutex.Lock()
	defer c.stateMutex.Unlock()

	return c.state
}

func (c *Client) setState(toState clientState) {
	c.stateMutex.Lock()
	defer c.stateMutex.Unlock()

	c.state = toState
}

func (c *Client) setStateIf(toState clientState,
	pred func(st clientState) bool) (bool, clientState) {

	c.stateMutex.Lock()
	defer c.stateMutex.Unlock()

	if pred(c.state) {
		c.state = toState
		return true, toState
	}

	return false, c.state

}

func (c *Client) setStateFrom(fromState clientState,
	toState clientState) (bool, clientState) {

	return c.setStateIf(toState,
		func(st clientState) bool { return st == fromState })
}

func (c *Client) startChild() (*exec.Cmd, error) {
	subProcess := exec.Command(c.childPath, c.childArgs...)

	stdin, err := subProcess.StdinPipe()
	if err != nil {
		return nil, err
	}
	stdin.Close()

	stdout, _ := subProcess.StdoutPipe()
	stderr, _ := subProcess.StderrPipe()

	if err = subProcess.Start(); err != nil {
		return nil, err
	}

	go func() {
		br := bufio.NewReader(stdout)
		for {
			s, err := br.ReadString('\n')
			if err != nil {
				return
			}
			log.Debugf("child stdout: %s", strings.TrimSuffix(s, "\n"))
		}
	}()

	go func() {
		br := bufio.NewReader(stderr)
		for {
			s, err := br.ReadString('\n')
			if err != nil {
				return
			}
			log.Debugf("child stderr: %s", strings.TrimSuffix(s, "\n"))
		}
	}()

	go subProcess.Wait() // reap dead children

	return subProcess, nil
}

func (c *Client) handleChild(con net.Conn) {
	var wg sync.WaitGroup

	bail := make(chan bool)

	fromDataPump := func() {
		defer wg.Done()
		for {
			var mlen uint16

			err := binary.Read(con, binary.BigEndian, &mlen)
			if err != nil {
				log.Debugln("fromDataPump error: ", err)
				bail <- true
				return
			}

			buf := make([]byte, mlen)
			_, err = io.ReadFull(con, buf)
			if err != nil {
				log.Debugln("fromDataPump error: ", err)
				bail <- true
				return
			}

			c.FromChild <- buf
		}
	}

	toDataPump := func() {
		defer wg.Done()
		for {
			select {
			case buf := <-c.ToChild:
				mlen := uint16(len(buf))
				err := binary.Write(con, binary.BigEndian, mlen)
				if err != nil {
					log.Debugln("toDataPump error: ", err)
					return
				}
				_, err = con.Write(buf)
				if err != nil {
					log.Debugln("toDataPump error: ", err)
					return
				}
			case <-bail:
				log.Debugln("toDataPump bail")
				return
			}
		}
	}

	wg.Add(1)
	go fromDataPump()
	wg.Add(1)
	go toDataPump()
	wg.Wait()
}

func (c *Client) Stop() {
	ok, _ := c.setStateIf(CLIENT_STATE_STOPPING,
		func(st clientState) bool {
			return st != CLIENT_STATE_STOPPING
		})
	if !ok {
		return
	}

	log.Debugf("Stopping client")

	c.stop <- true

	select {
	case <-c.stopped:
		log.Debugf("Stopped client")
		return
	}
}

func (c *Client) acceptDeadline() *time.Time {
	if c.acceptTimeout == 0 {
		return nil
	}

	t := time.Now().Add(c.acceptTimeout)
	return &t
}

func (c *Client) Start() error {
	ok, state := c.setStateFrom(CLIENT_STATE_STOPPED, CLIENT_STATE_STARTING)
	if !ok {
		return fmt.Errorf("client in invalid state for stating: %d", state)
	}

	l, err := net.Listen("unix", c.sockPath)
	if err != nil {
		c.setState(CLIENT_STATE_STOPPED)
		return err
	}

	var cmd *exec.Cmd

	go func() {
		for {
			var err error
			cmd, err = c.startChild()
			if err != nil {
				log.Debugf("unixchild start error: %s", err.Error())
				c.ErrChild <- fmt.Errorf("Child start error: %s", err.Error())
			} else {
				if t := c.acceptDeadline(); t != nil {
					l.(*net.UnixListener).SetDeadline(*t)
				}
				fd, err := l.Accept()
				if err != nil {
					text := fmt.Sprintf("unixchild accept error: %s",
						err.Error())
					c.ErrChild <- NewUcAcceptError(text)
				} else {
					c.setState(CLIENT_STATE_STARTED)
					c.handleChild(fd)
					c.ErrChild <- fmt.Errorf("Child exited")
				}
			}
			if c.getState() == CLIENT_STATE_STOPPING {
				log.Debugf("unixchild exit loop")
				return
			}
			time.Sleep(time.Second)
		}
	}()

	go func() {
		select {
		case <-c.stop:
			if c.getState() == CLIENT_STATE_STARTED {
				l.Close()
			}
			if cmd != nil {
				cmd.Process.Kill()
			}
			log.Debugf("deleting socket")
			os.Remove(c.sockPath)
			c.stopped <- true
		}
	}()

	return nil
}
<commit_msg>unixchild - prevent half-started state<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 unixchild

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

type UcAcceptError struct {
	Text string
}

func (err *UcAcceptError) Error() string {
	return err.Text
}

func NewUcAcceptError(text string) *UcAcceptError {
	return &UcAcceptError{
		Text: text,
	}
}

func IsUcAcceptError(err error) bool {
	_, ok := err.(*UcAcceptError)
	return ok
}

type Config struct {
	SockPath      string
	ChildPath     string
	ChildArgs     []string
	Depth         int
	MaxMsgSz      int
	AcceptTimeout time.Duration
}

type clientState uint32

const (
	CLIENT_STATE_STOPPED clientState = iota
	CLIENT_STATE_STARTED
	CLIENT_STATE_STOPPING
)

type Client struct {
	FromChild     chan []byte
	ToChild       chan []byte
	ErrChild      chan error
	childPath     string
	sockPath      string
	childArgs     []string
	maxMsgSz      int
	acceptTimeout time.Duration
	stop          chan bool
	stopped       chan bool
	state         clientState
}

func New(conf Config) *Client {
	c := &Client{
		childPath:     conf.ChildPath,
		sockPath:      conf.SockPath,
		childArgs:     conf.ChildArgs,
		maxMsgSz:      conf.MaxMsgSz,
		FromChild:     make(chan []byte, conf.Depth),
		ToChild:       make(chan []byte, conf.Depth),
		ErrChild:      make(chan error),
		acceptTimeout: conf.AcceptTimeout,
		stop:          make(chan bool),
		stopped:       make(chan bool),
	}

	if c.maxMsgSz == 0 {
		c.maxMsgSz = 1024
	}

	return c
}

func (c *Client) startChild() (*exec.Cmd, error) {
	subProcess := exec.Command(c.childPath, c.childArgs...)

	stdin, err := subProcess.StdinPipe()
	if err != nil {
		return nil, err
	}
	stdin.Close()

	stdout, _ := subProcess.StdoutPipe()
	stderr, _ := subProcess.StderrPipe()

	if err = subProcess.Start(); err != nil {
		return nil, err
	}

	go func() {
		br := bufio.NewReader(stdout)
		for {
			s, err := br.ReadString('\n')
			if err != nil {
				return
			}
			log.Debugf("child stdout: %s", strings.TrimSuffix(s, "\n"))
		}
	}()

	go func() {
		br := bufio.NewReader(stderr)
		for {
			s, err := br.ReadString('\n')
			if err != nil {
				return
			}
			log.Debugf("child stderr: %s", strings.TrimSuffix(s, "\n"))
		}
	}()

	go subProcess.Wait() // reap dead children

	return subProcess, nil
}

func (c *Client) handleChild(con net.Conn) {
	var wg sync.WaitGroup

	bail := make(chan bool)

	fromDataPump := func() {
		defer wg.Done()
		for {
			var mlen uint16

			err := binary.Read(con, binary.BigEndian, &mlen)
			if err != nil {
				log.Debugln("fromDataPump error: ", err)
				bail <- true
				return
			}

			buf := make([]byte, mlen)
			_, err = io.ReadFull(con, buf)
			if err != nil {
				log.Debugln("fromDataPump error: ", err)
				bail <- true
				return
			}

			c.FromChild <- buf
		}
	}

	toDataPump := func() {
		defer wg.Done()
		for {
			select {
			case buf := <-c.ToChild:
				mlen := uint16(len(buf))
				err := binary.Write(con, binary.BigEndian, mlen)
				if err != nil {
					log.Debugln("toDataPump error: ", err)
					return
				}
				_, err = con.Write(buf)
				if err != nil {
					log.Debugln("toDataPump error: ", err)
					return
				}
			case <-bail:
				log.Debugln("toDataPump bail")
				return
			}
		}
	}

	wg.Add(1)
	go fromDataPump()
	wg.Add(1)
	go toDataPump()
	wg.Wait()
}

func (c *Client) Stop() {
	if c.state != CLIENT_STATE_STARTED {
		return
	}

	c.state = CLIENT_STATE_STOPPING
	log.Debugf("Stopping client")

	c.stop <- true

	select {
	case <-c.stopped:
		c.state = CLIENT_STATE_STOPPED
		log.Debugf("Stopped client")
		return
	}
}

func (c *Client) acceptDeadline() *time.Time {
	if c.acceptTimeout == 0 {
		return nil
	}

	t := time.Now().Add(c.acceptTimeout)
	return &t
}

func (c *Client) deleteSocket() {
	log.Debugf("deleting socket")
	os.Remove(c.sockPath)
}

func (c *Client) Start() error {
	if c.state != CLIENT_STATE_STOPPED {
		return fmt.Errorf("Attempt to start unixchild twice")
	}

	l, err := net.Listen("unix", c.sockPath)
	if err != nil {
		return err
	}

	cmd, err := c.startChild()
	if err != nil {
		err = fmt.Errorf("unixchild start error: %s", err.Error())
		log.Debugf("%s", err.Error())
		c.deleteSocket()
		return err
	}

	if t := c.acceptDeadline(); t != nil {
		l.(*net.UnixListener).SetDeadline(*t)
	}
	fd, err := l.Accept()
	if err != nil {
		err = NewUcAcceptError(fmt.Sprintf("unixchild accept error: %s",
			err.Error()))
		c.deleteSocket()
		return err
	}

	c.state = CLIENT_STATE_STARTED

	go func() {
		c.handleChild(fd)
		c.Stop()
		c.ErrChild <- fmt.Errorf("child process terminated")
	}()

	go func() {
		<-c.stop
		l.Close()
		if cmd != nil {
			cmd.Process.Kill()
		}
		c.deleteSocket()
		c.stopped <- true
	}()

	return nil
}
<|endoftext|>


<commit_before>package linode

import (
	"fmt"
	"strconv"

	"github.com/chiefy/linodego"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceLinodeNodeBalancerConfig() *schema.Resource {
	return &schema.Resource{
		Create: resourceLinodeNodeBalancerConfigCreate,
		Read:   resourceLinodeNodeBalancerConfigRead,
		Update: resourceLinodeNodeBalancerConfigUpdate,
		Delete: resourceLinodeNodeBalancerConfigDelete,
		Exists: resourceLinodeNodeBalancerConfigExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"nodebalancer_id": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The ID of the NodeBalancer to access.",
				Required:    true,
				ForceNew:    true,
			},
			"protocol": &schema.Schema{
				Type:         schema.TypeString,
				Description:  "The protocol this port is configured to serve. If this is set to https you must include an ssl_cert and an ssl_key.",
				Required:     true,
				InputDefault: "us-east",
				Default:      linodego.ProtocolHTTP,
			},
			"port": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The TCP port this Config is for. These values must be unique across configs on a single NodeBalancer (you can't have two configs for port 80, for example). While some ports imply some protocols, no enforcement is done and you may configure your NodeBalancer however is useful to you. For example, while port 443 is generally used for HTTPS, you do not need SSL configured to have a NodeBalancer listening on port 443.",
				Optional:    true,
				Default:     80,
			},
			"check_interval": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How often, in seconds, to check that backends are up and serving requests.",
				Optional:    true,
				Default:     0,
			},
			"check_timeout": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How long, in seconds, to wait for a check attempt before considering it failed.",
				Optional:    true,
				Default:     0,
			},
			"check_attempts": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How many times to attempt a check before considering a backend to be down.",
				Optional:    true,
				Default:     0,
			},
			"algorithm": &schema.Schema{
				Type:        schema.TypeString,
				Description: "What algorithm this NodeBalancer should use for routing traffic to backends: roundrobin, leastconn, source",
				Optional:    true,
				Default:     linodego.AlgorithmRoundRobin,
			},
			"stickiness": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Controls how session stickiness is handled on this port: 'none', 'table', 'http_cookie'",
				Optional:    true,
				Default:     linodego.StickinessNone,
			},
			"check": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The type of check to perform against backends to ensure they are serving requests. This is used to determine if backends are up or down. If none no check is performed. connection requires only a connection to the backend to succeed. http and http_body rely on the backend serving HTTP, and that the response returned matches what is expected.",
				Optional:    true,
				Default:     linodego.CheckHTTP,
			},
			"check_path": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The URL path to check on each backend. If the backend does not respond to this request it is considered to be down.",
				Optional:    true,
			},
			"check_body": &schema.Schema{
				Type:        schema.TypeString,
				Description: "This value must be present in the response body of the check in order for it to pass. If this value is not present in the response body of a check request, the backend is considered to be down",
				Optional:    true,
			},
			"check_passive": &schema.Schema{
				Type:        schema.TypeBool,
				Description: "If true, any response from this backend with a 5xx status code will be enough for it to be considered unhealthy and taken out of rotation.",
				Optional:    true,
				Default:     true,
			},
			"cipher_suite": &schema.Schema{
				Type:        schema.TypeString,
				Description: "What ciphers to use for SSL connections served by this NodeBalancer. legacy is considered insecure and should only be used if necessary.",
				Optional:    true,
				Default:     linodego.CipherRecommended,
			},
			"ssl_commonname": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The common name for the SSL certification this port is serving if this port is not configured to use SSL.",
				Computed:    true,
			},
			"ssl_fingerprint": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The fingerprint for the SSL certification this port is serving if this port is not configured to use SSL.",
				Computed:    true,
			},
			"ssl_cert": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The certificate this port is serving. This is not returned. If set, this field will come back as `<REDACTED>`. Please use the ssl_commonname and ssl_fingerprint to identify the certificate.",
				Optional:    true,
			},
			"ssl_key": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The private key corresponding to this port's certificate. This is not returned. If set, this field will come back as `<REDACTED>`. Please use the ssl_commonname and ssl_fingerprint to identify the certificate.",
				Optional:    true,
				Sensitive:   true,
			},
		},
	}
}

func resourceLinodeNodeBalancerConfigExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	_, err = client.GetNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return false, fmt.Errorf("Failed to get Linode NodeBalancerConfig ID %s because %s", d.Id(), err)
	}
	return true, nil
}

func syncConfigResourceData(d *schema.ResourceData, config *linodego.NodeBalancerConfig) {
	d.Set("algorithm", config.Algorithm)
	d.Set("check", config.Check)
	d.Set("check_attempts", config.CheckAttempts)
	d.Set("check_body", config.CheckBody)
	d.Set("check_interval", config.CheckInterval)
	d.Set("check_passive", config.CheckPassive)
	d.Set("check_path", config.CheckPath)
	d.Set("port", config.Port)
	d.Set("protocol", config.Protocol)
	d.Set("ssl_fingerprint", config.SSLFingerprint)
	d.Set("ssl_commonname", config.SSLCommonName)
}

func resourceLinodeNodeBalancerConfigRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	nodebalancer, err := client.GetNodeBalancerConfig(int(nodebalancerID), int(id))

	if err != nil {
		return fmt.Errorf("Failed to find the specified Linode NodeBalancerConfig because %s", err)
	}

	syncConfigResourceData(d, nodebalancer)

	return nil
}

func resourceLinodeNodeBalancerConfigCreate(d *schema.ResourceData, meta interface{}) error {
	client, ok := meta.(linodego.Client)
	if !ok {
		return fmt.Errorf("Invalid Client when creating Linode NodeBalancerConfig")
	}

	createOpts := linodego.NodeBalancerConfigCreateOptions{
		NodeBalancerID: d.Get("nodebalancer_id").(int),
		Algorithm:      d.Get("algorithm").(linodego.ConfigAlgorithm),
		Check:          d.Get("check").(linodego.ConfigCheck),
		CheckAttempts:  d.Get("check_attempts").(int),
		CheckBody:      d.Get("check_body").(string),
		CheckInterval:  d.Get("check_interval").(int),
		CheckPassive:   d.Get("check_passive").(bool),
		CheckPath:      d.Get("check_path").(string),
		Port:           d.Get("port").(int),
		Protocol:       d.Get("protocol").(linodego.ConfigProtocol),
		SSLCert:        d.Get("ssl_cert").(string),
		SSLKey:         d.Get("ssl_key").(string),
	}

	config, err := client.CreateNodeBalancerConfig(&createOpts)
	if err != nil {
		return fmt.Errorf("Failed to create a Linode NodeBalancerConfig in because %s", err)
	}
	d.SetId(fmt.Sprintf("%d", config.ID))

	syncConfigResourceData(d, config)

	return nil
}

func resourceLinodeNodeBalancerConfigUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	config, err := client.GetNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return fmt.Errorf("Failed to fetch data about the current NodeBalancerConfig because %s", err)
	}

	updateOpts := linodego.NodeBalancerConfigUpdateOptions{
		NodeBalancerID: int(nodebalancerID),
		Algorithm:      d.Get("algorithm").(linodego.ConfigAlgorithm),
		Check:          d.Get("check").(linodego.ConfigCheck),
		CheckAttempts:  d.Get("check_attempts").(int),
		CheckBody:      d.Get("check_body").(string),
		CheckInterval:  d.Get("check_interval").(int),
		CheckPassive:   d.Get("check_passive").(bool),
		CheckPath:      d.Get("check_path").(string),
		Port:           d.Get("port").(int),
		Protocol:       d.Get("protocol").(linodego.ConfigProtocol),
		SSLCert:        d.Get("ssl_cert").(string),
		SSLKey:         d.Get("ssl_key").(string),
	}

	if config, err = client.UpdateNodeBalancerConfig(int(nodebalancerID), int(id), updateOpts); err != nil {
		return err
	}
	syncConfigResourceData(d, config)

	return nil
}

func resourceLinodeNodeBalancerConfigDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}
	err = client.DeleteNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return fmt.Errorf("Failed to delete Linode NodeBalancerConfig %d because %s", id, err)
	}
	return nil
}
<commit_msg>NodeBalancerID is not a param for NBConfig create/update<commit_after>package linode

import (
	"fmt"
	"strconv"

	"github.com/chiefy/linodego"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceLinodeNodeBalancerConfig() *schema.Resource {
	return &schema.Resource{
		Create: resourceLinodeNodeBalancerConfigCreate,
		Read:   resourceLinodeNodeBalancerConfigRead,
		Update: resourceLinodeNodeBalancerConfigUpdate,
		Delete: resourceLinodeNodeBalancerConfigDelete,
		Exists: resourceLinodeNodeBalancerConfigExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"nodebalancer_id": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The ID of the NodeBalancer to access.",
				Required:    true,
				ForceNew:    true,
			},
			"protocol": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The protocol this port is configured to serve. If this is set to https you must include an ssl_cert and an ssl_key.",
				Optional:    true,
				Default:     linodego.ProtocolHTTP,
			},
			"port": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The TCP port this Config is for. These values must be unique across configs on a single NodeBalancer (you can't have two configs for port 80, for example). While some ports imply some protocols, no enforcement is done and you may configure your NodeBalancer however is useful to you. For example, while port 443 is generally used for HTTPS, you do not need SSL configured to have a NodeBalancer listening on port 443.",
				Optional:    true,
				Default:     80,
			},
			"check_interval": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How often, in seconds, to check that backends are up and serving requests.",
				Optional:    true,
				Default:     0,
			},
			"check_timeout": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How long, in seconds, to wait for a check attempt before considering it failed.",
				Optional:    true,
				Default:     0,
			},
			"check_attempts": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "How many times to attempt a check before considering a backend to be down.",
				Optional:    true,
				Default:     0,
			},
			"algorithm": &schema.Schema{
				Type:        schema.TypeString,
				Description: "What algorithm this NodeBalancer should use for routing traffic to backends: roundrobin, leastconn, source",
				Optional:    true,
				Default:     linodego.AlgorithmRoundRobin,
			},
			"stickiness": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Controls how session stickiness is handled on this port: 'none', 'table', 'http_cookie'",
				Optional:    true,
				Default:     linodego.StickinessNone,
			},
			"check": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The type of check to perform against backends to ensure they are serving requests. This is used to determine if backends are up or down. If none no check is performed. connection requires only a connection to the backend to succeed. http and http_body rely on the backend serving HTTP, and that the response returned matches what is expected.",
				Optional:    true,
				Default:     linodego.CheckHTTP,
			},
			"check_path": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The URL path to check on each backend. If the backend does not respond to this request it is considered to be down.",
				Optional:    true,
			},
			"check_body": &schema.Schema{
				Type:        schema.TypeString,
				Description: "This value must be present in the response body of the check in order for it to pass. If this value is not present in the response body of a check request, the backend is considered to be down",
				Optional:    true,
			},
			"check_passive": &schema.Schema{
				Type:        schema.TypeBool,
				Description: "If true, any response from this backend with a 5xx status code will be enough for it to be considered unhealthy and taken out of rotation.",
				Optional:    true,
				Default:     true,
			},
			"cipher_suite": &schema.Schema{
				Type:        schema.TypeString,
				Description: "What ciphers to use for SSL connections served by this NodeBalancer. legacy is considered insecure and should only be used if necessary.",
				Optional:    true,
				Default:     linodego.CipherRecommended,
			},
			"ssl_commonname": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The common name for the SSL certification this port is serving if this port is not configured to use SSL.",
				Computed:    true,
			},
			"ssl_fingerprint": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The fingerprint for the SSL certification this port is serving if this port is not configured to use SSL.",
				Computed:    true,
			},
			"ssl_cert": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The certificate this port is serving. This is not returned. If set, this field will come back as `<REDACTED>`. Please use the ssl_commonname and ssl_fingerprint to identify the certificate.",
				Optional:    true,
			},
			"ssl_key": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The private key corresponding to this port's certificate. This is not returned. If set, this field will come back as `<REDACTED>`. Please use the ssl_commonname and ssl_fingerprint to identify the certificate.",
				Optional:    true,
				Sensitive:   true,
			},
		},
	}
}

func resourceLinodeNodeBalancerConfigExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	_, err = client.GetNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return false, fmt.Errorf("Failed to get Linode NodeBalancerConfig ID %s because %s", d.Id(), err)
	}
	return true, nil
}

func syncConfigResourceData(d *schema.ResourceData, config *linodego.NodeBalancerConfig) {
	d.Set("algorithm", config.Algorithm)
	d.Set("check", config.Check)
	d.Set("check_attempts", config.CheckAttempts)
	d.Set("check_body", config.CheckBody)
	d.Set("check_interval", config.CheckInterval)
	d.Set("check_passive", config.CheckPassive)
	d.Set("check_path", config.CheckPath)
	d.Set("port", config.Port)
	d.Set("protocol", config.Protocol)
	d.Set("ssl_fingerprint", config.SSLFingerprint)
	d.Set("ssl_commonname", config.SSLCommonName)
}

func resourceLinodeNodeBalancerConfigRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	nodebalancer, err := client.GetNodeBalancerConfig(int(nodebalancerID), int(id))

	if err != nil {
		return fmt.Errorf("Failed to find the specified Linode NodeBalancerConfig because %s", err)
	}

	syncConfigResourceData(d, nodebalancer)

	return nil
}

func resourceLinodeNodeBalancerConfigCreate(d *schema.ResourceData, meta interface{}) error {
	client, ok := meta.(linodego.Client)
	if !ok {
		return fmt.Errorf("Invalid Client when creating Linode NodeBalancerConfig")
	}

	nodebalancerID := d.Get("nodebalancer_id").(int)

	createOpts := linodego.NodeBalancerConfigCreateOptions{
		Algorithm:     linodego.ConfigAlgorithm(d.Get("algorithm").(string)),
		Check:         linodego.ConfigCheck(d.Get("check").(string)),
		CheckAttempts: d.Get("check_attempts").(int),
		CheckBody:     d.Get("check_body").(string),
		CheckInterval: d.Get("check_interval").(int),
		CheckPassive:  d.Get("check_passive").(bool),
		CheckPath:     d.Get("check_path").(string),
		Port:          d.Get("port").(int),
		Protocol:      linodego.ConfigProtocol(d.Get("protocol").(string)),
		SSLCert:       d.Get("ssl_cert").(string),
		SSLKey:        d.Get("ssl_key").(string),
	}

	config, err := client.CreateNodeBalancerConfig(nodebalancerID, &createOpts)
	if err != nil {
		return fmt.Errorf("Failed to create a Linode NodeBalancerConfig because %s", err)
	}
	d.SetId(fmt.Sprintf("%d", config.ID))

	syncConfigResourceData(d, config)

	return nil
}

func resourceLinodeNodeBalancerConfigUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}

	config, err := client.GetNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return fmt.Errorf("Failed to fetch data about the current NodeBalancerConfig because %s", err)
	}

	updateOpts := linodego.NodeBalancerConfigUpdateOptions{
		Algorithm:     linodego.ConfigAlgorithm(d.Get("algorithm").(string)),
		Check:         linodego.ConfigCheck(d.Get("check").(string)),
		CheckAttempts: d.Get("check_attempts").(int),
		CheckBody:     d.Get("check_body").(string),
		CheckInterval: d.Get("check_interval").(int),
		CheckPassive:  d.Get("check_passive").(bool),
		CheckPath:     d.Get("check_path").(string),
		Port:          d.Get("port").(int),
		Protocol:      linodego.ConfigProtocol(d.Get("protocol").(string)),
		SSLCert:       d.Get("ssl_cert").(string),
		SSLKey:        d.Get("ssl_key").(string),
	}

	if config, err = client.UpdateNodeBalancerConfig(int(nodebalancerID), int(id), updateOpts); err != nil {
		return err
	}
	syncConfigResourceData(d, config)

	return nil
}

func resourceLinodeNodeBalancerConfigDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancerConfig ID %s as int because %s", d.Id(), err)
	}
	nodebalancerID, err := strconv.ParseInt(d.Get("nodebalancer_id").(string), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode NodeBalancer ID %s as int because %s", d.Get("nodebalancer"), err)
	}
	err = client.DeleteNodeBalancerConfig(int(nodebalancerID), int(id))
	if err != nil {
		return fmt.Errorf("Failed to delete Linode NodeBalancerConfig %d because %s", id, err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package umbrella provides utility functions to maintain your Shade
// repository, such as cleaning up orphaned files and chunks.
package umbrella

import (
	"flag"
	"fmt"
	"time"

	"github.com/asjoyner/shade"
	"github.com/asjoyner/shade/drive"
	"github.com/asjoyner/shade/drive/encrypt"
	"github.com/golang/glog"
)

var (
	maxFilesDelete  = flag.Int("maxFilesDelete", 100, "A safety limit: the maxmium number of files to delete per run.")
	maxChunksDelete = flag.Int("maxChunksDelete", 100, "A safety limit: the maxmium number of files to delete per run.")
)

// FoundFile groups files with their associated sums
type FoundFile struct {
	file *shade.File
	sum  []byte
}

// FetchFiles uses the provided client to fetch all of the known files and
// sorts them into those which are inUse and those which are Obsolete.
func FetchFiles(client drive.Client) (inUseFiles, obsoleteFiles []FoundFile, err error) {
	filesByPath := make(map[string]FoundFile)
	obsoleteFiles = make([]FoundFile, 0)
	// ListFiles to retrieve all file objects
	files, err := client.ListFiles()
	if err != nil {
		return nil, nil, fmt.Errorf("%q ListFiles(): %s", client.GetConfig().Provider, err)
	}
	glog.Infof("Found %d file(s) via %s", len(files), client.GetConfig().Provider)
	for _, sha256sum := range files {
		// fetch the file
		f, err := client.GetFile(sha256sum)
		if err != nil {
			glog.Warningf("Failed to fetch file %x: %s  (skipping)", sha256sum, err)
			continue
		}
		file := &shade.File{}
		if err := file.FromJSON(f); err != nil {
			glog.Warningf("Could not unmarshal file %x: %v", sha256sum, err)
			continue
		}
		existing, ok := filesByPath[file.Filename]
		if !ok {
			filesByPath[file.Filename] = FoundFile{file, sha256sum}
			continue
		}

		if existing.file.ModifiedTime.After(file.ModifiedTime) {
			obsoleteFiles = append(obsoleteFiles, FoundFile{file, sha256sum})
			continue
		}
		filesByPath[file.Filename] = FoundFile{file, sha256sum}
		obsoleteFiles = append(obsoleteFiles, FoundFile{file, sha256sum})
	}
	inUseFiles = make([]FoundFile, 0, len(filesByPath))
	for _, ff := range filesByPath {
		inUseFiles = append(inUseFiles, ff)
	}
	return
}

// Cleanup attempts to remove obsolete files and unused chunks from persistent
// storage clients.
func Cleanup(client drive.Client) error {
	inUseFiles, obsoleteFiles, err := FetchFiles(client)
	if err != nil {
		return err
	}
	niu := len(inUseFiles)
	no := len(obsoleteFiles)
	if niu < no {
		err := fmt.Errorf("more files are obsolete (%d) than remain (%d); aborting", no, niu)
		glog.Warning(err.Error())
		return err
	}
	if no > *maxFilesDelete {
		err := fmt.Errorf("num obsolete files (%d) over safety threshold (%d)", no, maxFilesDelete)
		glog.Warning(err.Error())
		return err
	}
	for _, ff := range obsoleteFiles {
		glog.Infof("Releasing obsolete file: %x", ff.sum)
		client.ReleaseFile(ff.sum)
	}

	// Build the map of all the chunksInUse
	chunksInUse := make(map[string]struct{})
	for _, ff := range inUseFiles {
		for _, chunk := range ff.file.Chunks {
			chunksInUse[string(chunk.Sha256)] = struct{}{}
		}
		esums, err := encrypt.GetAllEncryptedSums(ff.file)
		if err != nil {
			for _, s := range esums {
				chunksInUse[string(s)] = struct{}{}
			}
		}
	}

	if err := cleanupUnusedFiles(client, chunksInUse); err != nil {
		return err
	}
	return nil
}

func cleanupUnusedFiles(client drive.Client, chunksInUse map[string]struct{}) error {
	var unusedChunks [][]byte
	lister := client.NewChunkLister()
	for lister.Next() {
		csum := lister.Sha256()
		if _, ok := chunksInUse[string(csum)]; !ok {
			unusedChunks = append(unusedChunks, csum)
		}
	}
	if err := lister.Err(); err != nil {
		return err
	}
	uc := len(unusedChunks)
	if uc <= *maxChunksDelete {
		err := fmt.Errorf("num unused chunks (%d) over safety threshold (%d)", uc, maxFilesDelete)
		glog.Warning(err.Error())
		return err
	}
	for _, csum := range unusedChunks {
		client.ReleaseChunk(csum)
	}
	return nil
}

// CleanupLoop calls Cleanup once per hour
func CleanupLoop(client drive.Client) {
	for {
		Cleanup(client)
		time.Sleep(1 * time.Hour)
	}
}
<commit_msg>Shorten var names, add some helpful debug logging<commit_after>// Package umbrella provides utility functions to maintain your Shade
// repository, such as cleaning up orphaned files and chunks.
package umbrella

import (
	"flag"
	"fmt"
	"time"

	"github.com/asjoyner/shade"
	"github.com/asjoyner/shade/drive"
	"github.com/asjoyner/shade/drive/encrypt"
	"github.com/golang/glog"
)

var (
	maxFilesDelete  = flag.Int("maxFilesDelete", 100, "A safety limit: the maxmium number of files to delete per run.")
	maxChunksDelete = flag.Int("maxChunksDelete", 100, "A safety limit: the maxmium number of files to delete per run.")
)

// FoundFile groups files with their associated sums
type FoundFile struct {
	file *shade.File
	sum  []byte
}

// FetchFiles uses the provided client to fetch all of the known files and
// sorts them into those which are inUse and those which are obsolete.
func FetchFiles(client drive.Client) (inUse, obsolete []FoundFile, err error) {
	filesByPath := make(map[string]FoundFile)
	obsolete = make([]FoundFile, 0)
	// ListFiles to retrieve all file objects
	files, err := client.ListFiles()
	if err != nil {
		return nil, nil, fmt.Errorf("%q ListFiles(): %s", client.GetConfig().Provider, err)
	}
	glog.Infof("Found %d file(s) via %s", len(files), client.GetConfig().Provider)
	for _, sha256sum := range files {
		// fetch the file
		f, err := client.GetFile(sha256sum)
		if err != nil {
			glog.Warningf("Failed to fetch file %x: %s  (skipping)", sha256sum, err)
			continue
		}
		file := &shade.File{}
		if err := file.FromJSON(f); err != nil {
			glog.Warningf("Could not unmarshal file %x: %v", sha256sum, err)
			continue
		}
		existing, ok := filesByPath[file.Filename]
		if !ok {
			glog.V(2).Infof("found new file: %x", sha256sum)
			filesByPath[file.Filename] = FoundFile{file, sha256sum}
			continue
		}

		if existing.file.ModifiedTime.After(file.ModifiedTime) {
			glog.V(2).Infof("obsolete file: %x (%d < %d)", sha256sum, file.ModifiedTime.Unix(), existing.file.ModifiedTime.Unix())
			obsolete = append(obsolete, FoundFile{file, sha256sum})
			continue
		}
		filesByPath[file.Filename] = FoundFile{file, sha256sum}
		glog.V(2).Infof("obsolete file: %x (%d < %d)", existing.sum, file.ModifiedTime.Unix(), existing.file.ModifiedTime.Unix())
		obsolete = append(obsolete, FoundFile{existing.file, existing.sum})
	}
	inUse = make([]FoundFile, 0, len(filesByPath))
	for _, ff := range filesByPath {
		inUse = append(inUse, ff)
	}
	return
}

// Cleanup attempts to remove obsolete files and unused chunks from persistent
// storage clients.
func Cleanup(client drive.Client) error {
	inUse, obsolete, err := FetchFiles(client)
	if err != nil {
		return err
	}
	niu := len(inUse)
	no := len(obsolete)
	if niu < no {
		err := fmt.Errorf("more files are obsolete (%d) than remain (%d); aborting", no, niu)
		glog.Warning(err.Error())
		return err
	}
	if no > *maxFilesDelete {
		err := fmt.Errorf("num obsolete files (%d) over safety threshold (%d)", no, maxFilesDelete)
		glog.Warning(err.Error())
		return err
	}
	for _, ff := range obsolete {
		glog.Infof("Releasing obsolete file: %x", ff.sum)
		client.ReleaseFile(ff.sum)
	}

	// Build the map of all the chunksInUse
	chunksInUse := make(map[string]struct{})
	for _, ff := range inUse {
		for _, chunk := range ff.file.Chunks {
			chunksInUse[string(chunk.Sha256)] = struct{}{}
		}
		esums, err := encrypt.GetAllEncryptedSums(ff.file)
		if err != nil {
			for _, s := range esums {
				chunksInUse[string(s)] = struct{}{}
			}
		}
	}

	if err := cleanupUnusedFiles(client, chunksInUse); err != nil {
		return err
	}
	return nil
}

func cleanupUnusedFiles(client drive.Client, chunksInUse map[string]struct{}) error {
	var unusedChunks [][]byte
	lister := client.NewChunkLister()
	for lister.Next() {
		csum := lister.Sha256()
		if _, ok := chunksInUse[string(csum)]; !ok {
			unusedChunks = append(unusedChunks, csum)
			continue
		}
		glog.V(2).Infof("chunk is in use: %x", csum)
	}
	if err := lister.Err(); err != nil {
		return err
	}
	uc := len(unusedChunks)
	if uc <= *maxChunksDelete {
		err := fmt.Errorf("num unused chunks (%d) over safety threshold (%d)", uc, maxFilesDelete)
		glog.Warning(err.Error())
		return err
	}
	for _, csum := range unusedChunks {
		glog.V(2).Infof("removing unreferenced chunk: %x", csum)
		client.ReleaseChunk(csum)
	}
	return nil
}

// CleanupLoop calls Cleanup once per hour
func CleanupLoop(client drive.Client) {
	for {
		Cleanup(client)
		time.Sleep(1 * time.Hour)
	}
}
<|endoftext|>


<commit_before>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type mtime time.Time

func (t *mtime) UnmarshalJSON(b []byte) error {
	layout := "2006-01-02 15:04"
	tmp, err := time.Parse(layout, strings.Trim(string(b), "\""))
	*t = mtime(tmp)
	return err
}

type Meta struct {
	Author string
	Date   mtime
	Title  string
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath, path) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.MarkdownBasic(b)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<commit_msg>sanitize rendered markdown<commit_after>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bm "github.com/microcosm-cc/bluemonday"
	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type mtime time.Time

func (t *mtime) UnmarshalJSON(b []byte) error {
	layout := "2006-01-02 15:04"
	tmp, err := time.Parse(layout, strings.Trim(string(b), "\""))
	*t = mtime(tmp)
	return err
}

type Meta struct {
	Author string
	Date   mtime
	Title  string
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath, path) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.MarkdownBasic(b)
		ret.html = bm.UGCPolicy().SanitizeBytes(ret.html)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c != nil && c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// package discordgo provides Discord binding for Go
package discordgo

import "fmt"

// DiscordGo Version, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.5.0"

/*
type Config struct {
	Debug bool
}
*/
/*
// possible future main struct for discord connection
type Discord struct {
	Debug    bool      // Set to true to enable debug logging
	Token    string    // authentication token
	User     User      // authenticated user info
	Guilds   []Guild   // Cached Guild info
	Channels []Channel // Cached Channel info
	API      api       // all api endpoint functions
	DataWS   dataWS    // data websocket connection
	VoiceWS  voiceWS   // voice websocket/udp connections
	VoiceUDP voiceUDP
}

type api struct {
	Session
}
type dataWS struct {
}

type voiceWS struct {
}

type voiceUDP struct {
}
*/
// NOTICE: This function should be considered unstable because I am still
// exploring the best way to implement my goals here.  So, it is more likely
// to change than than the low level API functions.
//
// New creates a new Discord session interface and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session. If you pass a token
// or username and password (in that order), then it will attempt to login to
// Discord and open a websocket connection.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Varibles used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 2 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord
	if pass == "" {
		s.Token = auth
	} else {
		s.Token, err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// TODO: Add code here to fetch authenticated user info like settings,
	// avatar, User ID, etc.  If fails, return error.

	// Open websocket connection
	err = s.Open()
	if err != nil {
		fmt.Println(err)
	}

	// Do websocket handshake.
	err = s.Handshake()
	if err != nil {
		fmt.Println(err)
	}

	// Listen for events.
	go s.Listen()

	return
}
<commit_msg>Up version to 0.6.0-alpha<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// package discordgo provides Discord binding for Go
package discordgo

import "fmt"

// DiscordGo Version, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.6.0-alpha"

/*
type Config struct {
	Debug bool
}
*/
/*
// possible future main struct for discord connection
type Discord struct {
	Debug    bool      // Set to true to enable debug logging
	Token    string    // authentication token
	User     User      // authenticated user info
	Guilds   []Guild   // Cached Guild info
	Channels []Channel // Cached Channel info
	API      api       // all api endpoint functions
	DataWS   dataWS    // data websocket connection
	VoiceWS  voiceWS   // voice websocket/udp connections
	VoiceUDP voiceUDP
}

type api struct {
	Session
}
type dataWS struct {
}

type voiceWS struct {
}

type voiceUDP struct {
}
*/
// NOTICE: This function should be considered unstable because I am still
// exploring the best way to implement my goals here.  So, it is more likely
// to change than than the low level API functions.
//
// New creates a new Discord session interface and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session. If you pass a token
// or username and password (in that order), then it will attempt to login to
// Discord and open a websocket connection.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Varibles used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 2 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord
	if pass == "" {
		s.Token = auth
	} else {
		s.Token, err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// TODO: Add code here to fetch authenticated user info like settings,
	// avatar, User ID, etc.  If fails, return error.

	// Open websocket connection
	err = s.Open()
	if err != nil {
		fmt.Println(err)
	}

	// Do websocket handshake.
	err = s.Handshake()
	if err != nil {
		fmt.Println(err)
	}

	// Listen for events.
	go s.Listen()

	return
}
<|endoftext|>


<commit_before><commit_msg>continued work on PlatinumRiftEp1<commit_after>package main

import (
	"fmt"
	"strings"
)

func main() {
	// playerCount: the amount of players (2 to 4)
	// myId: my player ID (0, 1, 2 or 3)
	// zoneCount: the amount of zones on the map
	// linkCount: the amount of links between all zones
	var playerCount, myID, zoneCount, linkCount int
	fmt.Scan(&playerCount, &myID, &zoneCount, &linkCount)
	zones := make(map[int]*zone, zoneCount)
	zonesByPlatinum := make(map[int][]int, 7)
	for i := 0; i < zoneCount; i++ {
		// zoneId: this zone's ID (between 0 and zoneCount-1)
		// platinumSource: the amount of Platinum this zone can provide per game turn
		var zoneID, platinumSource int
		fmt.Scan(&zoneID, &platinumSource)
		zones[zoneID] = &zone{id: zoneID, platinum: platinumSource}
		zonesByPlatinum[platinumSource] = append(zonesByPlatinum[platinumSource], zoneID)
	}
	for i := 0; i < linkCount; i++ {
		var zone1, zone2 int
		fmt.Scan(&zone1, &zone2)
		zones[zone1].links = append(zones[zone1].links, zone2)
		zones[zone2].links = append(zones[zone2].links, zone1)
	}
	// game loop
	for {
		// platinum: my available Platinum
		var myPlatinum int
		fmt.Scan(&myPlatinum)

		var neutralZoneCount int
		for i := 0; i < zoneCount; i++ {
			z := zones[i]
			var myPods int
			var enemyPods [3]int
			// zId: this zone's ID
			// ownerId: the player who owns this zone (-1 otherwise)
			// podsP0: player 0's PODs on this zone
			// podsP1: player 1's PODs on this zone
			// podsP2: player 2's PODs on this zone (always 0 for a two player game)
			// podsP3: player 3's PODs on this zone (always 0 for a two or three player game)
			var zID, ownerID, podsP0, podsP1, podsP2, podsP3 int
			fmt.Scan(&zID, &ownerID, &podsP0, &podsP1, &podsP2, &podsP3)
			switch myID {
			case -1:
				myPods, enemyPods = 0, [3]int{0, 0, 0}
				neutralZoneCount++
			case 0:
				myPods, enemyPods = podsP0, [3]int{podsP1, podsP2, podsP3}
			case 1:
				myPods, enemyPods = podsP1, [3]int{podsP0, podsP2, podsP3}
			case 2:
				myPods, enemyPods = podsP2, [3]int{podsP0, podsP1, podsP3}
			case 3:
				myPods, enemyPods = podsP3, [3]int{podsP0, podsP1, podsP2}
			}
			z.ownerID, z.myPods, z.enemyPods = ownerID, myPods, enemyPods
		}

		//		fmt.Fprintln(os.Stderr, zones)
		//		fmt.Fprintln(os.Stderr, myPlatinum)
		moves, buys := []string{}, []string{}
		podBudget := myPlatinum / podCost
		buys = claimNeutralZones(podBudget, zones, zonesByPlatinum, myID)
		buys = append(buys, reinforceOwnedZones(podBudget, zones, zonesByPlatinum, myID)...)

		moveCommand := strings.Join(moves, " ")
		fmt.Println(moveCommand)
		buyCommand := strings.Join(buys, " ")
		fmt.Println(buyCommand)

	}
}
func reinforceOwnedZones(podBudget int, zones map[int]*zone, zonesByPlatinum map[int][]int, myID int) (buys []string) {
	return
}
func claimNeutralZones(podBudget int, zones map[int]*zone, zonesByPlatinum map[int][]int, myID int) (buys []string) {
	for i := 6; i > -1; i-- {
		if podBudget < 1 {
			return
		}
		for _, zID := range zonesByPlatinum[i] {
			z := zones[zID]
			need := 1
			if z.ownerID == myID || z.ownerID != -1 {
				continue
			}
			if z.EnemyCount() > z.myPods { // TODO: if there are enemyPods, it's probably not neutral and you can't place pods directly on it
				need = z.EnemyCount() - z.myPods + 1
			}
			if podBudget > need { // can I afford to take this zone?
				buys = append(buys, fmt.Sprintf("%d %d", need, zID))
				podBudget = podBudget - need
			}
			if podBudget < 1 {
				return
			}
		}
	}
	return
}

type zone struct {
	id        int
	platinum  int
	links     []int
	ownerID   int
	myPods    int
	enemyPods [3]int
}

func (z *zone) EnemyCount() int {
	return z.enemyPods[0] + z.enemyPods[1] + z.enemyPods[2]
}
func (z *zone) String() string {
	return fmt.Sprintf("%d{i: %d, p: %d, e: %d}", z.ownerID, z.id, z.platinum, z.enemyPods[0]+z.enemyPods[1]+z.enemyPods[2])
}

const podCost = 20
<|endoftext|>