<commit_before>// Copyright 2012 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.

// +build cgo

package runtime_test

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestCgoCrashHandler(t *testing.T) {
	testCrashHandler(t, true)
}

func TestCgoSignalDeadlock(t *testing.T) {
	if testing.Short() && runtime.GOOS == "windows" {
		t.Skip("Skipping in short mode") // takes up to 64 seconds
	}
	got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoTraceback(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "CgoTraceback")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoCallbackGC(t *testing.T) {
	if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	if testing.Short() {
		switch {
		case runtime.GOOS == "dragonfly":
			t.Skip("see golang.org/issue/11990")
		case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
			t.Skip("too slow for arm builders")
		case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
			t.Skip("too slow for mips64x builders")
		}
	}
	got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoExternalThreadPanic(t *testing.T) {
	if runtime.GOOS == "plan9" {
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
	want := "panic: BOOM"
	if !strings.Contains(got, want) {
		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
	}
}

func TestCgoExternalThreadSIGPROF(t *testing.T) {
	// issue 9456.
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("no pthreads on %s", runtime.GOOS)
	case "darwin":
		if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
			// static constructor needs external linking, but we don't support
			// external linking on OS X 10.6.
			out, err := exec.Command("uname", "-r").Output()
			if err != nil {
				t.Fatalf("uname -r failed: %v", err)
			}
			// OS X 10.6 == Darwin 10.x
			if strings.HasPrefix(string(out), "10.") {
				t.Skipf("no external linking on OS X 10.6")
			}
		}
	}
	if runtime.GOARCH == "ppc64" {
		// TODO(austin) External linking not implemented on
		// ppc64 (issue #8912)
		t.Skipf("no external linking on ppc64")
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoExternalThreadSignal(t *testing.T) {
	// issue 10139
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoDLLImports(t *testing.T) {
	// test issue 9356
	if runtime.GOOS != "windows" {
		t.Skip("skipping windows specific test")
	}
	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %v", want, got)
	}
}

func TestCgoExecSignalMask(t *testing.T) {
	// Test issue 13164.
	switch runtime.GOOS {
	case "windows", "plan9":
		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q, got %v", want, got)
	}
}

func TestEnsureDropM(t *testing.T) {
	// Test for issue 13881.
	switch runtime.GOOS {
	case "windows", "plan9":
		t.Skipf("skipping dropm test on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "EnsureDropM")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q, got %v", want, got)
	}
}

// Test for issue 14387.
// Test that the program that doesn't need any cgo pointer checking
// takes about the same amount of time with it as without it.
func TestCgoCheckBytes(t *testing.T) {
	// Make sure we don't count the build time as part of the run time.
	testenv.MustHaveGoBuild(t)
	exe, err := buildTestProg(t, "testprogcgo")
	if err != nil {
		t.Fatal(err)
	}

	// Try it 10 times to avoid flakiness.
	const tries = 10
	var tot1, tot2 time.Duration
	for i := 0; i < tries; i++ {
		cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))

		start := time.Now()
		cmd.Run()
		d1 := time.Since(start)

		cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))

		start = time.Now()
		cmd.Run()
		d2 := time.Since(start)

		if d1*20 > d2 {
			// The slow version (d2) was less than 20 times
			// slower than the fast version (d1), so OK.
			return
		}

		tot1 += d1
		tot2 += d2
	}

	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
}

func TestCgoPanicDeadlock(t *testing.T) {
	// test issue 14432
	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
	want := "panic: cgo error\n\n"
	if !strings.HasPrefix(got, want) {
		t.Fatalf("output does not start with %q:\n%s", want, got)
	}
}

func TestCgoCCodeSIGPROF(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q got %v", want, got)
	}
}

func TestCgoCrashTraceback(t *testing.T) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
	got := runTestProg(t, "testprogcgo", "CrashTraceback")
	for i := 1; i <= 3; i++ {
		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
			t.Errorf("missing cgo symbolizer:%d", i)
		}
	}
}

func TestCgoTracebackContext(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "TracebackContext")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q got %v", want, got)
	}
}

func testCgoPprof(t *testing.T, buildArg, runArg string) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
	testenv.MustHaveGoRun(t)

	exe, err := buildTestProg(t, "testprogcgo", buildArg)
	if err != nil {
		t.Fatal(err)
	}

	got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	fn := strings.TrimSpace(string(got))
	defer os.Remove(fn)

	cmd := testEnv(exec.Command("go", "tool", "pprof", "-top", "-nodecount=1", exe, fn))

	found := false
	for i, e := range cmd.Env {
		if strings.HasPrefix(e, "PPROF_TMPDIR=") {
			cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
			found = true
			break
		}
	}
	if !found {
		cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
	}

	top, err := cmd.CombinedOutput()
	t.Logf("%s", top)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Contains(top, []byte("cpuHog")) {
		t.Error("missing cpuHog in pprof output")
	}
}

func TestCgoPprof(t *testing.T) {
	testCgoPprof(t, "", "CgoPprof")
}

func TestCgoPprofPIE(t *testing.T) {
	testCgoPprof(t, "-ldflags=-extldflags=-pie", "CgoPprof")
}

func TestCgoPprofThread(t *testing.T) {
	testCgoPprof(t, "", "CgoPprofThread")
}
<commit_msg>runtime: disable TestCgoCallbackGC on FreeBSD<commit_after>// Copyright 2012 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.

// +build cgo

package runtime_test

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestCgoCrashHandler(t *testing.T) {
	testCrashHandler(t, true)
}

func TestCgoSignalDeadlock(t *testing.T) {
	if testing.Short() && runtime.GOOS == "windows" {
		t.Skip("Skipping in short mode") // takes up to 64 seconds
	}
	got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoTraceback(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "CgoTraceback")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoCallbackGC(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("no pthreads on %s", runtime.GOOS)
	case "freebsd":
		testenv.SkipFlaky(t, 16396)
	}
	if testing.Short() {
		switch {
		case runtime.GOOS == "dragonfly":
			t.Skip("see golang.org/issue/11990")
		case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
			t.Skip("too slow for arm builders")
		case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
			t.Skip("too slow for mips64x builders")
		}
	}
	got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoExternalThreadPanic(t *testing.T) {
	if runtime.GOOS == "plan9" {
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
	want := "panic: BOOM"
	if !strings.Contains(got, want) {
		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
	}
}

func TestCgoExternalThreadSIGPROF(t *testing.T) {
	// issue 9456.
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("no pthreads on %s", runtime.GOOS)
	case "darwin":
		if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
			// static constructor needs external linking, but we don't support
			// external linking on OS X 10.6.
			out, err := exec.Command("uname", "-r").Output()
			if err != nil {
				t.Fatalf("uname -r failed: %v", err)
			}
			// OS X 10.6 == Darwin 10.x
			if strings.HasPrefix(string(out), "10.") {
				t.Skipf("no external linking on OS X 10.6")
			}
		}
	}
	if runtime.GOARCH == "ppc64" {
		// TODO(austin) External linking not implemented on
		// ppc64 (issue #8912)
		t.Skipf("no external linking on ppc64")
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoExternalThreadSignal(t *testing.T) {
	// issue 10139
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got:\n%s", want, got)
	}
}

func TestCgoDLLImports(t *testing.T) {
	// test issue 9356
	if runtime.GOOS != "windows" {
		t.Skip("skipping windows specific test")
	}
	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %v", want, got)
	}
}

func TestCgoExecSignalMask(t *testing.T) {
	// Test issue 13164.
	switch runtime.GOOS {
	case "windows", "plan9":
		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q, got %v", want, got)
	}
}

func TestEnsureDropM(t *testing.T) {
	// Test for issue 13881.
	switch runtime.GOOS {
	case "windows", "plan9":
		t.Skipf("skipping dropm test on %s", runtime.GOOS)
	}
	got := runTestProg(t, "testprogcgo", "EnsureDropM")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q, got %v", want, got)
	}
}

// Test for issue 14387.
// Test that the program that doesn't need any cgo pointer checking
// takes about the same amount of time with it as without it.
func TestCgoCheckBytes(t *testing.T) {
	// Make sure we don't count the build time as part of the run time.
	testenv.MustHaveGoBuild(t)
	exe, err := buildTestProg(t, "testprogcgo")
	if err != nil {
		t.Fatal(err)
	}

	// Try it 10 times to avoid flakiness.
	const tries = 10
	var tot1, tot2 time.Duration
	for i := 0; i < tries; i++ {
		cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))

		start := time.Now()
		cmd.Run()
		d1 := time.Since(start)

		cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))

		start = time.Now()
		cmd.Run()
		d2 := time.Since(start)

		if d1*20 > d2 {
			// The slow version (d2) was less than 20 times
			// slower than the fast version (d1), so OK.
			return
		}

		tot1 += d1
		tot2 += d2
	}

	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
}

func TestCgoPanicDeadlock(t *testing.T) {
	// test issue 14432
	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
	want := "panic: cgo error\n\n"
	if !strings.HasPrefix(got, want) {
		t.Fatalf("output does not start with %q:\n%s", want, got)
	}
}

func TestCgoCCodeSIGPROF(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q got %v", want, got)
	}
}

func TestCgoCrashTraceback(t *testing.T) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
	got := runTestProg(t, "testprogcgo", "CrashTraceback")
	for i := 1; i <= 3; i++ {
		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
			t.Errorf("missing cgo symbolizer:%d", i)
		}
	}
}

func TestCgoTracebackContext(t *testing.T) {
	got := runTestProg(t, "testprogcgo", "TracebackContext")
	want := "OK\n"
	if got != want {
		t.Errorf("expected %q got %v", want, got)
	}
}

func testCgoPprof(t *testing.T, buildArg, runArg string) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
	testenv.MustHaveGoRun(t)

	exe, err := buildTestProg(t, "testprogcgo", buildArg)
	if err != nil {
		t.Fatal(err)
	}

	got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	fn := strings.TrimSpace(string(got))
	defer os.Remove(fn)

	cmd := testEnv(exec.Command("go", "tool", "pprof", "-top", "-nodecount=1", exe, fn))

	found := false
	for i, e := range cmd.Env {
		if strings.HasPrefix(e, "PPROF_TMPDIR=") {
			cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
			found = true
			break
		}
	}
	if !found {
		cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
	}

	top, err := cmd.CombinedOutput()
	t.Logf("%s", top)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Contains(top, []byte("cpuHog")) {
		t.Error("missing cpuHog in pprof output")
	}
}

func TestCgoPprof(t *testing.T) {
	testCgoPprof(t, "", "CgoPprof")
}

func TestCgoPprofPIE(t *testing.T) {
	testCgoPprof(t, "-ldflags=-extldflags=-pie", "CgoPprof")
}

func TestCgoPprofThread(t *testing.T) {
	testCgoPprof(t, "", "CgoPprofThread")
}
<|endoftext|>


<commit_before>package domain

import (
	"context"
	"encoding/json"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker

//Each method of the ServiceBroker interface maps to an individual endpoint of the Open Service Broker API.
//The specification is available here: https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md
//The OpenAPI documentation is available here: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/openservicebrokerapi/servicebroker/v2.14/openapi.yaml
type ServiceBroker interface {

	// Services gets the catalog of services offered by the service broker
	//   GET /v2/catalog
	Services(ctx context.Context) ([]Service, error)

	// Provision creates a new service instance
	//   PUT /v2/service_instances/{instance_id}
	Provision(ctx context.Context, instanceID string, details ProvisionDetails, asyncAllowed bool) (ProvisionedServiceSpec, error)

	// Deprovision deletes an existing service instance
	//  DELETE /v2/service_instances/{instance_id}
	Deprovision(ctx context.Context, instanceID string, details DeprovisionDetails, asyncAllowed bool) (DeprovisionServiceSpec, error)

	// GetInstance fetches information about a service instance
	//   GET /v2/service_instances/{instance_id}
	GetInstance(ctx context.Context, instanceID string) (GetInstanceDetailsSpec, error)

	// Update modifies an existing service instance
	//  PATCH /v2/service_instances/{instance_id}
	Update(ctx context.Context, instanceID string, details UpdateDetails, asyncAllowed bool) (UpdateServiceSpec, error)

	// LastOperation fetches last operation state for a service instance
	//   GET /v2/service_instances/{instance_id}/last_operation
	LastOperation(ctx context.Context, instanceID string, details PollDetails) (LastOperation, error)

	// Bind creates a new service binding
	//   PUT /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	Bind(ctx context.Context, instanceID, bindingID string, details BindDetails, asyncAllowed bool) (Binding, error)

	// Unbind deletes an existing service binding
	//   DELETE /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	Unbind(ctx context.Context, instanceID, bindingID string, details UnbindDetails, asyncAllowed bool) (UnbindSpec, error)

	// GetBinding fetches an existing service binding
	//   GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	GetBinding(ctx context.Context, instanceID, bindingID string) (GetBindingSpec, error)

	// LastBindingOperation fetches last operation state for a service binding
	//   GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}/last_operation
	LastBindingOperation(ctx context.Context, instanceID, bindingID string, details PollDetails) (LastOperation, error)
}

type LastOperation struct {
	State       LastOperationState `json:"state"`
	Description string             `json:"description"`
}

type LastOperationState string

const (
	InProgress LastOperationState = "in progress"
	Succeeded  LastOperationState = "succeeded"
	Failed     LastOperationState = "failed"
)

type VolumeMount struct {
	Driver       string       `json:"driver"`
	ContainerDir string       `json:"container_dir"`
	Mode         string       `json:"mode"`
	DeviceType   string       `json:"device_type"`
	Device       SharedDevice `json:"device"`
}

type SharedDevice struct {
	VolumeId    string                 `json:"volume_id"`
	MountConfig map[string]interface{} `json:"mount_config"`
}

type ProvisionDetails struct {
	ServiceID        string           `json:"service_id"`
	PlanID           string           `json:"plan_id"`
	OrganizationGUID string           `json:"organization_guid"`
	SpaceGUID        string           `json:"space_guid"`
	RawContext       json.RawMessage  `json:"context,omitempty"`
	RawParameters    json.RawMessage  `json:"parameters,omitempty"`
	MaintenanceInfo  *MaintenanceInfo `json:"maintenance_info,omitempty"`
}

type ProvisionedServiceSpec struct {
	IsAsync       bool
	AlreadyExists bool
	DashboardURL  string
	OperationData string
}

type DeprovisionDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
	Force     bool   `json:"force"`
}

type DeprovisionServiceSpec struct {
	IsAsync       bool
	OperationData string
}

type GetInstanceDetailsSpec struct {
	ServiceID    string      `json:"service_id"`
	PlanID       string      `json:"plan_id"`
	DashboardURL string      `json:"dashboard_url"`
	Parameters   interface{} `json:"parameters"`
}

type UpdateDetails struct {
	ServiceID       string           `json:"service_id"`
	PlanID          string           `json:"plan_id"`
	RawParameters   json.RawMessage  `json:"parameters,omitempty"`
	PreviousValues  PreviousValues   `json:"previous_values"`
	RawContext      json.RawMessage  `json:"context,omitempty"`
	MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"`
}

type PreviousValues struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
	OrgID     string `json:"organization_id"`
	SpaceID   string `json:"space_id"`
}

type UpdateServiceSpec struct {
	IsAsync       bool
	DashboardURL  string
	OperationData string
}

type PollDetails struct {
	ServiceID     string `json:"service_id"`
	PlanID        string `json:"plan_id"`
	OperationData string `json:"operation"`
}

type BindDetails struct {
	AppGUID       string          `json:"app_guid"`
	PlanID        string          `json:"plan_id"`
	ServiceID     string          `json:"service_id"`
	BindResource  *BindResource   `json:"bind_resource,omitempty"`
	RawContext    json.RawMessage `json:"context,omitempty"`
	RawParameters json.RawMessage `json:"parameters,omitempty"`
}

type BindResource struct {
	AppGuid            string `json:"app_guid,omitempty"`
	SpaceGuid          string `json:"space_guid,omitempty"`
	Route              string `json:"route,omitempty"`
	CredentialClientID string `json:"credential_client_id,omitempty"`
}

type UnbindDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type UnbindSpec struct {
	IsAsync       bool
	OperationData string
}

type Binding struct {
	IsAsync         bool          `json:"is_async"`
	AlreadyExists   bool          `json:"already_exists"`
	OperationData   string        `json:"operation_data"`
	Credentials     interface{}   `json:"credentials"`
	SyslogDrainURL  string        `json:"syslog_drain_url"`
	RouteServiceURL string        `json:"route_service_url"`
	VolumeMounts    []VolumeMount `json:"volume_mounts"`
}

type GetBindingSpec struct {
	Credentials     interface{}
	SyslogDrainURL  string
	RouteServiceURL string
	VolumeMounts    []VolumeMount
	Parameters      interface{}
}

func (d ProvisionDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d ProvisionDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

func (d BindDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d BindDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

func (d UpdateDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d UpdateDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

type DetailsWithRawParameters interface {
	GetRawParameters() json.RawMessage
}

type DetailsWithRawContext interface {
	GetRawContext() json.RawMessage
}
<commit_msg>Add maintenance info to previous values<commit_after>package domain

import (
	"context"
	"encoding/json"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker

//Each method of the ServiceBroker interface maps to an individual endpoint of the Open Service Broker API.
//The specification is available here: https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md
//The OpenAPI documentation is available here: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/openservicebrokerapi/servicebroker/v2.14/openapi.yaml
type ServiceBroker interface {

	// Services gets the catalog of services offered by the service broker
	//   GET /v2/catalog
	Services(ctx context.Context) ([]Service, error)

	// Provision creates a new service instance
	//   PUT /v2/service_instances/{instance_id}
	Provision(ctx context.Context, instanceID string, details ProvisionDetails, asyncAllowed bool) (ProvisionedServiceSpec, error)

	// Deprovision deletes an existing service instance
	//  DELETE /v2/service_instances/{instance_id}
	Deprovision(ctx context.Context, instanceID string, details DeprovisionDetails, asyncAllowed bool) (DeprovisionServiceSpec, error)

	// GetInstance fetches information about a service instance
	//   GET /v2/service_instances/{instance_id}
	GetInstance(ctx context.Context, instanceID string) (GetInstanceDetailsSpec, error)

	// Update modifies an existing service instance
	//  PATCH /v2/service_instances/{instance_id}
	Update(ctx context.Context, instanceID string, details UpdateDetails, asyncAllowed bool) (UpdateServiceSpec, error)

	// LastOperation fetches last operation state for a service instance
	//   GET /v2/service_instances/{instance_id}/last_operation
	LastOperation(ctx context.Context, instanceID string, details PollDetails) (LastOperation, error)

	// Bind creates a new service binding
	//   PUT /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	Bind(ctx context.Context, instanceID, bindingID string, details BindDetails, asyncAllowed bool) (Binding, error)

	// Unbind deletes an existing service binding
	//   DELETE /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	Unbind(ctx context.Context, instanceID, bindingID string, details UnbindDetails, asyncAllowed bool) (UnbindSpec, error)

	// GetBinding fetches an existing service binding
	//   GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}
	GetBinding(ctx context.Context, instanceID, bindingID string) (GetBindingSpec, error)

	// LastBindingOperation fetches last operation state for a service binding
	//   GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}/last_operation
	LastBindingOperation(ctx context.Context, instanceID, bindingID string, details PollDetails) (LastOperation, error)
}

type LastOperation struct {
	State       LastOperationState `json:"state"`
	Description string             `json:"description"`
}

type LastOperationState string

const (
	InProgress LastOperationState = "in progress"
	Succeeded  LastOperationState = "succeeded"
	Failed     LastOperationState = "failed"
)

type VolumeMount struct {
	Driver       string       `json:"driver"`
	ContainerDir string       `json:"container_dir"`
	Mode         string       `json:"mode"`
	DeviceType   string       `json:"device_type"`
	Device       SharedDevice `json:"device"`
}

type SharedDevice struct {
	VolumeId    string                 `json:"volume_id"`
	MountConfig map[string]interface{} `json:"mount_config"`
}

type ProvisionDetails struct {
	ServiceID        string           `json:"service_id"`
	PlanID           string           `json:"plan_id"`
	OrganizationGUID string           `json:"organization_guid"`
	SpaceGUID        string           `json:"space_guid"`
	RawContext       json.RawMessage  `json:"context,omitempty"`
	RawParameters    json.RawMessage  `json:"parameters,omitempty"`
	MaintenanceInfo  *MaintenanceInfo `json:"maintenance_info,omitempty"`
}

type ProvisionedServiceSpec struct {
	IsAsync       bool
	AlreadyExists bool
	DashboardURL  string
	OperationData string
}

type DeprovisionDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
	Force     bool   `json:"force"`
}

type DeprovisionServiceSpec struct {
	IsAsync       bool
	OperationData string
}

type GetInstanceDetailsSpec struct {
	ServiceID    string      `json:"service_id"`
	PlanID       string      `json:"plan_id"`
	DashboardURL string      `json:"dashboard_url"`
	Parameters   interface{} `json:"parameters"`
}

type UpdateDetails struct {
	ServiceID       string           `json:"service_id"`
	PlanID          string           `json:"plan_id"`
	RawParameters   json.RawMessage  `json:"parameters,omitempty"`
	PreviousValues  PreviousValues   `json:"previous_values"`
	RawContext      json.RawMessage  `json:"context,omitempty"`
	MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"`
}

type PreviousValues struct {
	PlanID          string           `json:"plan_id"`
	ServiceID       string           `json:"service_id"`
	OrgID           string           `json:"organization_id"`
	SpaceID         string           `json:"space_id"`
	MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"`
}

type UpdateServiceSpec struct {
	IsAsync       bool
	DashboardURL  string
	OperationData string
}

type PollDetails struct {
	ServiceID     string `json:"service_id"`
	PlanID        string `json:"plan_id"`
	OperationData string `json:"operation"`
}

type BindDetails struct {
	AppGUID       string          `json:"app_guid"`
	PlanID        string          `json:"plan_id"`
	ServiceID     string          `json:"service_id"`
	BindResource  *BindResource   `json:"bind_resource,omitempty"`
	RawContext    json.RawMessage `json:"context,omitempty"`
	RawParameters json.RawMessage `json:"parameters,omitempty"`
}

type BindResource struct {
	AppGuid            string `json:"app_guid,omitempty"`
	SpaceGuid          string `json:"space_guid,omitempty"`
	Route              string `json:"route,omitempty"`
	CredentialClientID string `json:"credential_client_id,omitempty"`
}

type UnbindDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type UnbindSpec struct {
	IsAsync       bool
	OperationData string
}

type Binding struct {
	IsAsync         bool          `json:"is_async"`
	AlreadyExists   bool          `json:"already_exists"`
	OperationData   string        `json:"operation_data"`
	Credentials     interface{}   `json:"credentials"`
	SyslogDrainURL  string        `json:"syslog_drain_url"`
	RouteServiceURL string        `json:"route_service_url"`
	VolumeMounts    []VolumeMount `json:"volume_mounts"`
}

type GetBindingSpec struct {
	Credentials     interface{}
	SyslogDrainURL  string
	RouteServiceURL string
	VolumeMounts    []VolumeMount
	Parameters      interface{}
}

func (d ProvisionDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d ProvisionDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

func (d BindDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d BindDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

func (d UpdateDetails) GetRawContext() json.RawMessage {
	return d.RawContext
}

func (d UpdateDetails) GetRawParameters() json.RawMessage {
	return d.RawParameters
}

type DetailsWithRawParameters interface {
	GetRawParameters() json.RawMessage
}

type DetailsWithRawContext interface {
	GetRawContext() json.RawMessage
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

package flag

/*
 * Flags
 *
 * Usage:
 *	1) Define flags using flag.String(), Bool(), or Int(). Int flag values have type int64. Example:
 *		import flag "flag"
 *		var i int64
 *		var fi *flag.Flag = flag.Int("flagname", 1234, &i, "help message for flagname")
 *	The pointer may be nil; if non-nil, it points to a cell of the appropriate type to store the
 *	flag's value.
 *
 *	2) After all flags are defined, call
 *		flag.Parse()
 *	to parse the command line into the defined flags.
 *
 *	3) Flags may then be used directly (getters are SVal, BVal, Ival) or through the associated
 *	cell, if set:
 *		print("fi has value ", fi.IVal(), "\n");
 *		print("i has value ", i, "\n");
 *
 *	4) After parsing, flag.Arg(i) is the i'th argument after the flags.
 *	Args are indexed from 0 up to flag.NArg().
 *
 *	Command line flag syntax:
 *		-flag
 *		-flag=x
 *		-flag x
 *	One or two minus signs may be used; they are equivalent.
 *	A lone -- terminates the parsing of the command line.
 *	Integer flags accept 1234, 0664, 0x1234 and may be negative.
 *	Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
 */

import fmt "fmt"

//export Bool, Int, String
//export Arg, NArg
//export Parse
//export Flag.BVal BUG: variable exported but not defined: Flag.BVal
//export Flag.SVal BUG: variable exported but not defined: Flag.SVal
//export Flag

// BUG: ctoi, atoi, atob belong elsewhere
func ctoi(c int64) int64 {
	if '0' <= c && c <= '9' {
		return c - '0'
	}
	if 'a' <= c && c <= 'f' {
		return c - 'a'
	}
	if 'A' <= c && c <= 'F' {
		return c - 'A'
	}
	return 1000   // too large for any base
}

func atoi(s string) (value int64, ok bool) {
	if len(s) == 0 {
		return 0, false
	}
	if s[0] == '-' {
		n, t := atoi(s[1:len(s)]);
		return -n, t
	}
	var base int64 = 10;
	i := 0;
	if s[0] == '0' {
		base = 8;
		if len(s) > 1 && (s[1] == 'x' || s[1] == 'X') {
			base = 16;
			i += 2;
		}
	}
	var n int64 = 0;
	for ; i < len(s); i++ {
		k := ctoi(int64(s[i]));
		if k >= base {
			return 0, false
		}
		n = n * base + k
	}
	return n, true
}

func atob(str string) (value bool, ok bool) {
	switch str {
		case "1", "t", "T", "true", "TRUE", "True":
			return true, true;
		case "0", "f", "F", "false", "FALSE", "False":
			return false, true
	}
	return false, false
}

type (
	BoolValue struct;
	IntValue struct;
	StringValue struct;
)

// -- Bool Value
type BoolValue struct {
	val bool;
	p *bool;
}

func NewBoolValue(val bool, p *bool) *BoolValue {
	if p != nil {
		*p = val
	}
	return &BoolValue{val, p}
}

func (b *BoolValue) AsBool() *BoolValue {
	return b
}

func (b *BoolValue) AsInt() *IntValue {
	return nil
}

func (b *BoolValue) AsString() *StringValue {
	return nil
}

func (b *BoolValue) IsBool() bool {
	return true
}

func (b *BoolValue) IsInt() bool {
	return false
}

func (b *BoolValue) IsString() bool {
	return false
}

func (b *BoolValue) ValidValue(str string) bool {
	i, ok := atob(str);
	return ok;
}

func (b *BoolValue) Set(val bool) {
	if b.p != nil {
		*b.p = val
	}
	b.val = val
}

func (b *BoolValue) Str() string {
	if b.val {
		return "true"
	}
	return "false"
}

// -- Int Value
type IntValue struct {
	val	int64;
	p	*int64;
}

func NewIntValue(val int64, p *int64) *IntValue {
	if p != nil {
		*p = val
	}
	return &IntValue{val, p}
}

func (i *IntValue) AsBool() *BoolValue {
	return nil
}

func (i *IntValue) AsInt() *IntValue {
	return i
}

func (i *IntValue) AsString() *StringValue{
	return nil
}

func (i *IntValue) IsBool() bool {
	return false
}

func (i *IntValue) IsInt() bool {
	return true
}

func (i *IntValue) IsString() bool {
	return false
}

func (i *IntValue) ValidValue(str string) bool {
	k, ok := atoi(str);
	return ok;
}

func (i *IntValue) Set(val int64) {
	if i.p != nil {
		*i.p = val
	}
	i.val = val
}

func (i *IntValue) Str() string {
	return fmt.New().d64(i.val).str()
}

// -- String Value
type StringValue struct {
	val	string;
	p	*string;
}

func NewStringValue(val string, p *string) *StringValue {
	if p != nil {
		*p = val
	}
	return &StringValue{val, p}
}

func (e *StringValue) AsBool() *BoolValue {
	return nil
}

func (e *StringValue) AsInt() *IntValue {
	return nil
}

func (s *StringValue) AsString() *StringValue{
	return s
}

func (s *StringValue) IsBool() bool {
	return false
}

func (s *StringValue) IsInt() bool {
	return false
}

func (s *StringValue) IsString() bool {
	return true
}

func (s *StringValue) ValidValue(str string) bool {
	return true
}

func (s *StringValue) Set(val string) {
	if s.p != nil {
		*s.p = val
	}
	s.val = val
}

func (s *StringValue) Str() string {
	return `"` + s.val + `"`
}

// -- Value interface
type Value interface {
	AsBool()	*BoolValue;
	AsInt()	*IntValue;
	AsString()	*StringValue;
	IsBool()	bool;
	IsInt()	bool;
	IsString()	bool;
	Str()		string;
	ValidValue(str string) bool;
}

// -- Flag structure (internal)
export type Flag struct {
	name	string;
	usage	string;
	value	Value;
	next	*Flag;  // BUG: remove when we can iterate over maps
}

type Flags struct {
	actual *map[string] *Flag;
	formal *map[string] *Flag;
	first_arg	int;
	flag_list	*Flag;  // BUG: remove when we can iterate over maps
}

// --Customer's value getters
func (f *Flag) BVal() bool {
	if !f.value.IsBool() {
		return false;
	}
	return f.value.AsBool().val;
}

func (f *Flag) IVal() int64 {
	if !f.value.IsInt() {
		return 0
	}
	return f.value.AsInt().val;
}

func (f *Flag) SVal() string {
	if !f.value.IsString() {
		return "???";
	}
	return f.value.AsString().val;
}

func New() *Flags {
	f := new(Flags);
	f.first_arg = 1;	// 0 is the program name, 1 is first arg
	f.actual = new(map[string] *Flag);
	f.formal = new(map[string] *Flag);
	return f;
}

var flags *Flags = New();

export func PrintDefaults() {
	// BUG: use map iteration when available
	for f := flags.flag_list; f != nil; f = f.next {
		print("  -", f.name, "=", f.value.Str(), ": ", f.usage, "\n");
	}
}

export func Usage() {
	if sys.argc() > 0 {
		print("Usage of ", sys.argv(0), ": \n");
	} else {
		print("Usage: \n");
	}
	PrintDefaults();
	sys.exit(1);
}

export func NFlag() int {
	return len(flags.actual)
}

export func Arg(i int) string {
	i += flags.first_arg;
	if i < 0 || i >= sys.argc() {
		return "";
	}
	return sys.argv(i)
}

export func NArg() int {
	return sys.argc() - flags.first_arg
}

func Add(name string, value Value, usage string) *Flag {
	f := new(Flag);
	f.name = name;
	f.usage = usage;
	f.value = value;
	dummy, alreadythere := flags.formal[name];
	if alreadythere {
		print("flag redefined: ", name, "\n");
		panic("flag redefinition");
	}
	flags.formal[name] = f;
	f.next = flags.flag_list;  // BUG: remove when we can iterate over maps
	flags.flag_list = f;  // BUG: remove when we can iterate over maps
	return f;
}

export func Bool(name string, value bool, p *bool, usage string) *Flag {
	return Add(name, NewBoolValue(value, p), usage);
}

export func Int(name string, value int64, p *int64, usage string) *Flag {
	return Add(name, NewIntValue(value, p), usage);
}

export func String(name, value string, p *string, usage string) *Flag {
	return Add(name, NewStringValue(value, p), usage);
}

func (f *Flags) ParseOne(index int) (ok bool, next int)
{
	s := sys.argv(index);
	f.first_arg = index;  // until proven otherwise
	if len(s) == 0 {
		return false, -1
	}
	if s[0] != '-' {
		return false, -1
	}
	num_minuses := 1;
	if len(s) == 1 {
		return false, -1
	}
	if s[1] == '-' {
		num_minuses++;
		if len(s) == 2 {	// "--" terminates the flags
			return false, index + 1
		}
	}
	name := s[num_minuses : len(s)];
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		print("bad flag syntax: ", s, "\n");
		Usage();
	}

	// it's a flag. does it have an argument?
	has_value := false;
	value := "";
	for i := 1; i < len(name); i++ {  // equals cannot be first
		if name[i] == '=' {
			value = name[i+1 : len(name)];
			has_value = true;
			name = name[0 : i];
			break;
		}
	}
	flag, alreadythere := flags.actual[name];
	if alreadythere {
		print("flag specified twice: -", name, "\n");
		Usage();
	}
	m := flags.formal;
	flag, alreadythere = m[name]; // BUG
	if !alreadythere {
		print("flag provided but not defined: -", name, "\n");
		Usage();
	}
	if !has_value && index < sys.argc()-1 && flag.value.ValidValue(sys.argv(index+1)) {
		// value is the next arg
		has_value = true;
		index++;
		value = sys.argv(index);
	}
	switch {
		case flag.value.IsBool():
			if has_value {
				k, ok := atob(value);
				if !ok {
					print("invalid boolean value ", value, " for flag: -", name, "\n");
					Usage();
				}
				flag.value.AsBool().Set(k)
			} else {
				flag.value.AsBool().Set(true)
			}
		case flag.value.IsInt():
			if !has_value {
				print("flag needs an argument: -", name, "\n");
				Usage();
			}
			k, ok := atoi(value);
			if !ok {
				print("invalid integer value ", value, " for flag: -", name, "\n");
				Usage();
			}
			flag.value.AsInt().Set(k);
		case flag.value.IsString():
			if !has_value {
				print("flag needs an argument: -", name, "\n");
				Usage();
			}
			flag.value.AsString().Set(value)
	}
	flags.actual[name] = flag;
	return true, index + 1
}

export func Parse() {
	for i := 1; i < sys.argc();  {
		ok, next := flags.ParseOne(i);
		if next > 0 {
			flags.first_arg = next;
			i = next;
		}
		if !ok {
			break
		}
	}
}
<commit_msg>stop flag reading before lone - arg.<commit_after>// Copyright 2009 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.

package flag

/*
 * Flags
 *
 * Usage:
 *	1) Define flags using flag.String(), Bool(), or Int(). Int flag values have type int64. Example:
 *		import flag "flag"
 *		var i int64
 *		var fi *flag.Flag = flag.Int("flagname", 1234, &i, "help message for flagname")
 *	The pointer may be nil; if non-nil, it points to a cell of the appropriate type to store the
 *	flag's value.
 *
 *	2) After all flags are defined, call
 *		flag.Parse()
 *	to parse the command line into the defined flags.
 *
 *	3) Flags may then be used directly (getters are SVal, BVal, Ival) or through the associated
 *	cell, if set:
 *		print("fi has value ", fi.IVal(), "\n");
 *		print("i has value ", i, "\n");
 *
 *	4) After parsing, flag.Arg(i) is the i'th argument after the flags.
 *	Args are indexed from 0 up to flag.NArg().
 *
 *	Command line flag syntax:
 *		-flag
 *		-flag=x
 *		-flag x
 *	One or two minus signs may be used; they are equivalent.
 *
 *	Flag parsing stops just before the first non-flag argument
 *	("-" is a non-flag argument) or after the terminator "--".
 *
 *	Integer flags accept 1234, 0664, 0x1234 and may be negative.
 *	Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
 */

import fmt "fmt"

//export Bool, Int, String
//export Arg, NArg
//export Parse
//export Flag.BVal BUG: variable exported but not defined: Flag.BVal
//export Flag.SVal BUG: variable exported but not defined: Flag.SVal
//export Flag

// BUG: ctoi, atoi, atob belong elsewhere
func ctoi(c int64) int64 {
	if '0' <= c && c <= '9' {
		return c - '0'
	}
	if 'a' <= c && c <= 'f' {
		return c - 'a'
	}
	if 'A' <= c && c <= 'F' {
		return c - 'A'
	}
	return 1000   // too large for any base
}

func atoi(s string) (value int64, ok bool) {
	if len(s) == 0 {
		return 0, false
	}
	if s[0] == '-' {
		n, t := atoi(s[1:len(s)]);
		return -n, t
	}
	var base int64 = 10;
	i := 0;
	if s[0] == '0' {
		base = 8;
		if len(s) > 1 && (s[1] == 'x' || s[1] == 'X') {
			base = 16;
			i += 2;
		}
	}
	var n int64 = 0;
	for ; i < len(s); i++ {
		k := ctoi(int64(s[i]));
		if k >= base {
			return 0, false
		}
		n = n * base + k
	}
	return n, true
}

func atob(str string) (value bool, ok bool) {
	switch str {
		case "1", "t", "T", "true", "TRUE", "True":
			return true, true;
		case "0", "f", "F", "false", "FALSE", "False":
			return false, true
	}
	return false, false
}

type (
	BoolValue struct;
	IntValue struct;
	StringValue struct;
)

// -- Bool Value
type BoolValue struct {
	val bool;
	p *bool;
}

func NewBoolValue(val bool, p *bool) *BoolValue {
	if p != nil {
		*p = val
	}
	return &BoolValue{val, p}
}

func (b *BoolValue) AsBool() *BoolValue {
	return b
}

func (b *BoolValue) AsInt() *IntValue {
	return nil
}

func (b *BoolValue) AsString() *StringValue {
	return nil
}

func (b *BoolValue) IsBool() bool {
	return true
}

func (b *BoolValue) IsInt() bool {
	return false
}

func (b *BoolValue) IsString() bool {
	return false
}

func (b *BoolValue) ValidValue(str string) bool {
	i, ok := atob(str);
	return ok;
}

func (b *BoolValue) Set(val bool) {
	if b.p != nil {
		*b.p = val
	}
	b.val = val
}

func (b *BoolValue) Str() string {
	if b.val {
		return "true"
	}
	return "false"
}

// -- Int Value
type IntValue struct {
	val	int64;
	p	*int64;
}

func NewIntValue(val int64, p *int64) *IntValue {
	if p != nil {
		*p = val
	}
	return &IntValue{val, p}
}

func (i *IntValue) AsBool() *BoolValue {
	return nil
}

func (i *IntValue) AsInt() *IntValue {
	return i
}

func (i *IntValue) AsString() *StringValue{
	return nil
}

func (i *IntValue) IsBool() bool {
	return false
}

func (i *IntValue) IsInt() bool {
	return true
}

func (i *IntValue) IsString() bool {
	return false
}

func (i *IntValue) ValidValue(str string) bool {
	k, ok := atoi(str);
	return ok;
}

func (i *IntValue) Set(val int64) {
	if i.p != nil {
		*i.p = val
	}
	i.val = val
}

func (i *IntValue) Str() string {
	return fmt.New().d64(i.val).str()
}

// -- String Value
type StringValue struct {
	val	string;
	p	*string;
}

func NewStringValue(val string, p *string) *StringValue {
	if p != nil {
		*p = val
	}
	return &StringValue{val, p}
}

func (e *StringValue) AsBool() *BoolValue {
	return nil
}

func (e *StringValue) AsInt() *IntValue {
	return nil
}

func (s *StringValue) AsString() *StringValue{
	return s
}

func (s *StringValue) IsBool() bool {
	return false
}

func (s *StringValue) IsInt() bool {
	return false
}

func (s *StringValue) IsString() bool {
	return true
}

func (s *StringValue) ValidValue(str string) bool {
	return true
}

func (s *StringValue) Set(val string) {
	if s.p != nil {
		*s.p = val
	}
	s.val = val
}

func (s *StringValue) Str() string {
	return `"` + s.val + `"`
}

// -- Value interface
type Value interface {
	AsBool()	*BoolValue;
	AsInt()	*IntValue;
	AsString()	*StringValue;
	IsBool()	bool;
	IsInt()	bool;
	IsString()	bool;
	Str()		string;
	ValidValue(str string) bool;
}

// -- Flag structure (internal)
export type Flag struct {
	name	string;
	usage	string;
	value	Value;
	next	*Flag;  // BUG: remove when we can iterate over maps
}

type Flags struct {
	actual *map[string] *Flag;
	formal *map[string] *Flag;
	first_arg	int;
	flag_list	*Flag;  // BUG: remove when we can iterate over maps
}

// --Customer's value getters
func (f *Flag) BVal() bool {
	if !f.value.IsBool() {
		return false;
	}
	return f.value.AsBool().val;
}

func (f *Flag) IVal() int64 {
	if !f.value.IsInt() {
		return 0
	}
	return f.value.AsInt().val;
}

func (f *Flag) SVal() string {
	if !f.value.IsString() {
		return "???";
	}
	return f.value.AsString().val;
}

func New() *Flags {
	f := new(Flags);
	f.first_arg = 1;	// 0 is the program name, 1 is first arg
	f.actual = new(map[string] *Flag);
	f.formal = new(map[string] *Flag);
	return f;
}

var flags *Flags = New();

export func PrintDefaults() {
	// BUG: use map iteration when available
	for f := flags.flag_list; f != nil; f = f.next {
		print("  -", f.name, "=", f.value.Str(), ": ", f.usage, "\n");
	}
}

export func Usage() {
	if sys.argc() > 0 {
		print("Usage of ", sys.argv(0), ": \n");
	} else {
		print("Usage: \n");
	}
	PrintDefaults();
	sys.exit(1);
}

export func NFlag() int {
	return len(flags.actual)
}

export func Arg(i int) string {
	i += flags.first_arg;
	if i < 0 || i >= sys.argc() {
		return "";
	}
	return sys.argv(i)
}

export func NArg() int {
	return sys.argc() - flags.first_arg
}

func Add(name string, value Value, usage string) *Flag {
	f := new(Flag);
	f.name = name;
	f.usage = usage;
	f.value = value;
	dummy, alreadythere := flags.formal[name];
	if alreadythere {
		print("flag redefined: ", name, "\n");
		panic("flag redefinition");
	}
	flags.formal[name] = f;
	f.next = flags.flag_list;  // BUG: remove when we can iterate over maps
	flags.flag_list = f;  // BUG: remove when we can iterate over maps
	return f;
}

export func Bool(name string, value bool, p *bool, usage string) *Flag {
	return Add(name, NewBoolValue(value, p), usage);
}

export func Int(name string, value int64, p *int64, usage string) *Flag {
	return Add(name, NewIntValue(value, p), usage);
}

export func String(name, value string, p *string, usage string) *Flag {
	return Add(name, NewStringValue(value, p), usage);
}

func (f *Flags) ParseOne(index int) (ok bool, next int)
{
	s := sys.argv(index);
	f.first_arg = index;  // until proven otherwise
	if len(s) == 0 {
		return false, -1
	}
	if s[0] != '-' {
		return false, -1
	}
	num_minuses := 1;
	if len(s) == 1 {
		return false, index
	}
	if s[1] == '-' {
		num_minuses++;
		if len(s) == 2 {	// "--" terminates the flags
			return false, index + 1
		}
	}
	name := s[num_minuses : len(s)];
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		print("bad flag syntax: ", s, "\n");
		Usage();
	}

	// it's a flag. does it have an argument?
	has_value := false;
	value := "";
	for i := 1; i < len(name); i++ {  // equals cannot be first
		if name[i] == '=' {
			value = name[i+1 : len(name)];
			has_value = true;
			name = name[0 : i];
			break;
		}
	}
	flag, alreadythere := flags.actual[name];
	if alreadythere {
		print("flag specified twice: -", name, "\n");
		Usage();
	}
	m := flags.formal;
	flag, alreadythere = m[name]; // BUG
	if !alreadythere {
		print("flag provided but not defined: -", name, "\n");
		Usage();
	}
	if !has_value && index < sys.argc()-1 && flag.value.ValidValue(sys.argv(index+1)) {
		// value is the next arg
		has_value = true;
		index++;
		value = sys.argv(index);
	}
	switch {
		case flag.value.IsBool():
			if has_value {
				k, ok := atob(value);
				if !ok {
					print("invalid boolean value ", value, " for flag: -", name, "\n");
					Usage();
				}
				flag.value.AsBool().Set(k)
			} else {
				flag.value.AsBool().Set(true)
			}
		case flag.value.IsInt():
			if !has_value {
				print("flag needs an argument: -", name, "\n");
				Usage();
			}
			k, ok := atoi(value);
			if !ok {
				print("invalid integer value ", value, " for flag: -", name, "\n");
				Usage();
			}
			flag.value.AsInt().Set(k);
		case flag.value.IsString():
			if !has_value {
				print("flag needs an argument: -", name, "\n");
				Usage();
			}
			flag.value.AsString().Set(value)
	}
	flags.actual[name] = flag;
	return true, index + 1
}

export func Parse() {
	for i := 1; i < sys.argc();  {
		ok, next := flags.ParseOne(i);
		if next > 0 {
			flags.first_arg = next;
			i = next;
		}
		if !ok {
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"strconv"

	"time"

	"github.com/jantb/tcell"
	"github.com/jantb/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
	backing = make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	evName := ""
	go func() {
		for {
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			t := time.Now()
			drawBuffer(topRow, s, offset, lines)
			time2 := time.Now().Sub(t).String()

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)
			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}

			case *tcell.EventResize:
				s.Sync()
				w, h := s.Size()
				width = w
				height = h
				backing = make([][]Backing, h-1, h-1)
				for i := range backing {
					backing[i] = make([]Backing, w, w)
				}
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var backing = [][]Backing{}

func drawBuffer(topRow int, s tcell.Screen, offset int, lines [][]rune) {
	var style = getThemeColor("editor")
	c := GetCursor()
	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		s := syntax(lines[i], buffer.filename)
		for o, r := range lines[i] {
			backing[i][jj].value = r
			backing[i][jj].style = s[o].style
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}

	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}

	re := []rune{}
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', re, column.style)
				s.SetContent(x+1, ir, ' ', re, column.style)
				s.SetContent(x+2, ir, ' ', re, column.style)
				s.SetContent(x+3, ir, ' ', re, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<commit_msg>caching of syntax<commit_after>package main

import (
	"fmt"
	"os"

	"strconv"

	"time"

	"github.com/golang/groupcache/lru"
	"github.com/jantb/tcell"
	"github.com/jantb/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
	backing = make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	evName := ""
	go func() {
		for {
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			t := time.Now()
			drawBuffer(topRow, s, offset, lines)
			time2 := time.Now().Sub(t).String()

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)
			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}

			case *tcell.EventResize:
				s.Sync()
				w, h := s.Size()
				width = w
				height = h
				backing = make([][]Backing, h-1, h-1)
				for i := range backing {
					backing[i] = make([]Backing, w, w)
				}
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var backing = [][]Backing{}
var cache = lru.New(1000)

func drawBuffer(topRow int, s tcell.Screen, offset int, lines [][]rune) {
	var style = getThemeColor("editor")
	c := GetCursor()
	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		key := string(lines[i])
		s := []Backing{}
		if val, ok := cache.Get(key); !ok {
			val := syntax(lines[i], buffer.filename)
			cache.Add(key, val)
			s = val
		} else {
			s = val.([]Backing)
		}

		for o, r := range lines[i] {
			backing[i][jj].value = r
			backing[i][jj].style = s[o].style
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}

	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}

	re := []rune{}
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', re, column.style)
				s.SetContent(x+1, ir, ' ', re, column.style)
				s.SetContent(x+2, ir, ' ', re, column.style)
				s.SetContent(x+3, ir, ' ', re, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<|endoftext|>


<commit_before>package kafka

import (
	"errors"
	"fmt"
	"hash/fnv"
	"math/rand"
	"sync"
	"time"

	"github.com/optiopay/kafka/proto"
)

// DistributingProducer is the interface similar to Producer, but never require
// to explicitly specify partition.
//
// Distribute writes messages to the given topic, automatically choosing
// partition, returning the post-commit offset and any error encountered. The
// offset of each message is also updated accordingly.
type DistributingProducer interface {
	Distribute(topic string, messages ...*proto.Message) (offset int64, err error)
}

type randomProducer struct {
	rand       *rand.Rand
	producer   Producer
	partitions int32
}

// NewRandomProducer wraps given producer and return DistributingProducer that
// publish messages to kafka, randomly picking partition number from range
// [0, numPartitions)
func NewRandomProducer(p Producer, numPartitions int32) DistributingProducer {
	return &randomProducer{
		rand:       rand.New(rand.NewSource(time.Now().UnixNano())),
		producer:   p,
		partitions: numPartitions,
	}
}

// Distribute write messages to given kafka topic, randomly destination choosing
// partition. All messages written within single Produce call are atomically
// written to the same destination.
func (p *randomProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	part := 0
	if p.partitions > 0 {
		part = p.rand.Intn(int(p.partitions))
	}
	return p.producer.Produce(topic, int32(part), messages...)
}

type roundRobinProducer struct {
	producer   Producer
	partitions int32
	mu         sync.Mutex
	next       int32
}

// NewRoundRobinProducer wraps given producer and return DistributingProducer
// that publish messages to kafka, choosing destination partition from cycle
// build from [0, numPartitions) range.
func NewRoundRobinProducer(p Producer, numPartitions int32) DistributingProducer {
	return &roundRobinProducer{
		producer:   p,
		partitions: numPartitions,
		next:       0,
	}
}

// Distribute write messages to given kafka topic, choosing next destination
// partition from internal cycle. All messages written within single Produce
// call are atomically written to the same destination.
func (p *roundRobinProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	p.mu.Lock()
	part := p.next
	p.next++
	if p.next >= p.partitions {
		p.next = 0
	}
	p.mu.Unlock()

	return p.producer.Produce(topic, int32(part), messages...)
}

type hashProducer struct {
	producer   Producer
	partitions int32
}

// NewHashProducer wraps given producer and return DistributingProducer that
// publish messages to kafka, computing partition number from message key hash,
// using fnv hash and [0, numPartitions) range.
func NewHashProducer(p Producer, numPartitions int32) DistributingProducer {
	return &hashProducer{
		producer:   p,
		partitions: numPartitions,
	}
}

// Distribute write messages to given kafka topic, computing partition number from
// the message key value. Message key must be not nil and all messages written
// within single Produce call are atomically written to the same destination.
//
// All messages passed within single Produce call must hash to the same
// destination, otherwise no message is written and error is returned.
func (p *hashProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	if len(messages) == 0 {
		return 0, errors.New("no messages")
	}
	part, err := p.messagePartition(messages[0])
	if err != nil {
		return 0, fmt.Errorf("cannot hash message: %s", err)
	}
	// make sure that all messages within single call are to the same destination
	for i := 2; i < len(messages); i++ {
		mp, err := p.messagePartition(messages[i])
		if err != nil {
			return 0, fmt.Errorf("cannot hash message: %s", err)
		}
		if part != mp {
			return 0, errors.New("cannot publish messages to different destinations")
		}
	}

	return p.producer.Produce(topic, part, messages...)
}

// messagePartition compute message's key hash and return corresponding
// partition number.
func (p *hashProducer) messagePartition(m *proto.Message) (int32, error) {
	if m.Key == nil {
		return 0, errors.New("no key")
	}
	hasher := fnv.New32a()
	if _, err := hasher.Write(m.Key); err != nil {
		return 0, fmt.Errorf("cannot hash key: %s", err)
	}
	sum := int32(hasher.Sum32())
	if sum < 0 {
		sum = -sum
	}
	return sum % p.partitions, nil
}
<commit_msg>Add comment to code<commit_after>package kafka

import (
	"errors"
	"fmt"
	"hash/fnv"
	"math/rand"
	"sync"
	"time"

	"github.com/optiopay/kafka/proto"
)

// DistributingProducer is the interface similar to Producer, but never require
// to explicitly specify partition.
//
// Distribute writes messages to the given topic, automatically choosing
// partition, returning the post-commit offset and any error encountered. The
// offset of each message is also updated accordingly.
type DistributingProducer interface {
	Distribute(topic string, messages ...*proto.Message) (offset int64, err error)
}

type randomProducer struct {
	rand       *rand.Rand
	producer   Producer
	partitions int32
}

// NewRandomProducer wraps given producer and return DistributingProducer that
// publish messages to kafka, randomly picking partition number from range
// [0, numPartitions)
func NewRandomProducer(p Producer, numPartitions int32) DistributingProducer {
	return &randomProducer{
		rand:       rand.New(rand.NewSource(time.Now().UnixNano())),
		producer:   p,
		partitions: numPartitions,
	}
}

// Distribute write messages to given kafka topic, randomly destination choosing
// partition. All messages written within single Produce call are atomically
// written to the same destination.
func (p *randomProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	// In the case there are no partitions, which may happen for new topics
	// when AllowTopicCreation is passed, we will write to partition 0
	// since rand.Intn panics with 0
	part := 0
	if p.partitions > 0 {
		part = p.rand.Intn(int(p.partitions))
	}
	return p.producer.Produce(topic, int32(part), messages...)
}

type roundRobinProducer struct {
	producer   Producer
	partitions int32
	mu         sync.Mutex
	next       int32
}

// NewRoundRobinProducer wraps given producer and return DistributingProducer
// that publish messages to kafka, choosing destination partition from cycle
// build from [0, numPartitions) range.
func NewRoundRobinProducer(p Producer, numPartitions int32) DistributingProducer {
	return &roundRobinProducer{
		producer:   p,
		partitions: numPartitions,
		next:       0,
	}
}

// Distribute write messages to given kafka topic, choosing next destination
// partition from internal cycle. All messages written within single Produce
// call are atomically written to the same destination.
func (p *roundRobinProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	p.mu.Lock()
	part := p.next
	p.next++
	if p.next >= p.partitions {
		p.next = 0
	}
	p.mu.Unlock()

	return p.producer.Produce(topic, int32(part), messages...)
}

type hashProducer struct {
	producer   Producer
	partitions int32
}

// NewHashProducer wraps given producer and return DistributingProducer that
// publish messages to kafka, computing partition number from message key hash,
// using fnv hash and [0, numPartitions) range.
func NewHashProducer(p Producer, numPartitions int32) DistributingProducer {
	return &hashProducer{
		producer:   p,
		partitions: numPartitions,
	}
}

// Distribute write messages to given kafka topic, computing partition number from
// the message key value. Message key must be not nil and all messages written
// within single Produce call are atomically written to the same destination.
//
// All messages passed within single Produce call must hash to the same
// destination, otherwise no message is written and error is returned.
func (p *hashProducer) Distribute(topic string, messages ...*proto.Message) (offset int64, err error) {
	if len(messages) == 0 {
		return 0, errors.New("no messages")
	}
	part, err := p.messagePartition(messages[0])
	if err != nil {
		return 0, fmt.Errorf("cannot hash message: %s", err)
	}
	// make sure that all messages within single call are to the same destination
	for i := 2; i < len(messages); i++ {
		mp, err := p.messagePartition(messages[i])
		if err != nil {
			return 0, fmt.Errorf("cannot hash message: %s", err)
		}
		if part != mp {
			return 0, errors.New("cannot publish messages to different destinations")
		}
	}

	return p.producer.Produce(topic, part, messages...)
}

// messagePartition compute message's key hash and return corresponding
// partition number.
func (p *hashProducer) messagePartition(m *proto.Message) (int32, error) {
	if m.Key == nil {
		return 0, errors.New("no key")
	}
	hasher := fnv.New32a()
	if _, err := hasher.Write(m.Key); err != nil {
		return 0, fmt.Errorf("cannot hash key: %s", err)
	}
	sum := int32(hasher.Sum32())
	if sum < 0 {
		sum = -sum
	}
	return sum % p.partitions, nil
}
<|endoftext|>


<commit_before>package handler

import (
	"errors"
	"fmt"
	"log"
	"net/http"

	"github.com/deiwin/luncher-api/db"
	"github.com/deiwin/luncher-api/facebook"
	"github.com/deiwin/luncher-api/session"
)

type Facebook interface {
	// Login returns a handler that redirects the user to Facebook to log in
	Login() Handler
	// Redirected returns a handler that receives the user and page tokens for the
	// user who has just logged in through Facebook. Updates the user and page
	// access tokens in the DB
	Redirected() Handler
}

type fbook struct {
	auth            facebook.Authenticator
	sessionManager  session.Manager
	api             facebook.API
	usersCollection db.Users
}

func NewFacebook(fbAuth facebook.Authenticator, sessMgr session.Manager, api facebook.API, usersCollection db.Users) Facebook {
	return fbook{fbAuth, sessMgr, api, usersCollection}
}

func (fb fbook) Login() Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		session := fb.sessionManager.GetOrInitSession(w, r)
		redirectURL := fb.auth.AuthURL(session)
		http.Redirect(w, r, redirectURL, http.StatusSeeOther)
	}
}

func (fb fbook) Redirected() Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		err := fb.checkState(w, r)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		code := r.FormValue("code")
		if code == "" {
			log.Println("A Facebook redirect request is missing the 'code' value")
			http.Error(w, "Expecting a 'code' value", http.StatusBadRequest)
			return
		}
		tok, err := fb.auth.Token(code)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		client := fb.auth.Client(tok)

		connection := facebook.NewConnection(fb.api, client)
		userID, err := getUserID(connection)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		pageID, err := fb.getPageID(userID)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		pageAccessToken, err := fb.getPageAccessToken(connection, pageID)
		if err != nil {
			log.Println(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}

		fmt.Fprint(w, pageAccessToken)
	}
}

func (fb fbook) getPageAccessToken(connection facebook.Connection, pageID string) (pageAccessToken string, err error) {
	accs, err := connection.Accounts()
	if err != nil {
		return
	}
	for _, page := range accs.Data {
		if page.ID == pageID {
			pageAccessToken = page.AccessToken
			return
		}
	}
	err = errors.New("Couldn't find the administered page")
	return
}

func (fb fbook) checkState(w http.ResponseWriter, r *http.Request) error {
	session := fb.sessionManager.GetOrInitSession(w, r)
	state := r.FormValue("state")
	if state == "" {
		return errors.New("A Facebook redirect request is missing the 'state' value")
	} else if state != session {
		return errors.New("A Facebook redirect request's 'state' value does not match the session")
	}
	return nil
}

func getUserID(connection facebook.Connection) (userID string, err error) {
	user, err := connection.Me()
	if err != nil {
		return
	}
	userID = user.Id
	return
}

func (fb fbook) getPageID(userID string) (pageID string, err error) {
	userInDB, err := fb.usersCollection.Get(userID)
	if err != nil {
		return
	}
	pageID = userInDB.FacebookPageID
	return
}
<commit_msg>update redirected handler to store access_tokens in DB<commit_after>package handler

import (
	"errors"
	"fmt"
	"log"
	"net/http"

	"github.com/deiwin/luncher-api/db"
	"github.com/deiwin/luncher-api/facebook"
	"github.com/deiwin/luncher-api/session"
	"golang.org/x/oauth2"
)

type Facebook interface {
	// Login returns a handler that redirects the user to Facebook to log in
	Login() Handler
	// Redirected returns a handler that receives the user and page tokens for the
	// user who has just logged in through Facebook. Updates the user and page
	// access tokens in the DB
	Redirected() Handler
}

type fbook struct {
	auth            facebook.Authenticator
	sessionManager  session.Manager
	api             facebook.API
	usersCollection db.Users
}

func NewFacebook(fbAuth facebook.Authenticator, sessMgr session.Manager, api facebook.API, usersCollection db.Users) Facebook {
	return fbook{fbAuth, sessMgr, api, usersCollection}
}

func (fb fbook) Login() Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		session := fb.sessionManager.GetOrInitSession(w, r)
		redirectURL := fb.auth.AuthURL(session)
		http.Redirect(w, r, redirectURL, http.StatusSeeOther)
	}
}

func (fb fbook) Redirected() Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		err := fb.checkState(w, r)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		code := r.FormValue("code")
		if code == "" {
			log.Println("A Facebook redirect request is missing the 'code' value")
			http.Error(w, "Expecting a 'code' value", http.StatusBadRequest)
			return
		}
		tok, err := fb.auth.Token(code)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		client := fb.auth.Client(tok)

		connection := facebook.NewConnection(fb.api, client)
		userID, err := getUserID(connection)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		pageID, err := fb.getPageID(userID)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		pageAccessToken, err := fb.getPageAccessToken(connection, pageID)
		if err != nil {
			log.Println(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		err = fb.storeAccessTokensInDB(userID, tok, pageAccessToken)
		if err != nil {
			log.Print(err)
			http.Error(w, "", http.StatusInternalServerError)
			return
		}
		// TODO redirect to the admin page
		fmt.Fprint(w, pageAccessToken)
	}
}

func (fb fbook) storeAccessTokensInDB(userID string, tok *oauth2.Token, pageAccessToken string) (err error) {
	err = fb.usersCollection.SetAccessToken(userID, *tok)
	if err != nil {
		return
	}
	err = fb.usersCollection.SetPageAccessToken(userID, pageAccessToken)
	return
}

func (fb fbook) getPageAccessToken(connection facebook.Connection, pageID string) (pageAccessToken string, err error) {
	accs, err := connection.Accounts()
	if err != nil {
		return
	}
	for _, page := range accs.Data {
		if page.ID == pageID {
			pageAccessToken = page.AccessToken
			return
		}
	}
	err = errors.New("Couldn't find the administered page")
	return
}

func (fb fbook) checkState(w http.ResponseWriter, r *http.Request) error {
	session := fb.sessionManager.GetOrInitSession(w, r)
	state := r.FormValue("state")
	if state == "" {
		return errors.New("A Facebook redirect request is missing the 'state' value")
	} else if state != session {
		return errors.New("A Facebook redirect request's 'state' value does not match the session")
	}
	return nil
}

func getUserID(connection facebook.Connection) (userID string, err error) {
	user, err := connection.Me()
	if err != nil {
		return
	}
	userID = user.Id
	return
}

func (fb fbook) getPageID(userID string) (pageID string, err error) {
	userInDB, err := fb.usersCollection.Get(userID)
	if err != nil {
		return
	}
	pageID = userInDB.FacebookPageID
	return
}
<|endoftext|>


<commit_before>// Package cli implements a colored text handler suitable for command-line interfaces.
package cli

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"github.com/apex/log"
	"github.com/fatih/color"
)

// Default handler outputting to stderr.
var Default = New(os.Stderr)

// start time.
var start = time.Now()

var bold = color.New(color.Bold)

// Colors mapping.
var Colors = [...]*color.Color{
	log.DebugLevel: color.New(color.FgWhite),
	log.InfoLevel:  color.New(color.FgBlue),
	log.WarnLevel:  color.New(color.FgYellow),
	log.ErrorLevel: color.New(color.FgRed),
	log.FatalLevel: color.New(color.FgRed),
}

// Strings mapping.
var Strings = [...]string{
	log.DebugLevel: "•",
	log.InfoLevel:  "•",
	log.WarnLevel:  "•",
	log.ErrorLevel: "⨯",
	log.FatalLevel: "⨯",
}

// Handler implementation.
type Handler struct {
	mu      sync.Mutex
	Writer  io.Writer
	Padding int
}

// New handler.
func New(w io.Writer) *Handler {
	return &Handler{
		Writer:  w,
		Padding: 3,
	}
}

// HandleLog implements log.Handler.
func (h *Handler) HandleLog(e *log.Entry) error {
	color := Colors[e.Level]
	level := Strings[e.Level]
	names := e.Fields.Names()

	h.mu.Lock()
	defer h.mu.Unlock()

	color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message)

	for _, name := range names {
		if name == "source" {
			continue
		}
		fmt.Fprintf(h.Writer, " %s=%s", color.Sprint(name), e.Fields.Get(name))
	}

	fmt.Fprintln(h.Writer)

	return nil
}
<commit_msg>use colorable *os.File<commit_after>// Package cli implements a colored text handler suitable for command-line interfaces.
package cli

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"github.com/apex/log"
	"github.com/fatih/color"
	colorable "github.com/mattn/go-colorable"
)

// Default handler outputting to stderr.
var Default = New(os.Stderr)

// start time.
var start = time.Now()

var bold = color.New(color.Bold)

// Colors mapping.
var Colors = [...]*color.Color{
	log.DebugLevel: color.New(color.FgWhite),
	log.InfoLevel:  color.New(color.FgBlue),
	log.WarnLevel:  color.New(color.FgYellow),
	log.ErrorLevel: color.New(color.FgRed),
	log.FatalLevel: color.New(color.FgRed),
}

// Strings mapping.
var Strings = [...]string{
	log.DebugLevel: "•",
	log.InfoLevel:  "•",
	log.WarnLevel:  "•",
	log.ErrorLevel: "⨯",
	log.FatalLevel: "⨯",
}

// Handler implementation.
type Handler struct {
	mu      sync.Mutex
	Writer  io.Writer
	Padding int
}

// New handler.
func New(w io.Writer) *Handler {
	switch w2 := w.(type) {
	case *os.File:
		return &Handler{
			Writer:  colorable.NewColorable(w2),
			Padding: 3,
		}
	}
	return &Handler{
		Writer:  w,
		Padding: 3,
	}
}

// HandleLog implements log.Handler.
func (h *Handler) HandleLog(e *log.Entry) error {
	color := Colors[e.Level]
	level := Strings[e.Level]
	names := e.Fields.Names()

	h.mu.Lock()
	defer h.mu.Unlock()

	color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message)

	for _, name := range names {
		if name == "source" {
			continue
		}
		fmt.Fprintf(h.Writer, " %s=%s", color.Sprint(name), e.Fields.Get(name))
	}

	fmt.Fprintln(h.Writer)

	return nil
}
<|endoftext|>


<commit_before>package handlers

import (
	"errors"
	"github.com/hacksoc-manchester/www/helpers/crypto"
	"github.com/hacksoc-manchester/www/services/databaseService"
	"github.com/hacksoc-manchester/www/services/emailService"
	"net/http"
	"os"
)

func signUp(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates["sign-up"].ExecuteTemplate(w, "layout", reCaptchaSiteKey)

	case "POST":
		firstName := r.PostFormValue("first-name")
		lastName := r.PostFormValue("last-name")
		email := r.PostFormValue("email")
		subscribedToArticles := r.PostFormValue("subscribe-to-articles") == "on"
		subscribedToEvents := r.PostFormValue("subscribe-to-events") == "on"

		var response string

		if reCaptcha.Verify(*r) {
			err := registerUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

			if err == nil {
				response = "Welcome! You are now part of our mailing list."
			} else {
				response = err.Error()
			}
		} else {
			response = "Turing test failed. Please try again."
		}

		templates["message"].ExecuteTemplate(w, "layout", messageModel{"Sign Up", response})

	default:
		errorHandler(w, r, http.StatusBadRequest)
	}
}

func registerUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !subscribedToArticles && !subscribedToEvents {
		return errors.New("Please select at least one subscription.")
	}

	err := databaseService.CreateUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

	if err != nil {
		return err
	}

	senderName := "HackSoc"
	senderEmail := os.Getenv("NOREPLY_EMAIL")
	receiverName := firstName + " " + lastName
	subject := "Welcome to HackSoc!"
	message := `You are now part of our mailing list.

		To unsubscribe, <a href=\"` + getUnsubscribeLink(email) + `">click here.`

	return emailService.Send(senderName, senderEmail, receiverName, email, subject, message)
}

func getUnsubscribeLink(email string) string {
	token, _ := crypto.Encrypt(email)

	return "http://hacksoc.com/unsubscribe?token=" + token
}
<commit_msg>No escape req<commit_after>package handlers

import (
	"errors"
	"github.com/hacksoc-manchester/www/helpers/crypto"
	"github.com/hacksoc-manchester/www/services/databaseService"
	"github.com/hacksoc-manchester/www/services/emailService"
	"net/http"
	"os"
)

func signUp(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates["sign-up"].ExecuteTemplate(w, "layout", reCaptchaSiteKey)

	case "POST":
		firstName := r.PostFormValue("first-name")
		lastName := r.PostFormValue("last-name")
		email := r.PostFormValue("email")
		subscribedToArticles := r.PostFormValue("subscribe-to-articles") == "on"
		subscribedToEvents := r.PostFormValue("subscribe-to-events") == "on"

		var response string

		if reCaptcha.Verify(*r) {
			err := registerUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

			if err == nil {
				response = "Welcome! You are now part of our mailing list."
			} else {
				response = err.Error()
			}
		} else {
			response = "Turing test failed. Please try again."
		}

		templates["message"].ExecuteTemplate(w, "layout", messageModel{"Sign Up", response})

	default:
		errorHandler(w, r, http.StatusBadRequest)
	}
}

func registerUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !subscribedToArticles && !subscribedToEvents {
		return errors.New("Please select at least one subscription.")
	}

	err := databaseService.CreateUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

	if err != nil {
		return err
	}

	senderName := "HackSoc"
	senderEmail := os.Getenv("NOREPLY_EMAIL")
	receiverName := firstName + " " + lastName
	subject := "Welcome to HackSoc!"
	message := `You are now part of our mailing list.

		To unsubscribe, <a href="` + getUnsubscribeLink(email) + `">click here.`

	return emailService.Send(senderName, senderEmail, receiverName, email, subject, message)
}

func getUnsubscribeLink(email string) string {
	token, _ := crypto.Encrypt(email)

	return "http://hacksoc.com/unsubscribe?token=" + token
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/AlekSi/pointer"
	"github.com/google/go-github/github"
	"github.com/k8s-community/cicd"
	"github.com/k8s-community/github-integration/models"
	userManClient "github.com/k8s-community/user-manager/client"
	"github.com/takama/router"
	"gopkg.in/reform.v1"
	githubhook "gopkg.in/rjz/githubhook.v0"
)

// WebHookHandler is common handler for web hooks (installation, repositories installation, push)
func (h *Handler) WebHookHandler(c *router.Control) {
	secret := []byte(h.Env["GITHUBINT_TOKEN"])

	hook, err := githubhook.Parse(secret, c.Request)
	if err != nil {
		h.Errlog.Printf("cannot parse hook (ID %s): %s", hook.Id, err)
		return
	}

	switch hook.Event {
	case "integration_installation":
		// Triggered when an integration has been installed or uninstalled by user.
		h.Infolog.Printf("initialization web hook (ID %s)", hook.Id)
		err = h.saveInstallation(hook)

	case "integration_installation_repositories":
		// Triggered when a repository is added or removed from an installation.
		h.Infolog.Printf("initialization web hook for user repositories (ID %s)", hook.Id)
		err = h.initialUserManagement(hook)

	case "push":
		// Any Git push to a Repository, including editing tags or branches.
		// Commits via API actions that update references are also counted. This is the default event.
		h.Infolog.Printf("push hook (ID %s)", hook.Id)
		err = h.processPush(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	case "create":
		h.Infolog.Printf("create hook (ID %s)", hook.Id)
		// ToDo: keep it for the future
		/*err = h.processCreate(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}*/
		return

	default:
		h.Infolog.Printf("Warning! Don't know how to process hook (ID %s), event = %s", hook.Id, hook.Event)
		c.Code(http.StatusOK).Body(nil)
		return
	}

	if err != nil {
		h.Errlog.Printf("cannot process hook (ID %s): %s", hook.Id, err)
		c.Code(http.StatusInternalServerError).Body(nil)
		return
	}

	h.Infolog.Printf("finished to process hook (ID %s)", hook.Id)
	c.Code(http.StatusOK).Body(nil)
}

// initialUserManagement is used for user activation in k8s system
func (h *Handler) initialUserManagement(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationRepositoriesEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	userManagerURL := h.Env["USERMAN_BASE_URL"]

	client, err := userManClient.NewClient(nil, userManagerURL)
	if err != nil {
		return err
	}

	h.Infolog.Print("Try to activate (sync) user in k8s system: ", *evt.Sender.Login)

	user := userManClient.NewUser(*evt.Installation.Account.Login)

	code, err := client.User.Sync(user)
	if err != nil {
		return err
	}

	h.Infolog.Printf("Service user-man, method sync, returned code: %d", code)

	return nil
}

// processPush is used for start CI/CD process for some repository from push hook
func (h *Handler) processPush(c *router.Control, hook *githubhook.Hook) error {
	evt := github.PushEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// ToDO: process somehow kind of hooks without HeadCommit
	if evt.HeadCommit == nil {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - no HeadCommit inside", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	prefix := "refs/heads/" + h.Env["GITHUBINT_BRANCH"]
	if !strings.HasPrefix(*evt.Ref, prefix) {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - branch %s", hook.Id, *evt.Ref)
		return nil
	}

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	version := strings.Trim(*evt.Ref, prefix)
	commitID := *evt.HeadCommit.ID
	version += "-" + commitID[0:7]

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.HeadCommit.ID,
		Task:       cicd.TaskDeploy,
		Version:    &version,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

func (h *Handler) processCreate(c *router.Control, hook *githubhook.Hook) error {
	evt := github.CreateEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// Process only tags
	if evt.RefType == nil || *evt.RefType != "tag" {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - not a tag", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.Ref,
		Task:       cicd.TaskDeploy,
		Version:    evt.Ref,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

// saveInstallation saves installation in memory
func (h *Handler) saveInstallation(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	h.Infolog.Printf("save installation for user %s (installation ID = %d)", *evt.Sender.Login, *evt.Installation.ID)

	// save installation for commit status update
	err = h.setInstallationID(*evt.Installation.Account.Login, *evt.Installation.ID)
	if err != nil {
		h.Errlog.Printf("Couldn't save installation: %+v", err)
	}

	return nil
}

// installationID gets installation from DB
func (h *Handler) installationID(username string) (*int, error) {
	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil {
		return nil, err
	}
	inst := st.(*models.Installation)

	return pointer.ToInt(inst.InstallationID), nil
}

func (h *Handler) setInstallationID(username string, instID int) error {
	var inst = &models.Installation{}

	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil && err != reform.ErrNoRows {
		return err
	}

	if err == nil {
		inst = st.(*models.Installation)
	}

	inst.InstallationID = instID
	inst.Username = username

	err = h.DB.Save(inst)

	return err
}
<commit_msg>Improve logging<commit_after>package handlers

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/AlekSi/pointer"
	"github.com/google/go-github/github"
	"github.com/k8s-community/cicd"
	"github.com/k8s-community/github-integration/models"
	userManClient "github.com/k8s-community/user-manager/client"
	"github.com/takama/router"
	"gopkg.in/reform.v1"
	githubhook "gopkg.in/rjz/githubhook.v0"
)

// WebHookHandler is common handler for web hooks (installation, repositories installation, push)
func (h *Handler) WebHookHandler(c *router.Control) {
	secret := []byte(h.Env["GITHUBINT_TOKEN"])

	hook, err := githubhook.Parse(secret, c.Request)
	if err != nil {
		h.Errlog.Printf("cannot parse hook (ID %s): %s", hook.Id, err)
		return
	}

	switch hook.Event {
	case "integration_installation":
		// Triggered when an integration has been installed or uninstalled by user.
		h.Infolog.Printf("initialization web hook (ID %s)", hook.Id)
		err = h.saveInstallation(hook)

	case "integration_installation_repositories":
		// Triggered when a repository is added or removed from an installation.
		h.Infolog.Printf("initialization web hook for user repositories (ID %s)", hook.Id)
		err = h.initialUserManagement(hook)

	case "push":
		// Any Git push to a Repository, including editing tags or branches.
		// Commits via API actions that update references are also counted. This is the default event.
		h.Infolog.Printf("push hook (ID %s)", hook.Id)
		err = h.processPush(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	case "create":
		h.Infolog.Printf("create hook (ID %s)", hook.Id)
		// ToDo: keep it for the future
		/*err = h.processCreate(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}*/
		return

	default:
		h.Infolog.Printf("Warning! Don't know how to process hook (ID %s), event = %s", hook.Id, hook.Event)
		c.Code(http.StatusOK).Body(nil)
		return
	}

	if err != nil {
		h.Errlog.Printf("cannot process hook (ID %s, event = %s): %s", hook.Id, hook.Event, err)
		c.Code(http.StatusInternalServerError).Body(nil)
		return
	}

	h.Infolog.Printf("finished to process hook (ID %s, event = %s)", hook.Id, hook.Event)
	c.Code(http.StatusOK).Body(nil)
}

// initialUserManagement is used for user activation in k8s system
func (h *Handler) initialUserManagement(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationRepositoriesEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	userManagerURL := h.Env["USERMAN_BASE_URL"]

	client, err := userManClient.NewClient(nil, userManagerURL)
	if err != nil {
		return err
	}

	h.Infolog.Print("Try to activate (sync) user in k8s system: ", *evt.Sender.Login)

	user := userManClient.NewUser(*evt.Installation.Account.Login)

	code, err := client.User.Sync(user)
	if err != nil {
		return err
	}

	h.Infolog.Printf("Service user-man, method sync, returned code: %d", code)

	return nil
}

// processPush is used for start CI/CD process for some repository from push hook
func (h *Handler) processPush(c *router.Control, hook *githubhook.Hook) error {
	evt := github.PushEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// ToDO: process somehow kind of hooks without HeadCommit
	if evt.HeadCommit == nil {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - no HeadCommit inside", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	prefix := "refs/heads/" + h.Env["GITHUBINT_BRANCH"]
	if !strings.HasPrefix(*evt.Ref, prefix) {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - branch %s", hook.Id, *evt.Ref)
		return nil
	}

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	version := strings.Trim(*evt.Ref, prefix)
	commitID := *evt.HeadCommit.ID
	version += "-" + commitID[0:7]

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.HeadCommit.ID,
		Task:       cicd.TaskDeploy,
		Version:    &version,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

func (h *Handler) processCreate(c *router.Control, hook *githubhook.Hook) error {
	evt := github.CreateEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// Process only tags
	if evt.RefType == nil || *evt.RefType != "tag" {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - not a tag", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.Ref,
		Task:       cicd.TaskDeploy,
		Version:    evt.Ref,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

// saveInstallation saves installation in memory
func (h *Handler) saveInstallation(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	h.Infolog.Printf("save installation for user %s (installation ID = %d)", *evt.Sender.Login, *evt.Installation.ID)

	// save installation for commit status update
	err = h.setInstallationID(*evt.Installation.Account.Login, *evt.Installation.ID)
	if err != nil {
		h.Errlog.Printf("Couldn't save installation: %+v", err)
	}

	return nil
}

// installationID gets installation from DB
func (h *Handler) installationID(username string) (*int, error) {
	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil {
		return nil, err
	}
	inst := st.(*models.Installation)

	return pointer.ToInt(inst.InstallationID), nil
}

func (h *Handler) setInstallationID(username string, instID int) error {
	var inst = &models.Installation{}

	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil && err != reform.ErrNoRows {
		return err
	}

	if err == nil {
		inst = st.(*models.Installation)
	}

	inst.InstallationID = instID
	inst.Username = username

	err = h.DB.Save(inst)

	return err
}
<|endoftext|>