<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package git

import (
	"errors"
	"launchpad.net/gocheck"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"reflect"
	"testing"
)

func Test(t *testing.T) {
	gocheck.TestingT(t)
}

type S struct {
	repoPath string
}

var _ = gocheck.Suite(&S{})

func (s *S) SetUpSuite(c *gocheck.C) {
	tmpdir, err := filepath.EvalSymlinks(os.TempDir())
	c.Assert(err, gocheck.IsNil)
	s.repoPath = path.Join(tmpdir, "git")
	err = os.MkdirAll(s.repoPath, 0755)
	c.Assert(err, gocheck.IsNil)
	cmd := exec.Command("git", "init")
	cmd.Dir = s.repoPath
	err = cmd.Run()
	c.Assert(err, gocheck.IsNil)
	err = exec.Command("cp", "testdata/gitconfig", path.Join(s.repoPath, ".git", "config")).Run()
	c.Assert(err, gocheck.IsNil)
	subdir := path.Join(s.repoPath, "a", "b", "c")
	err = os.MkdirAll(subdir, 0755)
	c.Assert(err, gocheck.IsNil)
}

func (s *S) TearDownSuite(c *gocheck.C) {
	os.RemoveAll(s.repoPath)
}

func (s *S) TestDiscoverRepositoryPath(c *gocheck.C) {
	var data = []struct {
		path     string
		expected string
		err      error
	}{
		{
			path:     s.repoPath,
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b", "c"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b", "c", "d"),
			expected: "",
			err:      errors.New("Repository not found."),
		},
		{
			path:     path.Join(os.TempDir(), "aoshae8yahhh8ua", "doctor-jimmy"),
			expected: "",
			err:      errors.New("Repository not found."),
		},
	}
	for _, d := range data {
		got, err := DiscoverRepositoryPath(d.path)
		if got != d.expected {
			c.Errorf("DiscoverRepositoryPath(%q): Got %q. Want %q.", d.path, got, d.expected)
		}
		if err == nil && d.err != nil {
			c.Errorf("DiscoverRepositoryPath(%q): Expected non-nil error (%+v), got <nil>.", d.path, d.err)
		} else if err != nil && d.err != nil && d.err.Error() != err.Error() {
			c.Errorf("DiscoverRepositoryPath(%q): Expected error %v. Got %v.", d.path, d.err, err)
		}
	}
}

func (s *S) TestOpenRepository(c *gocheck.C) {
	var data = []struct {
		path     string
		expected *Repository
		err      error
	}{
		{
			path:     s.repoPath,
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git"),
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git") + "/",
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     "/",
			expected: nil,
			err:      errors.New("Repository not found."),
		},
	}
	for _, d := range data {
		repo, err := OpenRepository(d.path)
		if !reflect.DeepEqual(repo, d.expected) {
			c.Errorf("OpenRepository(%q): Got repository %+v. Want %+v.", d.path, repo, d.expected)
		}
		if d.err != nil && err == nil {
			c.Errorf("OpenRepository(%q): Expected non-nil error (%+v), got <nil>.", d.path, d.err)
		} else if d.err != nil && err != nil && d.err.Error() != err.Error() {
			c.Errorf("OpenRepository(%q): Expected error %v. Got %v.", d.path, d.err, err)
		}
	}
}

func (s *S) TestGetRemoteUrl(c *gocheck.C) {
	var data = []struct {
		name     string
		expected string
		err      error
	}{
		{"origin", "git@github.com:globocom/tsuru-django-sample.git", nil},
		{"tsuru", "git@tsuruhost.com:gopher.git", nil},
		{"wut", "", errors.New(`Remote "wut" not found.`)},
	}
	repo, err := OpenRepository(s.repoPath)
	c.Assert(err, gocheck.IsNil)
	for _, d := range data {
		got, err := repo.GetRemoteUrl(d.name)
		if got != d.expected {
			c.Errorf("GetRemoteUrl(%q): Want %q. Got %q.", d.name, d.expected, got)
		}
		if !reflect.DeepEqual(d.err, err) {
			c.Errorf("GetRemoteUrl(%q): Want error %q. Got %q.", d.name, d.err, err)
		}
	}
}

func BenchmarkGetRemoteUrl(b *testing.B) {
	tmpdir, err := filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		b.Fatal(err)
	}
	repoPath := path.Join(tmpdir, "git-bench")
	err = os.MkdirAll(repoPath, 0755)
	if err != nil {
		b.Fatal(err)
	}
	cmd := exec.Command("git", "init")
	cmd.Dir = repoPath
	err = cmd.Run()
	if err != nil {
		b.Fatal(err)
	}
	err = exec.Command("cp", "testdata/gitconfig", path.Join(repoPath, ".git", "config")).Run()
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		repo, _ := OpenRepository(repoPath)
		repo.GetRemoteUrl("origin")
		repo.GetRemoteUrl("tsuru")
	}
}
<commit_msg>git: fix some golint warnings<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package git

import (
	"errors"
	"launchpad.net/gocheck"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"reflect"
	"testing"
)

func Test(t *testing.T) {
	gocheck.TestingT(t)
}

type S struct {
	repoPath string
}

var _ = gocheck.Suite(&S{})

func (s *S) SetUpSuite(c *gocheck.C) {
	tmpdir, err := filepath.EvalSymlinks(os.TempDir())
	c.Assert(err, gocheck.IsNil)
	s.repoPath = path.Join(tmpdir, "git")
	err = os.MkdirAll(s.repoPath, 0755)
	c.Assert(err, gocheck.IsNil)
	cmd := exec.Command("git", "init")
	cmd.Dir = s.repoPath
	err = cmd.Run()
	c.Assert(err, gocheck.IsNil)
	err = exec.Command("cp", "testdata/gitconfig", path.Join(s.repoPath, ".git", "config")).Run()
	c.Assert(err, gocheck.IsNil)
	subdir := path.Join(s.repoPath, "a", "b", "c")
	err = os.MkdirAll(subdir, 0755)
	c.Assert(err, gocheck.IsNil)
}

func (s *S) TearDownSuite(c *gocheck.C) {
	os.RemoveAll(s.repoPath)
}

func (s *S) TestDiscoverRepositoryPath(c *gocheck.C) {
	var data = []struct {
		path     string
		expected string
		err      error
	}{
		{
			path:     s.repoPath,
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b", "c"),
			expected: path.Join(s.repoPath, ".git"),
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, "a", "b", "c", "d"),
			expected: "",
			err:      errors.New("Repository not found."),
		},
		{
			path:     path.Join(os.TempDir(), "aoshae8yahhh8ua", "doctor-jimmy"),
			expected: "",
			err:      errors.New("Repository not found."),
		},
	}
	for _, d := range data {
		got, err := DiscoverRepositoryPath(d.path)
		if got != d.expected {
			c.Errorf("DiscoverRepositoryPath(%q): Got %q. Want %q.", d.path, got, d.expected)
		}
		if err == nil && d.err != nil {
			c.Errorf("DiscoverRepositoryPath(%q): Expected non-nil error (%+v), got <nil>.", d.path, d.err)
		} else if err != nil && d.err != nil && d.err.Error() != err.Error() {
			c.Errorf("DiscoverRepositoryPath(%q): Expected error %v. Got %v.", d.path, d.err, err)
		}
	}
}

func (s *S) TestOpenRepository(c *gocheck.C) {
	var data = []struct {
		path     string
		expected *Repository
		err      error
	}{
		{
			path:     s.repoPath,
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git"),
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     path.Join(s.repoPath, ".git") + "/",
			expected: &Repository{path: path.Join(s.repoPath, ".git")},
			err:      nil,
		},
		{
			path:     "/",
			expected: nil,
			err:      errors.New("Repository not found."),
		},
	}
	for _, d := range data {
		repo, err := OpenRepository(d.path)
		if !reflect.DeepEqual(repo, d.expected) {
			c.Errorf("OpenRepository(%q): Got repository %+v. Want %+v.", d.path, repo, d.expected)
		}
		if d.err != nil && err == nil {
			c.Errorf("OpenRepository(%q): Expected non-nil error (%+v), got <nil>.", d.path, d.err)
		} else if d.err != nil && err != nil && d.err.Error() != err.Error() {
			c.Errorf("OpenRepository(%q): Expected error %v. Got %v.", d.path, d.err, err)
		}
	}
}

func (s *S) TestGetRemoteURL(c *gocheck.C) {
	var data = []struct {
		name     string
		expected string
		err      error
	}{
		{"origin", "git@github.com:globocom/tsuru-django-sample.git", nil},
		{"tsuru", "git@tsuruhost.com:gopher.git", nil},
		{"wut", "", errors.New(`Remote "wut" not found.`)},
	}
	repo, err := OpenRepository(s.repoPath)
	c.Assert(err, gocheck.IsNil)
	for _, d := range data {
		got, err := repo.GetRemoteUrl(d.name)
		if got != d.expected {
			c.Errorf("GetRemoteUrl(%q): Want %q. Got %q.", d.name, d.expected, got)
		}
		if !reflect.DeepEqual(d.err, err) {
			c.Errorf("GetRemoteUrl(%q): Want error %q. Got %q.", d.name, d.err, err)
		}
	}
}

func BenchmarkGetRemoteURL(b *testing.B) {
	tmpdir, err := filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		b.Fatal(err)
	}
	repoPath := path.Join(tmpdir, "git-bench")
	err = os.MkdirAll(repoPath, 0755)
	if err != nil {
		b.Fatal(err)
	}
	cmd := exec.Command("git", "init")
	cmd.Dir = repoPath
	err = cmd.Run()
	if err != nil {
		b.Fatal(err)
	}
	err = exec.Command("cp", "testdata/gitconfig", path.Join(repoPath, ".git", "config")).Run()
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		repo, _ := OpenRepository(repoPath)
		repo.GetRemoteUrl("origin")
		repo.GetRemoteUrl("tsuru")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/alecthomas/kingpin.v2"
	"sync"
	"time"
)

var (
	owner                     = kingpin.Arg("owner", "GitHub owner.").Required().String()
	repo                      = kingpin.Arg("repo", "GitHub repository").Required().String()
	base                      = kingpin.Arg("base", "Base tag/commit").Required().String()
	auth_token                = kingpin.Flag("token", "OAuth Token").Envar("GITHUB_TOKEN").String()
	since      *time.Time     = nil
	client     *github.Client = nil
)

func issueWorker(pages <-chan int, results chan<- github.Issue) {
	for page := range pages {
		issues, _, err := client.Issues.ListByRepo(
			context.Background(),
			*owner,
			*repo,
			&github.IssueListByRepoOptions{
				State:       "closed",
				Since:       *since,
				ListOptions: github.ListOptions{Page: page, PerPage: 100},
			})
		if err != nil {
			panic(err)
		}
		fmt.Print(".")
		for _, issue := range issues {
			results <- *issue
		}
	}
}

func commitWorker(pages <-chan int, results chan<- github.RepositoryCommit) {
	for page := range pages {
		commits, _, err := client.Repositories.ListCommits(
			context.Background(),
			*owner,
			*repo,
			&github.CommitsListOptions{
				SHA:         "master",
				Since:       *since,
				ListOptions: github.ListOptions{Page: page, PerPage: 100},
			})
		if err != nil {
			panic(err)
		}
		fmt.Print(".")
		for _, commit := range commits {
			results <- *commit
		}
	}
}

func main() {
	kingpin.Parse()

	fmt.Println("Getting repository data...")

	if *auth_token != "" {
		tc := oauth2.NewClient(
			context.Background(),
			oauth2.StaticTokenSource(
				&oauth2.Token{AccessToken: *auth_token},
			))
		client = github.NewClient(tc)
	} else {
		client = github.NewClient(nil)
	}

	base, _, err := client.Repositories.GetCommit(context.Background(), *owner, *repo, *base)
	if err != nil {
		panic(err)
	}
	since = base.Commit.Author.Date

	_, issueInfo, err := client.Issues.ListByRepo(
		context.Background(),
		*owner,
		*repo,
		&github.IssueListByRepoOptions{
			State:       "closed",
			Since:       *since,
			ListOptions: github.ListOptions{Page: 1, PerPage: 100},
		})
	if err != nil {
		panic(err)
	}
	_, commitInfo, err := client.Repositories.ListCommits(
		context.Background(),
		*owner,
		*repo,
		&github.CommitsListOptions{
			SHA:         "master",
			Since:       *since,
			ListOptions: github.ListOptions{Page: 1, PerPage: 100},
		})
	if err != nil {
		panic(err)
	}

	fmt.Printf("Fetching %d commit pages and %d issue pages...", commitInfo.LastPage, issueInfo.LastPage)
	// Enumerate commits
	commitPages := make(chan int, 100)
	commits := make(chan github.RepositoryCommit, 10000)
	commitWg := &sync.WaitGroup{}
	commitWg.Add(5)
	for w := 1; w <= 5; w++ {
		go func() {
			defer commitWg.Done()
			commitWorker(commitPages, commits)
		}()
	}
	for p := 1; p <= commitInfo.LastPage; p++ {
		commitPages <- p
	}
	close(commitPages)
	go func() {
		commitWg.Wait()
		close(commits)
	}()

	// Enumerate issues
	issuePages := make(chan int, 100)
	issues := make(chan github.Issue, 10000)
	issueWg := &sync.WaitGroup{}
	issueWg.Add(5)
	for w := 1; w <= 5; w++ {
		go func() {
			defer issueWg.Done()
			issueWorker(issuePages, issues)
		}()
	}
	for p := 1; p <= issueInfo.LastPage; p++ {
		issuePages <- p
	}
	close(issuePages)
	go func() {
		issueWg.Wait()
		close(issues)
	}()

	closedIssues := 0
	closedPRs := 0
	for issue := range issues {
		if issue.ClosedAt.After(*since) {
			if issue.PullRequestLinks != nil {
				closedPRs++
			} else {
				closedIssues++
			}
		}
	}

	contributors := make(map[string]bool)
	commitCount := 0
	for commit := range commits {
		contributors[*commit.Commit.Author.Name] = true
		commitCount += 1
	}
	days := int(time.Since(*since).Hours() / 24)

	fmt.Println("")
	fmt.Printf("Since the last release, the project has had %d commits by %d contributors, "+
		"resulting in %d closed issues and %d closed PRs, all of this in just over %d days.",
		commitCount, len(contributors), closedIssues, closedPRs, days)
	fmt.Println("")
}
<commit_msg>fix stats for small releases<commit_after>package main

import (
	"context"
	"fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/alecthomas/kingpin.v2"
	"sync"
	"time"
)

var (
	owner                     = kingpin.Arg("owner", "GitHub owner.").Required().String()
	repo                      = kingpin.Arg("repo", "GitHub repository").Required().String()
	base                      = kingpin.Arg("base", "Base tag/commit").Required().String()
	auth_token                = kingpin.Flag("token", "OAuth Token").Envar("GITHUB_TOKEN").String()
	since      *time.Time     = nil
	client     *github.Client = nil
)

func issueWorker(pages <-chan int, results chan<- github.Issue) {
	for page := range pages {
		issues, _, err := client.Issues.ListByRepo(
			context.Background(),
			*owner,
			*repo,
			&github.IssueListByRepoOptions{
				State:       "closed",
				Since:       *since,
				ListOptions: github.ListOptions{Page: page, PerPage: 100},
			})
		if err != nil {
			panic(err)
		}
		fmt.Print(".")
		for _, issue := range issues {
			results <- *issue
		}
	}
}

func commitWorker(pages <-chan int, results chan<- github.RepositoryCommit) {
	for page := range pages {
		commits, _, err := client.Repositories.ListCommits(
			context.Background(),
			*owner,
			*repo,
			&github.CommitsListOptions{
				SHA:         "master",
				Since:       *since,
				ListOptions: github.ListOptions{Page: page, PerPage: 100},
			})
		if err != nil {
			panic(err)
		}
		fmt.Print(".")
		for _, commit := range commits {
			results <- *commit
		}
	}
}

func main() {
	kingpin.Parse()

	fmt.Println("Getting repository data...")

	if *auth_token != "" {
		tc := oauth2.NewClient(
			context.Background(),
			oauth2.StaticTokenSource(
				&oauth2.Token{AccessToken: *auth_token},
			))
		client = github.NewClient(tc)
	} else {
		client = github.NewClient(nil)
	}

	base, _, err := client.Repositories.GetCommit(context.Background(), *owner, *repo, *base)
	if err != nil {
		panic(err)
	}
	since = base.Commit.Author.Date

	_, issueInfo, err := client.Issues.ListByRepo(
		context.Background(),
		*owner,
		*repo,
		&github.IssueListByRepoOptions{
			State:       "closed",
			Since:       *since,
			ListOptions: github.ListOptions{Page: 1, PerPage: 100},
		})
	if err != nil {
		panic(err)
	}
	if issueInfo.LastPage == 0 {
		// if we have only one page, LastPage is not set.
		issueInfo.LastPage = 1
	}
	_, commitInfo, err := client.Repositories.ListCommits(
		context.Background(),
		*owner,
		*repo,
		&github.CommitsListOptions{
			SHA:         "master",
			Since:       *since,
			ListOptions: github.ListOptions{Page: 1, PerPage: 100},
		})
	if err != nil {
		panic(err)
	}
	if commitInfo.LastPage == 0 {
		// if we have only one page, LastPage is not set.
		commitInfo.LastPage = 1
	}

	fmt.Printf("Fetching %d commit pages and %d issue pages...", commitInfo.LastPage, issueInfo.LastPage)
	// Enumerate commits
	commitPages := make(chan int, 100)
	commits := make(chan github.RepositoryCommit, 10000)
	commitWg := &sync.WaitGroup{}
	commitWg.Add(5)
	for w := 1; w <= 5; w++ {
		go func() {
			defer commitWg.Done()
			commitWorker(commitPages, commits)
		}()
	}
	for p := 1; p <= commitInfo.LastPage; p++ {
		commitPages <- p
	}
	close(commitPages)
	go func() {
		commitWg.Wait()
		close(commits)
	}()

	// Enumerate issues
	issuePages := make(chan int, 100)
	issues := make(chan github.Issue, 10000)
	issueWg := &sync.WaitGroup{}
	issueWg.Add(5)
	for w := 1; w <= 5; w++ {
		go func() {
			defer issueWg.Done()
			issueWorker(issuePages, issues)
		}()
	}
	for p := 1; p <= issueInfo.LastPage; p++ {
		issuePages <- p
	}
	close(issuePages)
	go func() {
		issueWg.Wait()
		close(issues)
	}()

	closedIssues := 0
	closedPRs := 0
	for issue := range issues {
		if issue.ClosedAt.After(*since) {
			if issue.PullRequestLinks != nil {
				closedPRs++
			} else {
				closedIssues++
			}
		}
	}

	contributors := make(map[string]bool)
	commitCount := 0
	for commit := range commits {
		contributors[*commit.Commit.Author.Name] = true
		commitCount += 1
	}
	days := int(time.Since(*since).Hours() / 24)

	fmt.Println("")
	fmt.Printf("Since the last release, the project has had %d commits by %d contributors, "+
		"resulting in %d closed issues and %d closed PRs, all of this in just over %d days.",
		commitCount, len(contributors), closedIssues, closedPRs, days)
	fmt.Println("")
}
<|endoftext|>


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

package piazza

import (
	"fmt"
	"net/http"
	"os"
	"regexp"
	"strings"
)

const DefaultElasticsearchAddress = "http://localhost:9200"
const DefaultKafkaAddress = "localhost:9092"
const DefaultPzLoggerAddress = "localhost:14600"
const DefaultPzUuidgenAddress = "localhost:14800"
const DefaultDomain = ".venicegeo.io"
const DefaultProtocol = "http"

var hasProtocol = regexp.MustCompile(`^[a-z]+:\/\/.+$`)

const waitTimeoutMs = 3000
const waitSleepMs = 250

type ServiceName string

const (
	PzDiscover          ServiceName = "pz-discover"
	PzElasticSearch     ServiceName = "pz-elasticsearch"
	PzGoCommon          ServiceName = "PZ-GOCOMMON"     // not a real service, just for testing
	PzGoCommonTest      ServiceName = "PZ-GOCOMMONTEST" // not a real service, just for testing
	PzKafka             ServiceName = "pz-kafka"
	PzLogger            ServiceName = "pz-logger"
	PzUuidgen           ServiceName = "pz-uuidgen"
	PzWorkflow          ServiceName = "pz-workflow"
	PzsvcHello          ServiceName = "pzsvc-hello"
	PzServiceController ServiceName = "pz-servicecontroller"
	PzMetrics           ServiceName = "pz-metrics"
	PzIdam              ServiceName = "pz-idam"
)

var LocalPortNumbers = map[ServiceName]string{
	PzWorkflow:          "20000",
	PzLogger:            "20001",
	PzUuidgen:           "20002",
	PzDiscover:          "20003",
	PzElasticSearch:     "20004",
	PzServiceController: "20005",
	PzMetrics:           "20006",
	PzKafka:             "20007",
	PzsvcHello:          "20008",
	PzGoCommon:          "20009",
	PzGoCommonTest:      "20010",
	PzIdam:              "20011",
}

var EndpointPrefixes = map[ServiceName]string{
	PzDiscover:          "",
	PzElasticSearch:     "",
	PzKafka:             "",
	PzLogger:            "",
	PzUuidgen:           "",
	PzWorkflow:          "",
	PzsvcHello:          "",
	PzServiceController: "",
	PzMetrics:           "",
	PzIdam:              "",
}

var HealthcheckEndpoints = map[ServiceName]string{
	PzDiscover:          "",
	PzElasticSearch:     "",
	PzKafka:             "",
	PzLogger:            "/",
	PzUuidgen:           "/",
	PzWorkflow:          "/",
	PzsvcHello:          "/",
	PzServiceController: "",
	PzMetrics:           "/",
	PzIdam:              "",
}

type ServicesMap map[ServiceName]string

type SystemConfig struct {
	// our own service
	Name    ServiceName
	Address string
	BindTo  string

	// our external services
	endpoints ServicesMap

	Space        string // int or stage or prod or...
	PiazzaSystem string // System-level username

	vcapApplication *VcapApplication
	vcapServices    *VcapServices
	domain          string
}

func NewSystemConfig(serviceName ServiceName,
	requiredServices []ServiceName) (*SystemConfig, error) {

	var err error

	sys := &SystemConfig{endpoints: make(ServicesMap)}

	sys.vcapApplication, err = NewVcapApplication(serviceName)
	if err != nil {
		return nil, err
	}

	sys.vcapServices, err = NewVcapServices()
	if err != nil {
		return nil, err
	}

	if sys.vcapApplication != nil {
		sys.domain = sys.vcapApplication.GetDomain()
	} else {
		sys.domain = DefaultDomain
	}

	if os.Getenv("DOMAIN") != "" {
		sys.domain = os.Getenv("DOMAIN")
		if !strings.HasPrefix(sys.domain, ".") {
			sys.domain = "." + sys.domain
		}
	}

	sys.Space = os.Getenv("SPACE")
	if sys.Space == "" {
		sys.Space = "int"
	}

	sys.PiazzaSystem = os.Getenv("PIAZZA_SYSTEM")
	if sys.PiazzaSystem == "" {
		sys.PiazzaSystem = "piazzaSystem"
	}

	// set some data about our own service first
	sys.Name = serviceName
	sys.Address = sys.vcapApplication.GetAddress()
	sys.BindTo = sys.vcapApplication.GetBindToPort()

	// set the services table with the services we require,
	// using VcapServices to get the addresses
	err = sys.checkRequirements(requiredServices)
	if err != nil {
		return nil, err
	}

	err = sys.runHealthChecks()
	if err != nil {
		return nil, err
	}

	return sys, nil
}

func (sys *SystemConfig) checkRequirements(requirements []ServiceName) error {

	for _, name := range requirements {

		if name == sys.Name {
			sys.AddService(name, sys.Address)

		} else {
			if addr, ok := sys.vcapServices.Services[name]; !ok {
				// the service we want is not in VCAP, so fake it
				sys.AddService(name, string(name)+sys.domain)

			} else {
				// the service we want is in VCAP, with a full and valid address
				sys.AddService(name, addr)
			}
		}
	}

	return nil
}

func (sys *SystemConfig) runHealthChecks() error {
	for name, addr := range sys.endpoints {
		if name == sys.Name || name == PzKafka {
			continue
		}

		url := createUrl(addr, HealthcheckEndpoints[name])

		resp, err := http.Get(url)
		if err != nil {
			return fmt.Errorf("Health check errored for service: %s at %s <%#v>", name, url, resp)
		}

		err = resp.Body.Close()
		if err != nil {
			return err
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Health check failed for service: %s at %s <%#v>", name, url, resp)
		}
	}

	return nil
}

// it is explicitly allowed for outsiders to update an existing service, but we'll log it just to be safe
func (sys *SystemConfig) AddService(name ServiceName, address string) {
	sys.endpoints[name] = address
}

func (sys *SystemConfig) GetAddress(name ServiceName) (string, error) {
	addr, ok := sys.endpoints[name]
	if !ok {
		return "", fmt.Errorf("Unknown service: %s", name)
	}

	return addr, nil
}

func (sys *SystemConfig) GetURL(name ServiceName) (string, error) {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return "", err
	}

	url := createUrl(addr, EndpointPrefixes[name])
	return url, nil
}

func (sys *SystemConfig) GetDomain() string {
	return sys.domain
}

func (sys *SystemConfig) WaitForService(name ServiceName) error {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return err
	}

	url := createUrl(addr)
	return WaitForService(name, url)
}

func (sys *SystemConfig) WaitForServiceToDie(name ServiceName) error {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return err
	}

	url := createUrl(addr)
	return WaitForServiceToDie(name, url)
}

func createUrl(parts ...string) string {
	url := ""
	for _, p := range parts {
		url += p
	}
	if !hasProtocol.MatchString(url) {
		url = DefaultProtocol + "://" + url
	}
	return url
}
<commit_msg>Update SystemConfig.go<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package piazza

import (
	"fmt"
	"net/http"
	"os"
	"regexp"
	"strings"
)

const DefaultElasticsearchAddress = "localhost:9200"
const DefaultKafkaAddress = "localhost:9092"
const DefaultPzLoggerAddress = "localhost:14600"
const DefaultPzUuidgenAddress = "localhost:14800"
const DefaultDomain = ".venicegeo.io"
const DefaultProtocol = "http"

var hasProtocol = regexp.MustCompile(`^[a-z]+:\/\/.+$`)

const waitTimeoutMs = 3000
const waitSleepMs = 250

type ServiceName string

const (
	PzDiscover          ServiceName = "pz-discover"
	PzElasticSearch     ServiceName = "pz-elasticsearch"
	PzGoCommon          ServiceName = "PZ-GOCOMMON"     // not a real service, just for testing
	PzGoCommonTest      ServiceName = "PZ-GOCOMMONTEST" // not a real service, just for testing
	PzKafka             ServiceName = "pz-kafka"
	PzLogger            ServiceName = "pz-logger"
	PzUuidgen           ServiceName = "pz-uuidgen"
	PzWorkflow          ServiceName = "pz-workflow"
	PzsvcHello          ServiceName = "pzsvc-hello"
	PzServiceController ServiceName = "pz-servicecontroller"
	PzMetrics           ServiceName = "pz-metrics"
	PzIdam              ServiceName = "pz-idam"
)

var LocalPortNumbers = map[ServiceName]string{
	PzWorkflow:          "20000",
	PzLogger:            "20001",
	PzUuidgen:           "20002",
	PzDiscover:          "20003",
	PzElasticSearch:     "20004",
	PzServiceController: "20005",
	PzMetrics:           "20006",
	PzKafka:             "20007",
	PzsvcHello:          "20008",
	PzGoCommon:          "20009",
	PzGoCommonTest:      "20010",
	PzIdam:              "20011",
}

var EndpointPrefixes = map[ServiceName]string{
	PzDiscover:          "",
	PzElasticSearch:     "",
	PzKafka:             "",
	PzLogger:            "",
	PzUuidgen:           "",
	PzWorkflow:          "",
	PzsvcHello:          "",
	PzServiceController: "",
	PzMetrics:           "",
	PzIdam:              "",
}

var HealthcheckEndpoints = map[ServiceName]string{
	PzDiscover:          "",
	PzElasticSearch:     "",
	PzKafka:             "",
	PzLogger:            "/",
	PzUuidgen:           "/",
	PzWorkflow:          "/",
	PzsvcHello:          "/",
	PzServiceController: "",
	PzMetrics:           "/",
	PzIdam:              "",
}

type ServicesMap map[ServiceName]string

type SystemConfig struct {
	// our own service
	Name    ServiceName
	Address string
	BindTo  string

	// our external services
	endpoints ServicesMap

	Space        string // int or stage or prod or...
	PiazzaSystem string // System-level username

	vcapApplication *VcapApplication
	vcapServices    *VcapServices
	domain          string
}

func NewSystemConfig(serviceName ServiceName,
	requiredServices []ServiceName) (*SystemConfig, error) {

	var err error

	sys := &SystemConfig{endpoints: make(ServicesMap)}

	sys.vcapApplication, err = NewVcapApplication(serviceName)
	if err != nil {
		return nil, err
	}

	sys.vcapServices, err = NewVcapServices()
	if err != nil {
		return nil, err
	}

	if sys.vcapApplication != nil {
		sys.domain = sys.vcapApplication.GetDomain()
	} else {
		sys.domain = DefaultDomain
	}

	if os.Getenv("DOMAIN") != "" {
		sys.domain = os.Getenv("DOMAIN")
		if !strings.HasPrefix(sys.domain, ".") {
			sys.domain = "." + sys.domain
		}
	}

	sys.Space = os.Getenv("SPACE")
	if sys.Space == "" {
		sys.Space = "int"
	}

	sys.PiazzaSystem = os.Getenv("PIAZZA_SYSTEM")
	if sys.PiazzaSystem == "" {
		sys.PiazzaSystem = "piazzaSystem"
	}

	// set some data about our own service first
	sys.Name = serviceName
	sys.Address = sys.vcapApplication.GetAddress()
	sys.BindTo = sys.vcapApplication.GetBindToPort()

	// set the services table with the services we require,
	// using VcapServices to get the addresses
	err = sys.checkRequirements(requiredServices)
	if err != nil {
		return nil, err
	}

	err = sys.runHealthChecks()
	if err != nil {
		return nil, err
	}

	return sys, nil
}

func (sys *SystemConfig) checkRequirements(requirements []ServiceName) error {

	for _, name := range requirements {

		if name == sys.Name {
			sys.AddService(name, sys.Address)

		} else {
			if addr, ok := sys.vcapServices.Services[name]; !ok {
				// the service we want is not in VCAP, so fake it
				sys.AddService(name, string(name)+sys.domain)

			} else {
				// the service we want is in VCAP, with a full and valid address
				sys.AddService(name, addr)
			}
		}
	}

	return nil
}

func (sys *SystemConfig) runHealthChecks() error {
	for name, addr := range sys.endpoints {
		if name == sys.Name || name == PzKafka {
			continue
		}

		url := createUrl(addr, HealthcheckEndpoints[name])

		resp, err := http.Get(url)
		if err != nil {
			return fmt.Errorf("Health check errored for service: %s at %s <%#v>", name, url, resp)
		}

		err = resp.Body.Close()
		if err != nil {
			return err
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Health check failed for service: %s at %s <%#v>", name, url, resp)
		}
	}

	return nil
}

// it is explicitly allowed for outsiders to update an existing service, but we'll log it just to be safe
func (sys *SystemConfig) AddService(name ServiceName, address string) {
	sys.endpoints[name] = address
}

func (sys *SystemConfig) GetAddress(name ServiceName) (string, error) {
	addr, ok := sys.endpoints[name]
	if !ok {
		return "", fmt.Errorf("Unknown service: %s", name)
	}

	return addr, nil
}

func (sys *SystemConfig) GetURL(name ServiceName) (string, error) {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return "", err
	}

	url := createUrl(addr, EndpointPrefixes[name])
	return url, nil
}

func (sys *SystemConfig) GetDomain() string {
	return sys.domain
}

func (sys *SystemConfig) WaitForService(name ServiceName) error {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return err
	}

	url := createUrl(addr)
	return WaitForService(name, url)
}

func (sys *SystemConfig) WaitForServiceToDie(name ServiceName) error {
	addr, err := sys.GetAddress(name)
	if err != nil {
		return err
	}

	url := createUrl(addr)
	return WaitForServiceToDie(name, url)
}

func createUrl(parts ...string) string {
	url := ""
	for _, p := range parts {
		url += p
	}
	if !hasProtocol.MatchString(url) {
		url = DefaultProtocol + "://" + url
	}
	return url
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. 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 implements binary search.

package sort

// Search uses binary search to find and return the smallest index i
// in [0, n) at which f(i) is true, assuming that on the range [0, n),
// f(i) == true implies f(i+1) == true.  That is, Search requires that
// f is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; Search returns
// the first true index.  If there is no such index, Search returns n.
// Search calls f(i) only for i in the range [0, n).
//
// A common use of Search is to find the index i for a value x in
// a sorted, indexable data structure such as an array or slice.
// In this case, the argument f, typically a closure, captures the value
// to be searched for, and how the data structure is indexed and
// ordered.
//
// For instance, given a slice data sorted in ascending order,
// the call Search(len(data), func(i int) bool { return data[i] >= 23 })
// returns the smallest index i such that data[i] >= 23.  If the caller
// wants to find whether 23 is in the slice, it must test data[i] == 23
// separately.
//
// Searching data sorted in descending order would use the <=
// operator instead of the >= operator.
//
// To complete the example above, the following code tries to find the value
// x in an integer slice data sorted in ascending order:
//
//	x := 23
//	i := sort.Search(len(data), func(i int) bool { return data[i] >= x })
//	if i < len(data) && data[i] == x {
//		// x is present at data[i]
//	} else {
//		// x is not present in data,
//		// but i is the index where it would be inserted.
//	}
//
// As a more whimsical example, this program guesses your number:
//
//	func GuessingGame() {
//		var s string
//		fmt.Printf("Pick an integer from 0 to 100.\n")
//		answer := sort.Search(100, func(i int) bool {
//			fmt.Printf("Is your number <= %d? ", i)
//			fmt.Scanf("%s", &s)
//			return s != "" && s[0] == 'y'
//		})
//		fmt.Printf("Your number is %d.\n", answer)
//	}
//
func Search(n int, f func(int) bool) int {
	// Define f(-1) == false and f(n) == true.
	// Invariant: f(i-1) == false, f(j) == true.
	i, j := 0, n
	for i < j {
		h := i + (j-i)/2 // avoid overflow when computing h
		// i ≤ h < j
		if !f(h) {
			i = h + 1 // preserves f(i-1) == false
		} else {
			j = h // preserves f(j) == true
		}
	}
	// i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
	return i
}

// Convenience wrappers for common cases.

// SearchInts searches for x in a sorted slice of ints and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchInts(a []int, x int) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// SearchFloat64s searches for x in a sorted slice of float64s and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchFloat64s(a []float64, x float64) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// SearchStrings searches for x slice a sorted slice of strings and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchStrings(a []string, x string) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// Search returns the result of applying SearchInts to the receiver and x.
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }

// Search returns the result of applying SearchFloat64s to the receiver and x.
func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }

// Search returns the result of applying SearchStrings to the receiver and x.
func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }
<commit_msg>sort: Fixed a typo in the documentation for SearchStrings.<commit_after>// Copyright 2010 The Go Authors. 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 implements binary search.

package sort

// Search uses binary search to find and return the smallest index i
// in [0, n) at which f(i) is true, assuming that on the range [0, n),
// f(i) == true implies f(i+1) == true.  That is, Search requires that
// f is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; Search returns
// the first true index.  If there is no such index, Search returns n.
// Search calls f(i) only for i in the range [0, n).
//
// A common use of Search is to find the index i for a value x in
// a sorted, indexable data structure such as an array or slice.
// In this case, the argument f, typically a closure, captures the value
// to be searched for, and how the data structure is indexed and
// ordered.
//
// For instance, given a slice data sorted in ascending order,
// the call Search(len(data), func(i int) bool { return data[i] >= 23 })
// returns the smallest index i such that data[i] >= 23.  If the caller
// wants to find whether 23 is in the slice, it must test data[i] == 23
// separately.
//
// Searching data sorted in descending order would use the <=
// operator instead of the >= operator.
//
// To complete the example above, the following code tries to find the value
// x in an integer slice data sorted in ascending order:
//
//	x := 23
//	i := sort.Search(len(data), func(i int) bool { return data[i] >= x })
//	if i < len(data) && data[i] == x {
//		// x is present at data[i]
//	} else {
//		// x is not present in data,
//		// but i is the index where it would be inserted.
//	}
//
// As a more whimsical example, this program guesses your number:
//
//	func GuessingGame() {
//		var s string
//		fmt.Printf("Pick an integer from 0 to 100.\n")
//		answer := sort.Search(100, func(i int) bool {
//			fmt.Printf("Is your number <= %d? ", i)
//			fmt.Scanf("%s", &s)
//			return s != "" && s[0] == 'y'
//		})
//		fmt.Printf("Your number is %d.\n", answer)
//	}
//
func Search(n int, f func(int) bool) int {
	// Define f(-1) == false and f(n) == true.
	// Invariant: f(i-1) == false, f(j) == true.
	i, j := 0, n
	for i < j {
		h := i + (j-i)/2 // avoid overflow when computing h
		// i ≤ h < j
		if !f(h) {
			i = h + 1 // preserves f(i-1) == false
		} else {
			j = h // preserves f(j) == true
		}
	}
	// i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
	return i
}

// Convenience wrappers for common cases.

// SearchInts searches for x in a sorted slice of ints and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchInts(a []int, x int) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// SearchFloat64s searches for x in a sorted slice of float64s and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchFloat64s(a []float64, x float64) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// SearchStrings searches for x in a sorted slice of strings and returns the index
// as specified by Search. The slice must be sorted in ascending order.
//
func SearchStrings(a []string, x string) int {
	return Search(len(a), func(i int) bool { return a[i] >= x })
}

// Search returns the result of applying SearchInts to the receiver and x.
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }

// Search returns the result of applying SearchFloat64s to the receiver and x.
func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }

// Search returns the result of applying SearchStrings to the receiver and x.
func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }
<|endoftext|>


<commit_before>package engine

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	log "github.com/sirupsen/logrus"
	"github.com/tylerb/graceful"
	"golang.org/x/net/http2"
)

// List of functions to run at shutdown
var (
	shutdownFunctions []func()
	mut               sync.Mutex
	completed         bool
)

// AtShutdown adds a function to the list of functions that will be ran at shutdown
func AtShutdown(shutdownFunction func()) {
	mut.Lock()
	defer mut.Unlock()
	shutdownFunctions = append(shutdownFunctions, shutdownFunction)
}

// NewGracefulServer creates a new graceful server configuration
func (ac *Config) NewGracefulServer(mux *http.ServeMux, http2support bool, addr string) *graceful.Server {
	// Server configuration
	s := &http.Server{
		Addr:    addr,
		Handler: mux,

		// The timeout values is also the maximum time it can take
		// for a complete page of Server-Sent Events (SSE).
		ReadTimeout:  10 * time.Second,
		WriteTimeout: time.Duration(ac.writeTimeout) * time.Second,

		MaxHeaderBytes: 1 << 20,
	}
	if http2support {
		// Enable HTTP/2 support
		http2.ConfigureServer(s, nil)
	}
	gracefulServer := &graceful.Server{
		Server:  s,
		Timeout: ac.shutdownTimeout,
	}
	// Handle ctrl-c
	gracefulServer.ShutdownInitiated = ac.GenerateShutdownFunction(gracefulServer) // for investigating gracefulServer.Interrupted
	return gracefulServer
}

// GenerateShutdownFunction generates a function that will run the postponed
// shutdown functions.  Note that gracefulServer can be nil. It's only used for
// finding out if the server was interrupted (ctrl-c or killed, SIGINT/SIGTERM)
func (ac *Config) GenerateShutdownFunction(gracefulServer *graceful.Server) func() {
	return func() {
		mut.Lock()
		defer mut.Unlock()

		if completed {
			// The shutdown functions have already been called
			return
		}

		if ac.verboseMode {
			log.Info("Initiating shutdown")
		}

		// Call the shutdown functions in chronological order (FIFO)
		for _, shutdownFunction := range shutdownFunctions {
			shutdownFunction()
		}

		completed = true

		if ac.verboseMode {
			log.Info("Shutdown complete")
		}

		// Forced shutdown
		if gracefulServer != nil {
			if gracefulServer.Interrupted {
				//gracefulServer.Stop(forcedShutdownTimeout)
				ac.fatalExit(errors.New("Interrupted"))
			}
		}
		// TODO: To implement
		//if quicServer != nil {
		//fmt.Println("DEBUG: Has QUIC server at shutdown!")
		//}

		// One final flush
		os.Stdout.Sync()
	}
}

// Serve HTTP, HTTP/2 and/or HTTPS. Returns an error if unable to serve, or nil when done serving.
func (ac *Config) Serve(mux *http.ServeMux, done, ready chan bool) error {

	// If we are not writing internal logs to a file, reduce the verbosity
	http2.VerboseLogs = (ac.internalLogFilename != os.DevNull)

	if ac.onlyLuaMode {
		ready <- true // Send a "ready" message to the REPL
		<-done        // Wait for a "done" message from the REPL (or just keep waiting)
		// Serve nothing
		return nil // Done
	}

	// Channel to wait and see if we should just serve regular HTTP instead
	justServeRegularHTTP := make(chan bool)

	servingHTTPS := false
	servingHTTP := false

	// Goroutine that wait for a message to just serve regular HTTP, if needed
	go func() {
		<-justServeRegularHTTP // Wait for a message to just serve regular HTTP
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving HTTP on http://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving HTTP on http://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		HTTPserver := ac.NewGracefulServer(mux, false, ac.serverAddr)
		// Open the URL before the serving has started, in a short delay
		if ac.openURLAfterServing && ac.luaServerFilename != "" {
			go func() {
				time.Sleep(delayBeforeLaunchingBrowser)
				ac.OpenURL(ac.serverHost, ac.serverAddr, false)
			}()
		}
		// Start serving. Shut down gracefully at exit.
		if err := HTTPserver.ListenAndServe(); err != nil {
			mut.Lock()
			servingHTTP = false
			mut.Unlock()
			// If we can't serve regular HTTP on port 80, give up
			ac.fatalExit(err)
		}
	}()

	// Decide which protocol to listen to
	switch {
	case len(ac.certMagicDomains) > 0:
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving with CertMagic on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving with CertMagic on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// TODO: Look at "Advanced use" at https://github.com/caddyserver/certmagic#examples
		// Listen for HTTP and HTTPS requests, for specific domain(s)
		go func() {
			if err := certmagic.HTTPS(ac.certMagicDomains, mux); err != nil {
				log.Errorf("%s. Not serving with CertMagic.", err)
				log.Info("Use the -t flag for serving regular HTTP.")
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				// If HTTPS failed (perhaps the key + cert are missing),
				// serve plain HTTP instead
				justServeRegularHTTP <- true
			}
		}()
	case ac.serveJustQUIC: // Just serve QUIC, but fallback to HTTP
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving QUIC on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving QUIC on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// Start serving over QUIC
		go ac.ListenAndServeQUIC(mux, &mut, justServeRegularHTTP, &servingHTTPS)
	case ac.productionMode:
		// Listen for both HTTPS+HTTP/2 and HTTP requests, on different ports
		if len(ac.serverHost) == 0 {
			log.Info("Serving HTTP/2 on https://localhost/")
		} else {
			log.Info("Serving HTTP/2 on https://" + ac.serverHost + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		go func() {
			// Start serving. Shut down gracefully at exit.
			// Listen for HTTPS + HTTP/2 requests
			HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverHost+":443")
			// Start serving. Shut down gracefully at exit.
			if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				log.Error(err)
			}
		}()
		if len(ac.serverHost) == 0 {
			log.Info("Serving HTTP on http://localhost/")
		} else {
			log.Info("Serving HTTP on http://" + ac.serverHost + "/")
		}
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		go func() {
			HTTPserver := ac.NewGracefulServer(mux, false, ac.serverHost+":80")
			if err := HTTPserver.ListenAndServe(); err != nil {
				mut.Lock()
				servingHTTP = false
				mut.Unlock()
				// If we can't serve regular HTTP on port 80, give up
				ac.fatalExit(err)
			}
		}()
	case ac.serveJustHTTP2: // It's unusual to serve HTTP/2 without HTTPS
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://localhost" + ac.serverAddr + "/")
		} else {
			log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		go func() {
			// Listen for HTTP/2 requests
			HTTP2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
			// Start serving. Shut down gracefully at exit.
			if err := HTTP2server.ListenAndServe(); err != nil {
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				justServeRegularHTTP <- true
				log.Error(err)
			}
		}()
	case !(ac.serveJustHTTP2 || ac.serveJustHTTP):
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving HTTP/2 on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving HTTP/2 on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// Listen for HTTPS + HTTP/2 requests
		HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
		// Start serving. Shut down gracefully at exit.
		go func() {
			if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
				log.Errorf("%s. Not serving HTTP/2.", err)
				log.Info("Use the -t flag for serving regular HTTP.")
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				// If HTTPS failed (perhaps the key + cert are missing),
				// serve plain HTTP instead
				justServeRegularHTTP <- true
			}
		}()
	default:
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		justServeRegularHTTP <- true
	}

	// Wait just a tiny bit
	time.Sleep(20 * time.Millisecond)

	ready <- true // Send a "ready" message to the REPL

	// Open the URL, if specified
	if ac.openURLAfterServing {
		// Open the https:// URL if both http:// and https:// are being served
		mut.Lock()
		if (!servingHTTP) && (!servingHTTPS) {
			ac.fatalExit(errors.New("serving neither over https:// nor over https://"))
		}
		httpsProtocol := servingHTTPS
		mut.Unlock()
		ac.OpenURL(ac.serverHost, ac.serverAddr, httpsProtocol)
	}

	<-done // Wait for a "done" message from the REPL (or just keep waiting)

	return nil // Done serving
}
<commit_msg>Add support for HTTP -> HTTPS redirect<commit_after>package engine

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	log "github.com/sirupsen/logrus"
	"github.com/tylerb/graceful"
	"golang.org/x/net/http2"
)

// List of functions to run at shutdown
var (
	shutdownFunctions []func()
	mut               sync.Mutex
	completed         bool
)

// AtShutdown adds a function to the list of functions that will be ran at shutdown
func AtShutdown(shutdownFunction func()) {
	mut.Lock()
	defer mut.Unlock()
	shutdownFunctions = append(shutdownFunctions, shutdownFunction)
}

// NewGracefulServer creates a new graceful server configuration
func (ac *Config) NewGracefulServer(mux *http.ServeMux, http2support bool, addr string) *graceful.Server {
	// Server configuration
	s := &http.Server{
		Addr:    addr,
		Handler: mux,

		// The timeout values is also the maximum time it can take
		// for a complete page of Server-Sent Events (SSE).
		ReadTimeout:  10 * time.Second,
		WriteTimeout: time.Duration(ac.writeTimeout) * time.Second,

		MaxHeaderBytes: 1 << 20,
	}
	if http2support {
		// Enable HTTP/2 support
		http2.ConfigureServer(s, nil)
	}
	gracefulServer := &graceful.Server{
		Server:  s,
		Timeout: ac.shutdownTimeout,
	}
	// Handle ctrl-c
	gracefulServer.ShutdownInitiated = ac.GenerateShutdownFunction(gracefulServer) // for investigating gracefulServer.Interrupted
	return gracefulServer
}

// GenerateShutdownFunction generates a function that will run the postponed
// shutdown functions.  Note that gracefulServer can be nil. It's only used for
// finding out if the server was interrupted (ctrl-c or killed, SIGINT/SIGTERM)
func (ac *Config) GenerateShutdownFunction(gracefulServer *graceful.Server) func() {
	return func() {
		mut.Lock()
		defer mut.Unlock()

		if completed {
			// The shutdown functions have already been called
			return
		}

		if ac.verboseMode {
			log.Info("Initiating shutdown")
		}

		// Call the shutdown functions in chronological order (FIFO)
		for _, shutdownFunction := range shutdownFunctions {
			shutdownFunction()
		}

		completed = true

		if ac.verboseMode {
			log.Info("Shutdown complete")
		}

		// Forced shutdown
		if gracefulServer != nil {
			if gracefulServer.Interrupted {
				//gracefulServer.Stop(forcedShutdownTimeout)
				ac.fatalExit(errors.New("Interrupted"))
			}
		}
		// TODO: To implement
		//if quicServer != nil {
		//fmt.Println("DEBUG: Has QUIC server at shutdown!")
		//}

		// One final flush
		os.Stdout.Sync()
	}
}

// Serve HTTP, HTTP/2 and/or HTTPS. Returns an error if unable to serve, or nil when done serving.
func (ac *Config) Serve(mux *http.ServeMux, done, ready chan bool) error {

	// If we are not writing internal logs to a file, reduce the verbosity
	http2.VerboseLogs = (ac.internalLogFilename != os.DevNull)

	if ac.onlyLuaMode {
		ready <- true // Send a "ready" message to the REPL
		<-done        // Wait for a "done" message from the REPL (or just keep waiting)
		// Serve nothing
		return nil // Done
	}

	// Channel to wait and see if we should just serve regular HTTP instead
	justServeRegularHTTP := make(chan bool)

	servingHTTPS := false
	servingHTTP := false

	// Goroutine that wait for a message to just serve regular HTTP, if needed
	go func() {
		<-justServeRegularHTTP // Wait for a message to just serve regular HTTP
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving HTTP on http://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving HTTP on http://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		HTTPserver := ac.NewGracefulServer(mux, false, ac.serverAddr)
		// Open the URL before the serving has started, in a short delay
		if ac.openURLAfterServing && ac.luaServerFilename != "" {
			go func() {
				time.Sleep(delayBeforeLaunchingBrowser)
				ac.OpenURL(ac.serverHost, ac.serverAddr, false)
			}()
		}
		// Start serving. Shut down gracefully at exit.
		if err := HTTPserver.ListenAndServe(); err != nil {
			mut.Lock()
			servingHTTP = false
			mut.Unlock()
			// If we can't serve regular HTTP on port 80, give up
			ac.fatalExit(err)
		}
	}()

	// Decide which protocol to listen to
	switch {
	case len(ac.certMagicDomains) > 0:
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving with CertMagic on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving with CertMagic on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// TODO: Look at "Advanced use" at https://github.com/caddyserver/certmagic#examples
		// Listen for HTTP and HTTPS requests, for specific domain(s)
		go func() {
			if err := certmagic.HTTPS(ac.certMagicDomains, mux); err != nil {
				log.Errorf("%s. Not serving with CertMagic.", err)
				log.Info("Use the -t flag for serving regular HTTP.")
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				// If HTTPS failed (perhaps the key + cert are missing),
				// serve plain HTTP instead
				justServeRegularHTTP <- true
			}
		}()
	case ac.serveJustQUIC: // Just serve QUIC, but fallback to HTTP
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving QUIC on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving QUIC on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// Start serving over QUIC
		go ac.ListenAndServeQUIC(mux, &mut, justServeRegularHTTP, &servingHTTPS)
	case ac.productionMode:
		// Listen for both HTTPS+HTTP/2 and HTTP requests, on different ports
		if len(ac.serverHost) == 0 {
			log.Info("Serving HTTP/2 on https://localhost/")
		} else {
			log.Info("Serving HTTP/2 on https://" + ac.serverHost + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		go func() {
			// Start serving. Shut down gracefully at exit.
			// Listen for HTTPS + HTTP/2 requests
			HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverHost+":443")
			// Start serving. Shut down gracefully at exit.
			if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				log.Error(err)
			}
		}()
		if len(ac.serverHost) == 0 {
			log.Info("Serving HTTP on http://localhost/")
		} else {
			log.Info("Serving HTTP on http://" + ac.serverHost + "/")
		}
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		go func() {
			if ac.noHTTPRedirect {
				// Redirect HTTPS to HTTP
				redirectFunc := func(w http.ResponseWriter, req *http.Request) {
					http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
				}
				if err := http.ListenAndServe(ac.serverHost+":80", http.HandlerFunc(redirectFunc)); err != nil {
					mut.Lock()
					servingHTTP = false
					mut.Unlock()
					// If we can't serve regular HTTP on port 80, give up
					ac.fatalExit(err)
				}
			} else {
				// Don't redirect, but serve the same contents as the HTTPS server as HTTP on port 80
				HTTPserver := ac.NewGracefulServer(mux, false, ac.serverHost+":80")
				if err := HTTPserver.ListenAndServe(); err != nil {
					mut.Lock()
					servingHTTP = false
					mut.Unlock()
					// If we can't serve regular HTTP on port 80, give up
					ac.fatalExit(err)
				}
			}
		}()
	case ac.serveJustHTTP2: // It's unusual to serve HTTP/2 without HTTPS
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://localhost" + ac.serverAddr + "/")
		} else {
			log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		go func() {
			// Listen for HTTP/2 requests
			HTTP2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
			// Start serving. Shut down gracefully at exit.
			if err := HTTP2server.ListenAndServe(); err != nil {
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				justServeRegularHTTP <- true
				log.Error(err)
			}
		}()
	case !(ac.serveJustHTTP2 || ac.serveJustHTTP):
		if strings.HasPrefix(ac.serverAddr, ":") {
			log.Info("Serving HTTP/2 on https://localhost" + ac.serverAddr + "/")
		} else {
			log.Info("Serving HTTP/2 on https://" + ac.serverAddr + "/")
		}
		mut.Lock()
		servingHTTPS = true
		mut.Unlock()
		// Listen for HTTPS + HTTP/2 requests
		HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
		// Start serving. Shut down gracefully at exit.
		go func() {
			if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
				log.Errorf("%s. Not serving HTTP/2.", err)
				log.Info("Use the -t flag for serving regular HTTP.")
				mut.Lock()
				servingHTTPS = false
				mut.Unlock()
				// If HTTPS failed (perhaps the key + cert are missing),
				// serve plain HTTP instead
				justServeRegularHTTP <- true
			}
		}()
	default:
		mut.Lock()
		servingHTTP = true
		mut.Unlock()
		justServeRegularHTTP <- true
	}

	// Wait just a tiny bit
	time.Sleep(20 * time.Millisecond)

	ready <- true // Send a "ready" message to the REPL

	// Open the URL, if specified
	if ac.openURLAfterServing {
		// Open the https:// URL if both http:// and https:// are being served
		mut.Lock()
		if (!servingHTTP) && (!servingHTTPS) {
			ac.fatalExit(errors.New("serving neither over https:// nor over https://"))
		}
		httpsProtocol := servingHTTPS
		mut.Unlock()
		ac.OpenURL(ac.serverHost, ac.serverAddr, httpsProtocol)
	}

	<-done // Wait for a "done" message from the REPL (or just keep waiting)

	return nil // Done serving
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ovh/cds/engine/api"
	"github.com/ovh/cds/engine/cdn"
	"github.com/ovh/cds/engine/elasticsearch"
	"github.com/ovh/cds/engine/hatchery/kubernetes"
	"github.com/ovh/cds/engine/hatchery/local"
	"github.com/ovh/cds/engine/hatchery/marathon"
	"github.com/ovh/cds/engine/hatchery/openstack"
	"github.com/ovh/cds/engine/hatchery/swarm"
	"github.com/ovh/cds/engine/hatchery/vsphere"
	"github.com/ovh/cds/engine/hooks"
	"github.com/ovh/cds/engine/migrateservice"
	"github.com/ovh/cds/engine/repositories"
	"github.com/ovh/cds/engine/ui"
	"github.com/ovh/cds/engine/vcs"
	"github.com/ovh/cds/sdk/telemetry"
)

// Configuration contains CDS Configuration and toml description
type Configuration struct {
	// common
	Log struct {
		Level   string `toml:"level" default:"warning" comment:"Log Level: debug, info, warning, notice, critical" json:"level"`
		Format  string `toml:"format" default:"text" comment:"Stdout format: text, json, discard" json:"format"`
		Graylog struct {
			Host       string `toml:"host" comment:"Example: thot.ovh.com" json:"host"`
			Port       int    `toml:"port" comment:"Example: 12202" json:"port"`
			Protocol   string `toml:"protocol" default:"tcp" comment:"tcp or udp" json:"protocol"`
			ExtraKey   string `toml:"extraKey" comment:"Example: X-OVH-TOKEN. You can use many keys: aaa,bbb" json:"extraKey"`
			ExtraValue string `toml:"extraValue" comment:"value for extraKey field. For many keys: valueaaa,valuebbb" json:"extraValue"`
		} `toml:"graylog"`
	} `toml:"log" comment:"#####################\n CDS Logs Settings \n####################"`
	Telemetry telemetry.Configuration `toml:"telemetry" comment:"###########################\n CDS Telemetry Settings \n##########################" json:"telemetry"`
	// services
	API             *api.Configuration            `toml:"api" comment:"#####################\n API Configuration \n####################" json:"api"`
	UI              *ui.Configuration             `toml:"ui" comment:"#####################\n UI Configuration \n####################" json:"ui"`
	Hatchery        *HatcheryConfiguration        `toml:"hatchery" json:"hatchery"`
	Hooks           *hooks.Configuration          `toml:"hooks" comment:"######################\n CDS Hooks Settings \n######################" json:"hooks"`
	CDN             *cdn.Configuration            `toml:"cdn" comment:"######################\n CDS cdn Settings \n######################" json:"cdn"`
	VCS             *vcs.Configuration            `toml:"vcs" comment:"######################\n CDS VCS Settings \n######################" json:"vcs"`
	Repositories    *repositories.Configuration   `toml:"repositories" comment:"######################\n CDS Repositories Settings \n######################" json:"repositories"`
	ElasticSearch   *elasticsearch.Configuration  `toml:"elasticsearch" comment:"######################\n CDS ElasticSearch Settings \n This is use for CDS timeline and is optional\n######################" json:"elasticsearch"`
	DatabaseMigrate *migrateservice.Configuration `toml:"databaseMigrate" comment:"######################\n CDS DB Migrate Service Settings \n######################" json:"databaseMigrate"`
}

// HatcheryConfiguration contains subsection of Hatchery configuration
type HatcheryConfiguration struct {
	Local      *local.HatcheryConfiguration      `toml:"local" comment:"Hatchery Local. Doc: https://ovh.github.io/cds/docs/components/hatchery/local/" json:"local"`
	Kubernetes *kubernetes.HatcheryConfiguration `toml:"kubernetes" comment:"Hatchery Kubernetes. Doc: https://ovh.github.io/cds/docs/integrations/hatchery/kubernetes/" json:"kubernetes"`
	Marathon   *marathon.HatcheryConfiguration   `toml:"marathon" comment:"Hatchery Marathon. Doc: https://ovh.github.io/cds/docs/integrations/hatchery/marathon/" json:"marathon"`
	Openstack  *openstack.HatcheryConfiguration  `toml:"openstack" comment:"Hatchery OpenStack. Doc: https://ovh.github.io/cds/docs/integrations/hatchery/openstack/" json:"openstack"`
	Swarm      *swarm.HatcheryConfiguration      `toml:"swarm" comment:"Hatchery Swarm. Doc: https://ovh.github.io/cds/docs/integrations/swarm/" json:"swarm"`
	VSphere    *vsphere.HatcheryConfiguration    `toml:"vsphere" comment:"Hatchery VShpere. Doc: https://ovh.github.io/cds/docs/integrations/hatchery/vsphere/" json:"vshpere"`
}
<commit_msg>doc: fix references for hatcheries (#6175)<commit_after>package main

import (
	"github.com/ovh/cds/engine/api"
	"github.com/ovh/cds/engine/cdn"
	"github.com/ovh/cds/engine/elasticsearch"
	"github.com/ovh/cds/engine/hatchery/kubernetes"
	"github.com/ovh/cds/engine/hatchery/local"
	"github.com/ovh/cds/engine/hatchery/marathon"
	"github.com/ovh/cds/engine/hatchery/openstack"
	"github.com/ovh/cds/engine/hatchery/swarm"
	"github.com/ovh/cds/engine/hatchery/vsphere"
	"github.com/ovh/cds/engine/hooks"
	"github.com/ovh/cds/engine/migrateservice"
	"github.com/ovh/cds/engine/repositories"
	"github.com/ovh/cds/engine/ui"
	"github.com/ovh/cds/engine/vcs"
	"github.com/ovh/cds/sdk/telemetry"
)

// Configuration contains CDS Configuration and toml description
type Configuration struct {
	// common
	Log struct {
		Level   string `toml:"level" default:"warning" comment:"Log Level: debug, info, warning, notice, critical" json:"level"`
		Format  string `toml:"format" default:"text" comment:"Stdout format: text, json, discard" json:"format"`
		Graylog struct {
			Host       string `toml:"host" comment:"Example: thot.ovh.com" json:"host"`
			Port       int    `toml:"port" comment:"Example: 12202" json:"port"`
			Protocol   string `toml:"protocol" default:"tcp" comment:"tcp or udp" json:"protocol"`
			ExtraKey   string `toml:"extraKey" comment:"Example: X-OVH-TOKEN. You can use many keys: aaa,bbb" json:"extraKey"`
			ExtraValue string `toml:"extraValue" comment:"value for extraKey field. For many keys: valueaaa,valuebbb" json:"extraValue"`
		} `toml:"graylog"`
	} `toml:"log" comment:"#####################\n CDS Logs Settings \n####################"`
	Telemetry telemetry.Configuration `toml:"telemetry" comment:"###########################\n CDS Telemetry Settings \n##########################" json:"telemetry"`
	// services
	API             *api.Configuration            `toml:"api" comment:"#####################\n API Configuration \n####################" json:"api"`
	UI              *ui.Configuration             `toml:"ui" comment:"#####################\n UI Configuration \n####################" json:"ui"`
	Hatchery        *HatcheryConfiguration        `toml:"hatchery" json:"hatchery"`
	Hooks           *hooks.Configuration          `toml:"hooks" comment:"######################\n CDS Hooks Settings \n######################" json:"hooks"`
	CDN             *cdn.Configuration            `toml:"cdn" comment:"######################\n CDS cdn Settings \n######################" json:"cdn"`
	VCS             *vcs.Configuration            `toml:"vcs" comment:"######################\n CDS VCS Settings \n######################" json:"vcs"`
	Repositories    *repositories.Configuration   `toml:"repositories" comment:"######################\n CDS Repositories Settings \n######################" json:"repositories"`
	ElasticSearch   *elasticsearch.Configuration  `toml:"elasticsearch" comment:"######################\n CDS ElasticSearch Settings \n This is use for CDS timeline and is optional\n######################" json:"elasticsearch"`
	DatabaseMigrate *migrateservice.Configuration `toml:"databaseMigrate" comment:"######################\n CDS DB Migrate Service Settings \n######################" json:"databaseMigrate"`
}

// HatcheryConfiguration contains subsection of Hatchery configuration
type HatcheryConfiguration struct {
	Local      *local.HatcheryConfiguration      `toml:"local" comment:"Hatchery Local. Doc: https://ovh.github.io/cds/docs/components/hatchery/local/" json:"local"`
	Kubernetes *kubernetes.HatcheryConfiguration `toml:"kubernetes" comment:"Hatchery Kubernetes. Doc: https://ovh.github.io/cds/docs/integrations/kubernetes/" json:"kubernetes"`
	Marathon   *marathon.HatcheryConfiguration   `toml:"marathon" comment:"Hatchery Marathon. Doc: https://ovh.github.io/cds/docs/integrations/marathon/" json:"marathon"`
	Openstack  *openstack.HatcheryConfiguration  `toml:"openstack" comment:"Hatchery OpenStack. Doc: https://ovh.github.io/cds/docs/integrations/openstack/" json:"openstack"`
	Swarm      *swarm.HatcheryConfiguration      `toml:"swarm" comment:"Hatchery Swarm. Doc: https://ovh.github.io/cds/docs/integrations/swarm/" json:"swarm"`
	VSphere    *vsphere.HatcheryConfiguration    `toml:"vsphere" comment:"Hatchery VShpere. Doc: https://ovh.github.io/cds/docs/integrations/vsphere/" json:"vshpere"`
}
<|endoftext|>


<commit_before>package equidistant

import (
	"fmt"

	"github.com/ready-steady/adapt/internal"
)

// Open is a grid in (0, 1)^n.
type Open struct {
	nd uint
}

// NewOpen creates a grid.
func NewOpen(dimensions uint) *Open {
	return &Open{dimensions}
}

// Compute returns the nodes corresponding to a set of indices.
func (_ *Open) Compute(indices []uint64) []float64 {
	nodes := make([]float64, len(indices))
	for i := range nodes {
		level := internal.LEVEL_MASK & indices[i]
		order := indices[i] >> internal.LEVEL_SIZE
		nodes[i] = float64(order+1) / float64(uint64(2)<<level)
	}
	return nodes
}

// Refine returns the child indices of a set of indices.
func (self *Open) Refine(indices []uint64) []uint64 {
	return openRefine(indices, self.nd, 0, self.nd)
}

func openRefine(indices []uint64, nd, fd, ld uint) []uint64 {
	nn := uint(len(indices)) / nd

	children := make([]uint64, 2*nn*nd*(ld-fd))

	nc := uint(0)
	push := func(p, d uint, level, order uint64) {
		if level>>internal.LEVEL_SIZE != 0 || order>>internal.ORDER_SIZE != 0 {
			panic(fmt.Sprintf("the level %d or order %d is too large", level, order))
		}
		copy(children[nc*nd:], indices[p*nd:(p+1)*nd])
		children[nc*nd+d] = level | order<<internal.LEVEL_SIZE
		nc++
	}

	for i := uint(0); i < nn; i++ {
		for j := fd; j < ld; j++ {
			level := internal.LEVEL_MASK & indices[i*nd+j]
			order := indices[i*nd+j] >> internal.LEVEL_SIZE
			push(i, j, level+1, 2*order)
			push(i, j, level+1, 2*order+2)
		}
	}

	return children[:nc*nd]
}
<commit_msg>g/equidistant: introduce Open.RefineToward<commit_after>package equidistant

import (
	"fmt"

	"github.com/ready-steady/adapt/internal"
)

// Open is a grid in (0, 1)^n.
type Open struct {
	nd uint
}

// NewOpen creates a grid.
func NewOpen(dimensions uint) *Open {
	return &Open{dimensions}
}

// Compute returns the nodes corresponding to a set of indices.
func (_ *Open) Compute(indices []uint64) []float64 {
	nodes := make([]float64, len(indices))
	for i := range nodes {
		level := internal.LEVEL_MASK & indices[i]
		order := indices[i] >> internal.LEVEL_SIZE
		nodes[i] = float64(order+1) / float64(uint64(2)<<level)
	}
	return nodes
}

// Refine returns the child indices of a set of indices.
func (self *Open) Refine(indices []uint64) []uint64 {
	return openRefine(indices, self.nd, 0, self.nd)
}

// RefineToward returns the child indices of a set of indices with respect to a
// particular dimension.
func (self *Open) RefineToward(indices []uint64, i uint) []uint64 {
	return openRefine(indices, self.nd, i, i+1)
}

func openRefine(indices []uint64, nd, fd, ld uint) []uint64 {
	nn := uint(len(indices)) / nd

	children := make([]uint64, 2*nn*nd*(ld-fd))

	nc := uint(0)
	push := func(p, d uint, level, order uint64) {
		if level>>internal.LEVEL_SIZE != 0 || order>>internal.ORDER_SIZE != 0 {
			panic(fmt.Sprintf("the level %d or order %d is too large", level, order))
		}
		copy(children[nc*nd:], indices[p*nd:(p+1)*nd])
		children[nc*nd+d] = level | order<<internal.LEVEL_SIZE
		nc++
	}

	for i := uint(0); i < nn; i++ {
		for j := fd; j < ld; j++ {
			level := internal.LEVEL_MASK & indices[i*nd+j]
			order := indices[i*nd+j] >> internal.LEVEL_SIZE
			push(i, j, level+1, 2*order)
			push(i, j, level+1, 2*order+2)
		}
	}

	return children[:nc*nd]
}
<|endoftext|>


<commit_before>package db

import (
	"github.com/BurntSushi/migration"
	api "github.com/danmane/abalone/go/api"
	"github.com/danmane/abalone/go/api/migrations"
	"github.com/danmane/abalone/go/operator"
	"github.com/jinzhu/gorm"
)

func Open(dialect string, addr string, filestoragePath string) (*api.Services, error) {
	conn, err := migration.Open(dialect, addr, migrations.Migrations)
	if err != nil {
		return nil, err
	}
	db, err := gorm.Open(dialect, conn)
	if err != nil {
		return nil, err
	}

	r := &resources{
		DB:              &db,
		Ports:           operator.NewScheduler(16000, 2000),
		FilestoragePath: filestoragePath,
	}
	return &api.Services{
		Games:   &gamesDB{r},
		Matches: &matchesDB{r},
		Players: &playersDB{r},
		Users:   &usersDB{r},
		DB:      &db,
	}, nil
}

type resources struct {
	DB              *gorm.DB
	Ports           *operator.PortScheduler
	FilestoragePath string
}
<commit_msg>comment<commit_after>package db

import (
	"github.com/BurntSushi/migration"
	api "github.com/danmane/abalone/go/api"
	"github.com/danmane/abalone/go/api/migrations"
	"github.com/danmane/abalone/go/operator"
	"github.com/jinzhu/gorm"
)

func Open(dialect string, addr string, filestoragePath string) (*api.Services, error) {
	conn, err := migration.Open(dialect, addr, migrations.Migrations)
	if err != nil {
		return nil, err
	}
	db, err := gorm.Open(dialect, conn)
	if err != nil {
		return nil, err
	}

	r := &resources{
		DB:              &db,
		Ports:           operator.NewScheduler(16000, 2000),
		FilestoragePath: filestoragePath,
	}
	return &api.Services{
		Games:   &gamesDB{r},
		Matches: &matchesDB{r},
		Players: &playersDB{r},
		Users:   &usersDB{r},
		DB:      &db,
	}, nil
}

// resources bundles up all of the components required by the various DB
// structs.
type resources struct {
	DB              *gorm.DB
	Ports           *operator.PortScheduler
	FilestoragePath string
}
<|endoftext|>


<commit_before>package overcurrent

import (
	"errors"
	"time"
)

type CircuitState int
type CircuitEvent int

const (
	OpenState CircuitState = iota
	ClosedState
	HalfClosedState

	TripEvent CircuitEvent = iota
	ResetEvent
)

var (
	CircuitOpenError       = errors.New("Circuit is open.")
	InvocationTimeoutError = errors.New("Invocation has timed out.")
)

type Breaker struct {
	Events chan CircuitEvent

	circuit func() error

	invocationTimeout float64
	failureThreshold  int
	resetTimeout      float64

	failureCount    int
	hardTrip        bool
	lastFailureTime *time.Time
}

func NewBreaker(circuit func() error) *Breaker {
	return &Breaker{
		Events:            make(chan CircuitEvent),
		circuit:           circuit,
		invocationTimeout: 0.01,
		failureThreshold:  5,
		resetTimeout:      0.1,
	}
}

func (b *Breaker) State() CircuitState {
	if b.failureCount >= b.failureThreshold {
		if time.Now().Sub(*b.lastFailureTime) > time.Duration(b.resetTimeout) {
			return HalfClosedState
		} else {
			return OpenState
		}
	} else {
		return ClosedState
	}
}

func (b *Breaker) Call() error {
	if b.State() == OpenState || b.hardTrip {
		return CircuitOpenError
	}

	if err := b.callWithTimeout(); err != nil {
		b.recordError()
		return err
	}

	b.Reset()
	return nil
}

func (b *Breaker) callWithTimeout() error {
	if b.invocationTimeout == 0 {
		return b.circuit()
	}

	c := make(chan error)
	go func() {
		c <- b.circuit()
		close(c)
	}()

	select {
	case err := <-c:
		return err
	case <-time.After(time.Duration(b.invocationTimeout)):
		return InvocationTimeoutError
	}
}

func (b *Breaker) recordError() {
	now := time.Now()

	b.failureCount++
	b.lastFailureTime = &now
	b.sendEvent(TripEvent)
}

func (b *Breaker) sendEvent(event CircuitEvent) {
	select {
	case b.Events <- event:
	}
}

func (b *Breaker) Trip() {
	b.hardTrip = true
	b.recordError()
}

func (b *Breaker) Reset() {
	b.failureCount = 0
	b.lastFailureTime = nil
	b.hardTrip = false
	b.sendEvent(ResetEvent)
}
<commit_msg>Add probability to half-closed recovery.<commit_after>package overcurrent

import (
	"errors"
	"time"
)

type CircuitState int
type CircuitEvent int

const (
	OpenState CircuitState = iota
	ClosedState
	HalfClosedState

	TripEvent CircuitEvent = iota
	ResetEvent
)

var (
	CircuitOpenError       = errors.New("Circuit is open.")
	InvocationTimeoutError = errors.New("Invocation has timed out.")
)

type Breaker struct {
	Events chan CircuitEvent

	circuit func() error

	invocationTimeout     float64
	failureThreshold      int
	resetTimeout          float64
	halfClosedProbability float64

	failureCount    int
	hardTrip        bool
	lastFailureTime *time.Time
}

func NewBreaker(circuit func() error) *Breaker {
	return &Breaker{
		Events:                make(chan CircuitEvent),
		circuit:               circuit,
		invocationTimeout:     0.01,
		failureThreshold:      5,
		resetTimeout:          0.1,
		halfClosedProbability: 0.5,
	}
}

func (b *Breaker) State() CircuitState {
	if b.failureCount >= b.failureThreshold {
		if time.Now().Sub(*b.lastFailureTime) > time.Duration(b.resetTimeout) {
			return HalfClosedState
		} else {
			return OpenState
		}
	} else {
		return ClosedState
	}
}

func (b *Breaker) Call() error {
	if b.shouldTry() {
		return CircuitOpenError
	}

	if err := b.callWithTimeout(); err != nil {
		b.recordError()
		return err
	}

	b.Reset()
	return nil
}

func (b *Breaker) shouldTry() bool {
	return b.hardTrip || b.State() == OpenState || (b.State() == HalfClosedState && rand.Float32() <= b.halfClosedProbability)
}

func (b *Breaker) callWithTimeout() error {
	if b.invocationTimeout == 0 {
		return b.circuit()
	}

	c := make(chan error)
	go func() {
		c <- b.circuit()
		close(c)
	}()

	select {
	case err := <-c:
		return err
	case <-time.After(time.Duration(b.invocationTimeout)):
		return InvocationTimeoutError
	}
}

func (b *Breaker) recordError() {
	now := time.Now()

	b.failureCount++
	b.lastFailureTime = &now
	b.sendEvent(TripEvent)
}

func (b *Breaker) sendEvent(event CircuitEvent) {
	select {
	case b.Events <- event:
	}
}

func (b *Breaker) Trip() {
	b.hardTrip = true
	b.recordError()
}

func (b *Breaker) Reset() {
	b.failureCount = 0
	b.lastFailureTime = nil
	b.hardTrip = false
	b.sendEvent(ResetEvent)
}
<|endoftext|>