<commit_before>package typescriptify

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"time"
)

type TypeScriptify struct {
	Prefix           string
	Suffix           string
	Indent           string
	CreateFromMethod bool
	BackupExtension  string // If empty no backup

	golangTypes []reflect.Type
	types       map[reflect.Kind]string

	// throwaway, used when converting
	alreadyConverted map[reflect.Type]bool
}

func New() *TypeScriptify {
	result := new(TypeScriptify)
	result.Indent = "\t"
	result.BackupExtension = "backup"

	types := make(map[reflect.Kind]string)

	types[reflect.Bool] = "boolean"

	types[reflect.Int] = "number"
	types[reflect.Int8] = "number"
	types[reflect.Int16] = "number"
	types[reflect.Int32] = "number"
	types[reflect.Int64] = "number"
	types[reflect.Uint] = "number"
	types[reflect.Uint8] = "number"
	types[reflect.Uint16] = "number"
	types[reflect.Uint32] = "number"
	types[reflect.Uint64] = "number"
	types[reflect.Float32] = "number"
	types[reflect.Float64] = "number"

	types[reflect.String] = "string"

	result.types = types

	result.Indent = "    "
	result.CreateFromMethod = true

	return result
}

func deepFields(typeOf reflect.Type) []reflect.StructField {
	fields := make([]reflect.StructField, 0)

	if typeOf.Kind() == reflect.Ptr {
		typeOf = typeOf.Elem()
	}

	if typeOf.Kind() != reflect.Struct {
		return fields
	}

	for i := 0; i < typeOf.NumField(); i++ {
		f := typeOf.Field(i)

		kind := f.Type.Kind()
		if f.Anonymous && kind == reflect.Struct {
			//fmt.Println(v.Interface())
			fields = append(fields, deepFields(f.Type)...)
		} else {
			fields = append(fields, f)
		}
	}

	return fields
}

func (t *TypeScriptify) Add(obj interface{}) {
	t.AddType(reflect.TypeOf(obj))
}

func (t *TypeScriptify) AddType(typeOf reflect.Type) {
	t.golangTypes = append(t.golangTypes, typeOf)
}

func (t *TypeScriptify) Convert(customCode map[string]string) (string, error) {
	t.alreadyConverted = make(map[reflect.Type]bool)

	result := ""
	for _, typeof := range t.golangTypes {
		typeScriptCode, err := t.convertType(typeof, customCode)
		if err != nil {
			return "", err
		}
		result += "\n" + strings.Trim(typeScriptCode, " "+t.Indent+"\r\n")
	}
	return result, nil
}

func loadCustomCode(fileName string) (map[string]string, error) {
	result := make(map[string]string)
	f, err := os.Open(fileName)
	if err != nil {
		if os.IsNotExist(err) {
			return result, nil
		}
		return result, err
	}
	defer f.Close()

	bytes, err := ioutil.ReadAll(f)
	if err != nil {
		return result, err
	}

	var currentName string
	var currentValue string
	lines := strings.Split(string(bytes), "\n")
	for _, line := range lines {
		trimmedLine := strings.TrimSpace(line)
		if strings.HasPrefix(trimmedLine, "//[") && strings.HasSuffix(trimmedLine, ":]") {
			currentName = strings.Replace(strings.Replace(trimmedLine, "//[", "", -1), ":]", "", -1)
			currentValue = ""
		} else if trimmedLine == "//[end]" {
			result[currentName] = strings.TrimRight(currentValue, " \t\r\n")
			currentName = ""
			currentValue = ""
		} else if len(currentName) > 0 {
			currentValue += line + "\n"
		}
	}

	return result, nil
}

func (t TypeScriptify) backup(fileName string) error {
	fileIn, err := os.Open(fileName)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		// No neet to backup, just return:
		return nil
	}
	defer fileIn.Close()

	bytes, err := ioutil.ReadAll(fileIn)
	if err != nil {
		return err
	}

	fileOut, err := os.Create(fmt.Sprintf("%s-%s.%s", fileName, time.Now().Format("2006-01-02T15_04_05.99"), t.BackupExtension))
	if err != nil {
		return err
	}
	defer fileOut.Close()

	_, err = fileOut.Write(bytes)
	if err != nil {
		return err
	}

	return nil
}

func (t TypeScriptify) ConvertToFile(fileName string) error {
	if len(t.BackupExtension) > 0 {
		err := t.backup(fileName)
		if err != nil {
			return err
		}
	}

	customCode, err := loadCustomCode(fileName)
	if err != nil {
		return err
	}

	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer f.Close()

	converted, err := t.Convert(customCode)
	if err != nil {
		return err
	}

	f.WriteString("/* Do not change, this code is generated from Golang structs */\n\n")
	f.WriteString(converted)
	if err != nil {
		return err
	}

	return nil
}

func (t *TypeScriptify) convertType(typeOf reflect.Type, customCode map[string]string) (string, error) {
	if _, found := t.alreadyConverted[typeOf]; found { // Already converted
		return "", nil
	}

	entityName := fmt.Sprintf("%s%s%s", t.Prefix, t.Suffix, typeOf.Name())
	result := fmt.Sprintf("class %s {\n", entityName)
	builder := typeScriptClassBuilder{
		types:  t.types,
		indent: t.Indent,
	}

	fields := deepFields(typeOf)
	for _, field := range fields {
		jsonTag := field.Tag.Get("json")
		jsonFieldName := ""
		if len(jsonTag) > 0 {
			jsonTagParts := strings.Split(jsonTag, ",")
			if len(jsonTagParts) > 0 {
				jsonFieldName = strings.Trim(jsonTagParts[0], t.Indent)
			}
		}
		if len(jsonFieldName) > 0 && jsonFieldName != "-" {
			var err error
			if field.Type.Kind() == reflect.Struct { // Struct:
				typeScriptChunk, err := t.convertType(field.Type, customCode)
				if err != nil {
					return "", err
				}
				result = typeScriptChunk + "\n" + result
				builder.AddStructField(jsonFieldName, field.Type.Name())
			} else if field.Type.Kind() == reflect.Slice { // Slice:
				if field.Type.Elem().Kind() == reflect.Struct { // Slice of structs:
					typeScriptChunk, err := t.convertType(field.Type.Elem(), customCode)
					if err != nil {
						return "", err
					}
					result = typeScriptChunk + "\n" + result
					builder.AddArrayOfStructsField(jsonFieldName, field.Type.Elem().Name())
				} else { // Slice of simple fields:
					err = builder.AddSimpleArrayField(jsonFieldName, field.Type.Elem().Name(), field.Type.Elem().Kind())
				}
			} else { // Simple field:
				err = builder.AddSimpleField(jsonFieldName, field.Type.Name(), field.Type.Kind())
			}
			if err != nil {
				return "", err
			}
		}
	}

	result += builder.fields
	if t.CreateFromMethod {
		result += fmt.Sprintf("\n%sstatic createFrom(source: any) {\n", t.Indent)
		result += fmt.Sprintf("%s%svar result = new %s();\n", t.Indent, t.Indent, entityName)
		result += builder.createFromMethodBody
		result += fmt.Sprintf("%s%sreturn result;\n", t.Indent, t.Indent)
		result += fmt.Sprintf("%s}\n\n", t.Indent)
	}

	if customCode != nil {
		code := customCode[entityName]
		result += t.Indent + "//[" + entityName + ":]\n" + code + "\n\n" + t.Indent + "//[end]\n"
	}

	result += "}"

	t.alreadyConverted[typeOf] = true

	return result, nil
}

type typeScriptClassBuilder struct {
	types                map[reflect.Kind]string
	indent               string
	fields               string
	createFromMethodBody string
}

func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName, fieldType string, kind reflect.Kind) error {
	if typeScriptType, ok := t.types[kind]; ok {
		if len(fieldName) > 0 {
			t.fields += fmt.Sprintf("%s%s: %s[];\n", t.indent, fieldName, typeScriptType)
			t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"];\n", t.indent, t.indent, fieldName, fieldName)
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Cannot find type for %s (%s/%s)", kind.String(), fieldName, fieldType))
}

func (t *typeScriptClassBuilder) AddSimpleField(fieldName, fieldType string, kind reflect.Kind) error {
	if typeScriptType, ok := t.types[kind]; ok {
		if len(fieldName) > 0 {
			t.fields += fmt.Sprintf("%s%s: %s;\n", t.indent, fieldName, typeScriptType)
			t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"];\n", t.indent, t.indent, fieldName, fieldName)
			return nil
		}
	}
	return errors.New("Cannot find type for " + fieldType)
}

func (t *typeScriptClassBuilder) AddStructField(fieldName, fieldType string) {
	t.fields += fmt.Sprintf("%s%s: %s;\n", t.indent, fieldName, fieldType)
	t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"] ? %s.createFrom(source[\"%s\"]) : null;\n", t.indent, t.indent, fieldName, fieldName, fieldType, fieldName)
}

func (t *typeScriptClassBuilder) AddArrayOfStructsField(fieldName, fieldType string) {
	t.fields += fmt.Sprintf("%s%s: %s[];\n", t.indent, fieldName, fieldType)
	t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"] ? source[\"%s\"].map(function(element) { return %s.createFrom(element); }) : null;\n", t.indent, t.indent, fieldName, fieldName, fieldName, fieldType)
}
<commit_msg>Default export classes<commit_after>package typescriptify

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"time"
)

type TypeScriptify struct {
	Prefix           string
	Suffix           string
	Indent           string
	CreateFromMethod bool
	BackupExtension  string // If empty no backup
	DontExport       bool

	golangTypes []reflect.Type
	types       map[reflect.Kind]string

	// throwaway, used when converting
	alreadyConverted map[reflect.Type]bool
}

func New() *TypeScriptify {
	result := new(TypeScriptify)
	result.Indent = "\t"
	result.BackupExtension = "backup"

	types := make(map[reflect.Kind]string)

	types[reflect.Bool] = "boolean"

	types[reflect.Int] = "number"
	types[reflect.Int8] = "number"
	types[reflect.Int16] = "number"
	types[reflect.Int32] = "number"
	types[reflect.Int64] = "number"
	types[reflect.Uint] = "number"
	types[reflect.Uint8] = "number"
	types[reflect.Uint16] = "number"
	types[reflect.Uint32] = "number"
	types[reflect.Uint64] = "number"
	types[reflect.Float32] = "number"
	types[reflect.Float64] = "number"

	types[reflect.String] = "string"

	result.types = types

	result.Indent = "    "
	result.CreateFromMethod = true

	return result
}

func deepFields(typeOf reflect.Type) []reflect.StructField {
	fields := make([]reflect.StructField, 0)

	if typeOf.Kind() == reflect.Ptr {
		typeOf = typeOf.Elem()
	}

	if typeOf.Kind() != reflect.Struct {
		return fields
	}

	for i := 0; i < typeOf.NumField(); i++ {
		f := typeOf.Field(i)

		kind := f.Type.Kind()
		if f.Anonymous && kind == reflect.Struct {
			//fmt.Println(v.Interface())
			fields = append(fields, deepFields(f.Type)...)
		} else {
			fields = append(fields, f)
		}
	}

	return fields
}

func (t *TypeScriptify) Add(obj interface{}) {
	t.AddType(reflect.TypeOf(obj))
}

func (t *TypeScriptify) AddType(typeOf reflect.Type) {
	t.golangTypes = append(t.golangTypes, typeOf)
}

func (t *TypeScriptify) Convert(customCode map[string]string) (string, error) {
	t.alreadyConverted = make(map[reflect.Type]bool)

	result := ""
	for _, typeof := range t.golangTypes {
		typeScriptCode, err := t.convertType(typeof, customCode)
		if err != nil {
			return "", err
		}
		result += "\n" + strings.Trim(typeScriptCode, " "+t.Indent+"\r\n")
	}
	return result, nil
}

func loadCustomCode(fileName string) (map[string]string, error) {
	result := make(map[string]string)
	f, err := os.Open(fileName)
	if err != nil {
		if os.IsNotExist(err) {
			return result, nil
		}
		return result, err
	}
	defer f.Close()

	bytes, err := ioutil.ReadAll(f)
	if err != nil {
		return result, err
	}

	var currentName string
	var currentValue string
	lines := strings.Split(string(bytes), "\n")
	for _, line := range lines {
		trimmedLine := strings.TrimSpace(line)
		if strings.HasPrefix(trimmedLine, "//[") && strings.HasSuffix(trimmedLine, ":]") {
			currentName = strings.Replace(strings.Replace(trimmedLine, "//[", "", -1), ":]", "", -1)
			currentValue = ""
		} else if trimmedLine == "//[end]" {
			result[currentName] = strings.TrimRight(currentValue, " \t\r\n")
			currentName = ""
			currentValue = ""
		} else if len(currentName) > 0 {
			currentValue += line + "\n"
		}
	}

	return result, nil
}

func (t TypeScriptify) backup(fileName string) error {
	fileIn, err := os.Open(fileName)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		// No neet to backup, just return:
		return nil
	}
	defer fileIn.Close()

	bytes, err := ioutil.ReadAll(fileIn)
	if err != nil {
		return err
	}

	fileOut, err := os.Create(fmt.Sprintf("%s-%s.%s", fileName, time.Now().Format("2006-01-02T15_04_05.99"), t.BackupExtension))
	if err != nil {
		return err
	}
	defer fileOut.Close()

	_, err = fileOut.Write(bytes)
	if err != nil {
		return err
	}

	return nil
}

func (t TypeScriptify) ConvertToFile(fileName string) error {
	if len(t.BackupExtension) > 0 {
		err := t.backup(fileName)
		if err != nil {
			return err
		}
	}

	customCode, err := loadCustomCode(fileName)
	if err != nil {
		return err
	}

	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer f.Close()

	converted, err := t.Convert(customCode)
	if err != nil {
		return err
	}

	f.WriteString("/* Do not change, this code is generated from Golang structs */\n\n")
	f.WriteString(converted)
	if err != nil {
		return err
	}

	return nil
}

func (t *TypeScriptify) convertType(typeOf reflect.Type, customCode map[string]string) (string, error) {
	if _, found := t.alreadyConverted[typeOf]; found { // Already converted
		return "", nil
	}

	entityName := fmt.Sprintf("%s%s%s", t.Prefix, t.Suffix, typeOf.Name())
	result := fmt.Sprintf("class %s {\n", entityName)
	if !t.DontExport {
		result = "export " + result
	}
	builder := typeScriptClassBuilder{
		types:  t.types,
		indent: t.Indent,
	}

	fields := deepFields(typeOf)
	for _, field := range fields {
		jsonTag := field.Tag.Get("json")
		jsonFieldName := ""
		if len(jsonTag) > 0 {
			jsonTagParts := strings.Split(jsonTag, ",")
			if len(jsonTagParts) > 0 {
				jsonFieldName = strings.Trim(jsonTagParts[0], t.Indent)
			}
		}
		if len(jsonFieldName) > 0 && jsonFieldName != "-" {
			var err error
			if field.Type.Kind() == reflect.Struct { // Struct:
				typeScriptChunk, err := t.convertType(field.Type, customCode)
				if err != nil {
					return "", err
				}
				result = typeScriptChunk + "\n" + result
				builder.AddStructField(jsonFieldName, field.Type.Name())
			} else if field.Type.Kind() == reflect.Slice { // Slice:
				if field.Type.Elem().Kind() == reflect.Struct { // Slice of structs:
					typeScriptChunk, err := t.convertType(field.Type.Elem(), customCode)
					if err != nil {
						return "", err
					}
					result = typeScriptChunk + "\n" + result
					builder.AddArrayOfStructsField(jsonFieldName, field.Type.Elem().Name())
				} else { // Slice of simple fields:
					err = builder.AddSimpleArrayField(jsonFieldName, field.Type.Elem().Name(), field.Type.Elem().Kind())
				}
			} else { // Simple field:
				err = builder.AddSimpleField(jsonFieldName, field.Type.Name(), field.Type.Kind())
			}
			if err != nil {
				return "", err
			}
		}
	}

	result += builder.fields
	if t.CreateFromMethod {
		result += fmt.Sprintf("\n%sstatic createFrom(source: any) {\n", t.Indent)
		result += fmt.Sprintf("%s%svar result = new %s();\n", t.Indent, t.Indent, entityName)
		result += builder.createFromMethodBody
		result += fmt.Sprintf("%s%sreturn result;\n", t.Indent, t.Indent)
		result += fmt.Sprintf("%s}\n\n", t.Indent)
	}

	if customCode != nil {
		code := customCode[entityName]
		result += t.Indent + "//[" + entityName + ":]\n" + code + "\n\n" + t.Indent + "//[end]\n"
	}

	result += "}"

	t.alreadyConverted[typeOf] = true

	return result, nil
}

type typeScriptClassBuilder struct {
	types                map[reflect.Kind]string
	indent               string
	fields               string
	createFromMethodBody string
}

func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName, fieldType string, kind reflect.Kind) error {
	if typeScriptType, ok := t.types[kind]; ok {
		if len(fieldName) > 0 {
			t.fields += fmt.Sprintf("%s%s: %s[];\n", t.indent, fieldName, typeScriptType)
			t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"];\n", t.indent, t.indent, fieldName, fieldName)
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Cannot find type for %s (%s/%s)", kind.String(), fieldName, fieldType))
}

func (t *typeScriptClassBuilder) AddSimpleField(fieldName, fieldType string, kind reflect.Kind) error {
	if typeScriptType, ok := t.types[kind]; ok {
		if len(fieldName) > 0 {
			t.fields += fmt.Sprintf("%s%s: %s;\n", t.indent, fieldName, typeScriptType)
			t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"];\n", t.indent, t.indent, fieldName, fieldName)
			return nil
		}
	}
	return errors.New("Cannot find type for " + fieldType)
}

func (t *typeScriptClassBuilder) AddStructField(fieldName, fieldType string) {
	t.fields += fmt.Sprintf("%s%s: %s;\n", t.indent, fieldName, fieldType)
	t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"] ? %s.createFrom(source[\"%s\"]) : null;\n", t.indent, t.indent, fieldName, fieldName, fieldType, fieldName)
}

func (t *typeScriptClassBuilder) AddArrayOfStructsField(fieldName, fieldType string) {
	t.fields += fmt.Sprintf("%s%s: %s[];\n", t.indent, fieldName, fieldType)
	t.createFromMethodBody += fmt.Sprintf("%s%sresult.%s = source[\"%s\"] ? source[\"%s\"].map(function(element) { return %s.createFrom(element); }) : null;\n", t.indent, t.indent, fieldName, fieldName, fieldName, fieldType)
}
<|endoftext|>


<commit_before>// +build !arm

package bcd

import (
	sys "golang.org/x/sys/unix"
)

func gettid() (int, error) {
	return sys.Gettid(), nil
}

// Call this function to allow other (non-parent) processes to trace this one.
// Alternatively, set kernel.yama.ptrace_scope = 0 in
// /etc/sysctl.d/10-ptrace.conf.
//
// This is a Linux-specific utility function.
func EnableTracing() error {
	return sys.Prctl(sys.PR_SET_PTRACER, sys.PR_SET_PTRACER_ANY, 0, 0, 0)
}
<commit_msg>bcd_sys_linux: fix const conversion with recent Go.<commit_after>// +build !arm

package bcd

import (
	sys "golang.org/x/sys/unix"
)

func gettid() (int, error) {
	return sys.Gettid(), nil
}

// Call this function to allow other (non-parent) processes to trace this one.
// Alternatively, set kernel.yama.ptrace_scope = 0 in
// /etc/sysctl.d/10-ptrace.conf.
//
// This is a Linux-specific utility function.
func EnableTracing() error {
	// PR_SET_PTRACER_ANY may be a negative integer constant on some
	// systems, so we need to store it in a separate variable to bypass
	// Go's const conversion restrictions.
	var flag uint64
	flag = sys.PR_SET_PTRACER_ANY

	return sys.Prctl(sys.PR_SET_PTRACER, uintptr(flag), 0, 0, 0)
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

type (
	// Request holds the mapped fields from the request's JSON body
	Request struct {
		Query     string                 `json:"query" validate:"required,gte=10,lte=5000" conform:"trim"`
		Variables map[string]interface{} `json:"variables" validate:"required,gte=1,lte=15,dive,required"`
	}
)

// TODO: Consider extracting the common parts into its own package

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		return request, err
	}

	if err := validate(request, context); err != nil {
		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorDescription := err.Error()
		errorMessage := fmt.Sprintf("Error parsing request: %s", errorDescription)
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error("Error binding request: ", errorDescription)

		return echo.NewHTTPError(errorCode, []string{errorMessage})
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string
		var errorMessage = "Error validating request: "

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			context.Logger().Error(errorMessage, errs.Error())

			return echo.NewHTTPError(http.StatusUnprocessableEntity, []string{errs.Error()})
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorDescription := err.(error).Error()
			errorList = append(errorList, errorDescription)

			context.Logger().Error(errorMessage, errorDescription)
		}

		return echo.NewHTTPError(http.StatusUnprocessableEntity, errorList)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Query = sanitizer.Sanitize(request.Query)

	for key, value := range request.Variables {
		switch actualValue := value.(type) {
		case int:
			break
		case float64:
			break
		case string:
			request.Variables[key] = sanitizer.Sanitize(actualValue)
		case bool:
			break
		default:
			break
		}
	}
}
<commit_msg>Format fix<commit_after>package parser

import (
	"fmt"
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

// Request holds the mapped fields from the request's JSON body
type Request struct {
	Query     string                 `json:"query" validate:"required,gte=10,lte=5000" conform:"trim"`
	Variables map[string]interface{} `json:"variables" validate:"required,gte=1,lte=15,dive,required"`
}

// TODO: Consider extracting the common parts into its own package

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		return request, err
	}

	if err := validate(request, context); err != nil {
		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorDescription := err.Error()
		errorMessage := fmt.Sprintf("Error parsing request: %s", errorDescription)
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error("Error binding request: ", errorDescription)

		return echo.NewHTTPError(errorCode, []string{errorMessage})
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string
		var errorMessage = "Error validating request: "

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			context.Logger().Error(errorMessage, errs.Error())

			return echo.NewHTTPError(http.StatusUnprocessableEntity, []string{errs.Error()})
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorDescription := err.(error).Error()
			errorList = append(errorList, errorDescription)

			context.Logger().Error(errorMessage, errorDescription)
		}

		return echo.NewHTTPError(http.StatusUnprocessableEntity, errorList)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Query = sanitizer.Sanitize(request.Query)

	for key, value := range request.Variables {
		switch actualValue := value.(type) {
		case int:
			break
		case float64:
			break
		case string:
			request.Variables[key] = sanitizer.Sanitize(actualValue)
		case bool:
			break
		default:
			break
		}
	}
}
<|endoftext|>


<commit_before>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"rsc.io/letsencrypt"

	log "github.com/Sirupsen/logrus"
	logstash "github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	"github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	gorhandlers "github.com/gorilla/handlers"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
)

// ServeCmd is a cobra command for running the registry.
var ServeCmd = &cobra.Command{
	Use:   "serve <config>",
	Short: "`serve` stores and distributes Docker images",
	Long:  "`serve` stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {

		// setup context
		ctx := context.WithVersion(context.Background(), version.Version)

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(addr string) {
				log.Infof("debug server listening %v", addr)
				if err := http.ListenAndServe(addr, nil); err != nil {
					log.Fatalf("error listening on debug interface: %v", err)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(ctx, config)
		if err != nil {
			log.Fatalln(err)
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	config *configuration.Configuration
	app    *handlers.App
	Server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = context.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	if !config.Log.AccessLog.Disabled {
		handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
	}

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		config: config,
		Server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               nextProtos(config),
			MinVersion:               tls.VersionTLS10,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
		}

		if config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
			if config.HTTP.TLS.Certificate != "" {
				return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
			}
			var m letsencrypt.Manager
			if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
				return err
			}
			if !m.Registered() {
				if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
					return err
				}
			}
			tlsConf.GetCertificate = m.GetCertificate
		} else {
			tlsConf.Certificates = make([]tls.Certificate, 1)
			tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
			if err != nil {
				return err
			}
		}

		if len(config.HTTP.TLS.ClientCAs) != 0 {
			pool := x509.NewCertPool()

			for _, ca := range config.HTTP.TLS.ClientCAs {
				caPem, err := ioutil.ReadFile(ca)
				if err != nil {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				context.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		context.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		context.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.Server.Serve(ln)
}

func configureReporting(app *handlers.App) http.Handler {
	var handler http.Handler = app

	if app.Config.Reporting.Bugsnag.APIKey != "" {
		bugsnagConfig := bugsnag.Configuration{
			APIKey: app.Config.Reporting.Bugsnag.APIKey,
			// TODO(brianbland): provide the registry version here
			// AppVersion: "2.0",
		}
		if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
			bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
		}
		if app.Config.Reporting.Bugsnag.Endpoint != "" {
			bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
		}
		bugsnag.Configure(bugsnagConfig)

		handler = bugsnag.Handler(handler)
	}

	if app.Config.Reporting.NewRelic.LicenseKey != "" {
		agent := gorelic.NewAgent()
		agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
		if app.Config.Reporting.NewRelic.Name != "" {
			agent.NewrelicName = app.Config.Reporting.NewRelic.Name
		}
		agent.CollectHTTPStat = true
		agent.Verbose = app.Config.Reporting.NewRelic.Verbose
		agent.Run()

		handler = agent.WrapHTTPHandler(handler)
	}

	return handler
}

// configureLogging prepares the context with a logger using the
// configuration.
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
	if config.Log.Level == "" && config.Log.Formatter == "" {
		// If no config for logging is set, fallback to deprecated "Loglevel".
		log.SetLevel(logLevel(config.Loglevel))
		ctx = context.WithLogger(ctx, context.GetLogger(ctx))
		return ctx, nil
	}

	log.SetLevel(logLevel(config.Log.Level))

	formatter := config.Log.Formatter
	if formatter == "" {
		formatter = "text" // default formatter
	}

	switch formatter {
	case "json":
		log.SetFormatter(&log.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "text":
		log.SetFormatter(&log.TextFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "logstash":
		log.SetFormatter(&logstash.LogstashFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	default:
		// just let the library use default on empty string.
		if config.Log.Formatter != "" {
			return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
		}
	}

	if config.Log.Formatter != "" {
		log.Debugf("using %q logging formatter", config.Log.Formatter)
	}

	if len(config.Log.Fields) > 0 {
		// build up the static fields, if present.
		var fields []interface{}
		for k := range config.Log.Fields {
			fields = append(fields, k)
		}

		ctx = context.WithValues(ctx, config.Log.Fields)
		ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
	}

	return ctx, nil
}

func logLevel(level configuration.Loglevel) log.Level {
	l, err := log.ParseLevel(string(level))
	if err != nil {
		l = log.InfoLevel
		log.Warnf("error parsing level %q: %v, using %q	", level, err, l)
	}

	return l
}

// panicHandler add an HTTP handler to web app. The handler recover the happening
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
// defined in config.yml.
func panicHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Panic(fmt.Sprintf("%v", err))
			}
		}()
		handler.ServeHTTP(w, r)
	})
}

// alive simply wraps the handler with a route that always returns an http 200
// response when the path is matched. If the path is not matched, the request
// is passed to the provided handler. There is no guarantee of anything but
// that the server is up. Wrap with other handlers (such as health.Handler)
// for greater affect.
func alive(path string, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == path {
			w.Header().Set("Cache-Control", "no-cache")
			w.WriteHeader(http.StatusOK)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}

func nextProtos(config *configuration.Configuration) []string {
	switch config.HTTP.HTTP2.Disabled {
	case true:
		return []string{"http/1.1"}
	default:
		return []string{"h2", "http/1.1"}
	}
}
<commit_msg>Exposed registry config field<commit_after>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"rsc.io/letsencrypt"

	log "github.com/Sirupsen/logrus"
	logstash "github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	"github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	gorhandlers "github.com/gorilla/handlers"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
)

// ServeCmd is a cobra command for running the registry.
var ServeCmd = &cobra.Command{
	Use:   "serve <config>",
	Short: "`serve` stores and distributes Docker images",
	Long:  "`serve` stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {

		// setup context
		ctx := context.WithVersion(context.Background(), version.Version)

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(addr string) {
				log.Infof("debug server listening %v", addr)
				if err := http.ListenAndServe(addr, nil); err != nil {
					log.Fatalf("error listening on debug interface: %v", err)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(ctx, config)
		if err != nil {
			log.Fatalln(err)
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	Config *configuration.Configuration
	app    *handlers.App
	Server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = context.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	if !config.Log.AccessLog.Disabled {
		handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
	}

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		Config: config,
		Server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.Config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               nextProtos(config),
			MinVersion:               tls.VersionTLS10,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
		}

		if config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
			if config.HTTP.TLS.Certificate != "" {
				return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
			}
			var m letsencrypt.Manager
			if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
				return err
			}
			if !m.Registered() {
				if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
					return err
				}
			}
			tlsConf.GetCertificate = m.GetCertificate
		} else {
			tlsConf.Certificates = make([]tls.Certificate, 1)
			tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
			if err != nil {
				return err
			}
		}

		if len(config.HTTP.TLS.ClientCAs) != 0 {
			pool := x509.NewCertPool()

			for _, ca := range config.HTTP.TLS.ClientCAs {
				caPem, err := ioutil.ReadFile(ca)
				if err != nil {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				context.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		context.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		context.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.Server.Serve(ln)
}

func configureReporting(app *handlers.App) http.Handler {
	var handler http.Handler = app

	if app.Config.Reporting.Bugsnag.APIKey != "" {
		bugsnagConfig := bugsnag.Configuration{
			APIKey: app.Config.Reporting.Bugsnag.APIKey,
			// TODO(brianbland): provide the registry version here
			// AppVersion: "2.0",
		}
		if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
			bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
		}
		if app.Config.Reporting.Bugsnag.Endpoint != "" {
			bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
		}
		bugsnag.Configure(bugsnagConfig)

		handler = bugsnag.Handler(handler)
	}

	if app.Config.Reporting.NewRelic.LicenseKey != "" {
		agent := gorelic.NewAgent()
		agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
		if app.Config.Reporting.NewRelic.Name != "" {
			agent.NewrelicName = app.Config.Reporting.NewRelic.Name
		}
		agent.CollectHTTPStat = true
		agent.Verbose = app.Config.Reporting.NewRelic.Verbose
		agent.Run()

		handler = agent.WrapHTTPHandler(handler)
	}

	return handler
}

// configureLogging prepares the context with a logger using the
// configuration.
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
	if config.Log.Level == "" && config.Log.Formatter == "" {
		// If no config for logging is set, fallback to deprecated "Loglevel".
		log.SetLevel(logLevel(config.Loglevel))
		ctx = context.WithLogger(ctx, context.GetLogger(ctx))
		return ctx, nil
	}

	log.SetLevel(logLevel(config.Log.Level))

	formatter := config.Log.Formatter
	if formatter == "" {
		formatter = "text" // default formatter
	}

	switch formatter {
	case "json":
		log.SetFormatter(&log.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "text":
		log.SetFormatter(&log.TextFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "logstash":
		log.SetFormatter(&logstash.LogstashFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	default:
		// just let the library use default on empty string.
		if config.Log.Formatter != "" {
			return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
		}
	}

	if config.Log.Formatter != "" {
		log.Debugf("using %q logging formatter", config.Log.Formatter)
	}

	if len(config.Log.Fields) > 0 {
		// build up the static fields, if present.
		var fields []interface{}
		for k := range config.Log.Fields {
			fields = append(fields, k)
		}

		ctx = context.WithValues(ctx, config.Log.Fields)
		ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
	}

	return ctx, nil
}

func logLevel(level configuration.Loglevel) log.Level {
	l, err := log.ParseLevel(string(level))
	if err != nil {
		l = log.InfoLevel
		log.Warnf("error parsing level %q: %v, using %q	", level, err, l)
	}

	return l
}

// panicHandler add an HTTP handler to web app. The handler recover the happening
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
// defined in config.yml.
func panicHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Panic(fmt.Sprintf("%v", err))
			}
		}()
		handler.ServeHTTP(w, r)
	})
}

// alive simply wraps the handler with a route that always returns an http 200
// response when the path is matched. If the path is not matched, the request
// is passed to the provided handler. There is no guarantee of anything but
// that the server is up. Wrap with other handlers (such as health.Handler)
// for greater affect.
func alive(path string, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == path {
			w.Header().Set("Cache-Control", "no-cache")
			w.WriteHeader(http.StatusOK)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}

func nextProtos(config *configuration.Configuration) []string {
	switch config.HTTP.HTTP2.Disabled {
	case true:
		return []string{"http/1.1"}
	default:
		return []string{"h2", "http/1.1"}
	}
}
<|endoftext|>


<commit_before>package pubsub

import (
	"context"
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"

	"cloud.google.com/go/pubsub"
	"github.com/cloudevents/sdk-go/v2/binding"
	cecontext "github.com/cloudevents/sdk-go/v2/context"
	"github.com/cloudevents/sdk-go/v2/protocol"
	"github.com/cloudevents/sdk-go/v2/protocol/pubsub/internal"
)

const (
	ProtocolName = "Pub/Sub"
)

type subscriptionWithTopic struct {
	topicID        string
	subscriptionID string
}

// Protocol acts as both a pubsub topic and a pubsub subscription .
type Protocol struct {
	// PubSub

	// ReceiveSettings is used to configure Pubsub pull subscription.
	ReceiveSettings *pubsub.ReceiveSettings

	// AllowCreateTopic controls if the transport can create a topic if it does
	// not exist.
	AllowCreateTopic bool

	// AllowCreateSubscription controls if the transport can create a
	// subscription if it does not exist.
	AllowCreateSubscription bool

	projectID string
	topicID   string

	gccMux sync.Mutex

	subscriptions []subscriptionWithTopic
	client        *pubsub.Client

	connectionsBySubscription map[string]*internal.Connection
	connectionsByTopic        map[string]*internal.Connection

	incoming chan pubsub.Message
}

// New creates a new pubsub transport.
func New(ctx context.Context, opts ...Option) (*Protocol, error) {
	t := &Protocol{}
	t.incoming = make(chan pubsub.Message)
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}

	if t.client == nil {
		// Auth to pubsub.
		client, err := pubsub.NewClient(ctx, t.projectID)
		if err != nil {
			return nil, err
		}
		// Success.
		t.client = client
	}

	if t.connectionsBySubscription == nil {
		t.connectionsBySubscription = make(map[string]*internal.Connection)
	}

	if t.connectionsByTopic == nil {
		t.connectionsByTopic = make(map[string]*internal.Connection)
	}
	return t, nil
}

func (t *Protocol) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

// Send implements Sender.Send
func (t *Protocol) Send(ctx context.Context, in binding.Message, transformers ...binding.Transformer) error {
	var err error
	defer func() { _ = in.Finish(err) }()

	topic := cecontext.TopicFrom(ctx)
	if topic == "" {
		topic = t.topicID
	}

	conn := t.getOrCreateConnection(ctx, topic, "")

	msg := &pubsub.Message{}
	if err := WritePubSubMessage(ctx, in, msg, transformers...); err != nil {
		return err
	}

	if _, err := conn.Publish(ctx, msg); err != nil {
		return err
	}
	return nil
}

func (t *Protocol) getConnection(ctx context.Context, topic, subscription string) *internal.Connection {
	if subscription != "" {
		if conn, ok := t.connectionsBySubscription[subscription]; ok {
			return conn
		}
	}
	if topic != "" {
		if conn, ok := t.connectionsByTopic[topic]; ok {
			return conn
		}
	}

	return nil
}

func (t *Protocol) getOrCreateConnection(ctx context.Context, topic, subscription string) *internal.Connection {
	t.gccMux.Lock()
	defer t.gccMux.Unlock()

	// Get.
	if conn := t.getConnection(ctx, topic, subscription); conn != nil {
		return conn
	}
	// Create.
	conn := &internal.Connection{
		AllowCreateSubscription: t.AllowCreateSubscription,
		AllowCreateTopic:        t.AllowCreateTopic,
		ReceiveSettings:         t.ReceiveSettings,
		Client:                  t.client,
		ProjectID:               t.projectID,
		TopicID:                 topic,
		SubscriptionID:          subscription,
	}
	// Save for later.
	if subscription != "" {
		t.connectionsBySubscription[subscription] = conn
	}
	if topic != "" {
		t.connectionsByTopic[topic] = conn
	}

	return conn
}

// Receive implements Receiver.Receive
func (t *Protocol) Receive(ctx context.Context) (binding.Message, error) {
	m, ok := <-t.incoming
	if !ok {
		return nil, io.EOF
	}

	msg := NewMessage(&m)
	return msg, nil
}

func (t *Protocol) startSubscriber(ctx context.Context, sub subscriptionWithTopic, done func(error)) {
	logger := cecontext.LoggerFrom(ctx)
	logger.Infof("starting subscriber for Topic %q, Subscription %q", sub.topicID, sub.subscriptionID)
	conn := t.getOrCreateConnection(ctx, sub.topicID, sub.subscriptionID)

	logger.Info("conn is", conn)
	if conn == nil {
		err := fmt.Errorf("failed to find connection for Topic: %q, Subscription: %q", sub.topicID, sub.subscriptionID)
		done(err)
		return
	}
	// Ok, ready to start pulling.
	err := conn.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
		t.incoming <- *m
	})
	done(err)
}

func (t *Protocol) OpenInbound(ctx context.Context) error {
	cctx, cancel := context.WithCancel(ctx)
	defer cancel()
	n := len(t.subscriptions)

	// Make the channels for quit and errors.
	quit := make(chan struct{}, n)
	errc := make(chan error, n)

	// Start up each subscription.
	for _, sub := range t.subscriptions {
		go t.startSubscriber(cctx, sub, func(err error) {
			if err != nil {
				errc <- err
			} else {
				quit <- struct{}{}
			}
		})
	}

	// Collect errors and done calls until we have n of them.
	errs := []string(nil)
	for success := 0; success < n; success++ {
		var err error
		select {
		case <-ctx.Done(): // Block for parent context to finish.
			success--
		case err = <-errc: // Collect errors
		case <-quit:
		}
		if cancel != nil {
			// Stop all other subscriptions.
			cancel()
			cancel = nil
		}
		if err != nil {
			errs = append(errs, err.Error())
		}
	}

	close(quit)
	close(errc)

	return errors.New(strings.Join(errs, "\n"))
}

// Close implements Closer.Close
func (t *Protocol) Close(ctx context.Context) error {
	// TODO: Implement this.
	return nil
}

// pubsub protocol implements Sender, Receiver, Closer, Opener
var _ protocol.Opener = (*Protocol)(nil)
var _ protocol.Sender = (*Protocol)(nil)
var _ protocol.Receiver = (*Protocol)(nil)
var _ protocol.Closer = (*Protocol)(nil)
<commit_msg>Add cancellation to pubsub Receive (#469)<commit_after>package pubsub

import (
	"context"
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"

	"cloud.google.com/go/pubsub"
	"github.com/cloudevents/sdk-go/v2/binding"
	cecontext "github.com/cloudevents/sdk-go/v2/context"
	"github.com/cloudevents/sdk-go/v2/protocol"
	"github.com/cloudevents/sdk-go/v2/protocol/pubsub/internal"
)

const (
	ProtocolName = "Pub/Sub"
)

type subscriptionWithTopic struct {
	topicID        string
	subscriptionID string
}

// Protocol acts as both a pubsub topic and a pubsub subscription .
type Protocol struct {
	// PubSub

	// ReceiveSettings is used to configure Pubsub pull subscription.
	ReceiveSettings *pubsub.ReceiveSettings

	// AllowCreateTopic controls if the transport can create a topic if it does
	// not exist.
	AllowCreateTopic bool

	// AllowCreateSubscription controls if the transport can create a
	// subscription if it does not exist.
	AllowCreateSubscription bool

	projectID string
	topicID   string

	gccMux sync.Mutex

	subscriptions []subscriptionWithTopic
	client        *pubsub.Client

	connectionsBySubscription map[string]*internal.Connection
	connectionsByTopic        map[string]*internal.Connection

	incoming chan pubsub.Message
}

// New creates a new pubsub transport.
func New(ctx context.Context, opts ...Option) (*Protocol, error) {
	t := &Protocol{}
	t.incoming = make(chan pubsub.Message)
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}

	if t.client == nil {
		// Auth to pubsub.
		client, err := pubsub.NewClient(ctx, t.projectID)
		if err != nil {
			return nil, err
		}
		// Success.
		t.client = client
	}

	if t.connectionsBySubscription == nil {
		t.connectionsBySubscription = make(map[string]*internal.Connection)
	}

	if t.connectionsByTopic == nil {
		t.connectionsByTopic = make(map[string]*internal.Connection)
	}
	return t, nil
}

func (t *Protocol) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

// Send implements Sender.Send
func (t *Protocol) Send(ctx context.Context, in binding.Message, transformers ...binding.Transformer) error {
	var err error
	defer func() { _ = in.Finish(err) }()

	topic := cecontext.TopicFrom(ctx)
	if topic == "" {
		topic = t.topicID
	}

	conn := t.getOrCreateConnection(ctx, topic, "")

	msg := &pubsub.Message{}
	if err := WritePubSubMessage(ctx, in, msg, transformers...); err != nil {
		return err
	}

	if _, err := conn.Publish(ctx, msg); err != nil {
		return err
	}
	return nil
}

func (t *Protocol) getConnection(ctx context.Context, topic, subscription string) *internal.Connection {
	if subscription != "" {
		if conn, ok := t.connectionsBySubscription[subscription]; ok {
			return conn
		}
	}
	if topic != "" {
		if conn, ok := t.connectionsByTopic[topic]; ok {
			return conn
		}
	}

	return nil
}

func (t *Protocol) getOrCreateConnection(ctx context.Context, topic, subscription string) *internal.Connection {
	t.gccMux.Lock()
	defer t.gccMux.Unlock()

	// Get.
	if conn := t.getConnection(ctx, topic, subscription); conn != nil {
		return conn
	}
	// Create.
	conn := &internal.Connection{
		AllowCreateSubscription: t.AllowCreateSubscription,
		AllowCreateTopic:        t.AllowCreateTopic,
		ReceiveSettings:         t.ReceiveSettings,
		Client:                  t.client,
		ProjectID:               t.projectID,
		TopicID:                 topic,
		SubscriptionID:          subscription,
	}
	// Save for later.
	if subscription != "" {
		t.connectionsBySubscription[subscription] = conn
	}
	if topic != "" {
		t.connectionsByTopic[topic] = conn
	}

	return conn
}

// Receive implements Receiver.Receive
func (t *Protocol) Receive(ctx context.Context) (binding.Message, error) {
	select {
	case m, ok := <-t.incoming:
		if !ok {
			return nil, io.EOF
		}

		msg := NewMessage(&m)
		return msg, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (t *Protocol) startSubscriber(ctx context.Context, sub subscriptionWithTopic, done func(error)) {
	logger := cecontext.LoggerFrom(ctx)
	logger.Infof("starting subscriber for Topic %q, Subscription %q", sub.topicID, sub.subscriptionID)
	conn := t.getOrCreateConnection(ctx, sub.topicID, sub.subscriptionID)

	logger.Info("conn is", conn)
	if conn == nil {
		err := fmt.Errorf("failed to find connection for Topic: %q, Subscription: %q", sub.topicID, sub.subscriptionID)
		done(err)
		return
	}
	// Ok, ready to start pulling.
	err := conn.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
		t.incoming <- *m
	})
	done(err)
}

func (t *Protocol) OpenInbound(ctx context.Context) error {
	cctx, cancel := context.WithCancel(ctx)
	defer cancel()
	n := len(t.subscriptions)

	// Make the channels for quit and errors.
	quit := make(chan struct{}, n)
	errc := make(chan error, n)

	// Start up each subscription.
	for _, sub := range t.subscriptions {
		go t.startSubscriber(cctx, sub, func(err error) {
			if err != nil {
				errc <- err
			} else {
				quit <- struct{}{}
			}
		})
	}

	// Collect errors and done calls until we have n of them.
	errs := []string(nil)
	for success := 0; success < n; success++ {
		var err error
		select {
		case <-ctx.Done(): // Block for parent context to finish.
			success--
		case err = <-errc: // Collect errors
		case <-quit:
		}
		if cancel != nil {
			// Stop all other subscriptions.
			cancel()
			cancel = nil
		}
		if err != nil {
			errs = append(errs, err.Error())
		}
	}

	close(quit)
	close(errc)

	return errors.New(strings.Join(errs, "\n"))
}

// Close implements Closer.Close
func (t *Protocol) Close(ctx context.Context) error {
	// TODO: Implement this.
	return nil
}

// pubsub protocol implements Sender, Receiver, Closer, Opener
var _ protocol.Opener = (*Protocol)(nil)
var _ protocol.Sender = (*Protocol)(nil)
var _ protocol.Receiver = (*Protocol)(nil)
var _ protocol.Closer = (*Protocol)(nil)
<|endoftext|>


<commit_before>package nodefs

import (
	"fmt"
	"sync"
	"time"

	"github.com/hanwen/go-fuse/fuse"
)

type lockingFile struct {
	mu   *sync.Mutex
	file File
}

// NewDefaultFile returns a File instance that returns ENOSYS for
// every operation.
func NewLockingFile(mu *sync.Mutex, f File) File {
	return &lockingFile{
		mu:   mu,
		file: f,
	}
}

func (f *lockingFile) SetInode(*Inode) {
}

func (f *lockingFile) InnerFile() File {
	return nil
}

func (f *lockingFile) String() string {
	return fmt.Sprintf("lockingFile(%s)", f.file.String())
}

func (f *lockingFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Read(buf, off)
}

func (f *lockingFile) Write(data []byte, off int64) (uint32, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Write(data, off)
}

func (f *lockingFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Flush()
}

func (f *lockingFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.file.Release()
}

func (f *lockingFile) GetAttr(a *fuse.Attr) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.GetAttr(a)
}

func (f *lockingFile) Fsync(flags int) (code fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Fsync(flags)
}

func (f *lockingFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Utimens(atime, mtime)
}

func (f *lockingFile) Truncate(size uint64) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Truncate(size)
}

func (f *lockingFile) Chown(uid uint32, gid uint32) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Chown(uid, gid)
}

func (f *lockingFile) Chmod(perms uint32) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Chmod(perms)
}

func (f *lockingFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Allocate(off, size, mode)
}
<commit_msg>fuse/nodefs: fix NewLockingFile doc.<commit_after>package nodefs

import (
	"fmt"
	"sync"
	"time"

	"github.com/hanwen/go-fuse/fuse"
)

type lockingFile struct {
	mu   *sync.Mutex
	file File
}

// NewLockingFile serializes operations an existing File.
func NewLockingFile(mu *sync.Mutex, f File) File {
	return &lockingFile{
		mu:   mu,
		file: f,
	}
}

func (f *lockingFile) SetInode(*Inode) {
}

func (f *lockingFile) InnerFile() File {
	return f.file
}

func (f *lockingFile) String() string {
	return fmt.Sprintf("lockingFile(%s)", f.file.String())
}

func (f *lockingFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Read(buf, off)
}

func (f *lockingFile) Write(data []byte, off int64) (uint32, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Write(data, off)
}

func (f *lockingFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Flush()
}

func (f *lockingFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.file.Release()
}

func (f *lockingFile) GetAttr(a *fuse.Attr) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.GetAttr(a)
}

func (f *lockingFile) Fsync(flags int) (code fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Fsync(flags)
}

func (f *lockingFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Utimens(atime, mtime)
}

func (f *lockingFile) Truncate(size uint64) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Truncate(size)
}

func (f *lockingFile) Chown(uid uint32, gid uint32) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Chown(uid, gid)
}

func (f *lockingFile) Chmod(perms uint32) fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Chmod(perms)
}

func (f *lockingFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.file.Allocate(off, size, mode)
}
<|endoftext|>


<commit_before>
//Package provides the Data Access Layer (DAL) for the Hero Game.
package main

import (

	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"encoding/json"
	"os"
	"fmt"
	"strconv"
	log "github.com/Sirupsen/logrus"
	"math/rand"
)

//Db_Configuration struct holds the fields for the Database Connection string
type Db_configuration struct {

	Db_user	string 	`json:"Dbuser"`
	Db_pass	string 	`json:"Dbpass"`
	Db_ip	string 	`json:"Dbip"`
	Db_port	string	`json:"Dbport"`
	Db_name	string 	`json:"Dbname"`
}

// Db_connection returns the database connection string
// from a json configuration file
func Db_connection() string {
// Returns the Connection String for the Database

	file,err := os.Open("dbconfig.json")

	if err != nil {
		fmt.Println("File Error:", err)
	}

	decoder := json.NewDecoder(file)

	var db_conf = Db_configuration{}

	err2 := decoder.Decode(&db_conf)

	if err2 != nil {
		fmt.Println("Decoder Error:", err2)
	}

	return db_conf.Db_user + ":" + db_conf.Db_pass + "@tcp(" + db_conf.Db_ip + ":" + db_conf.Db_port + ")/" + db_conf.Db_name

}

func main() {

	var dbconn = Db_connection()

	log.Info("Database Connection: " + dbconn)

	sqldb, err := sql.Open("mysql", dbconn)

	if err != nil {
		log.Errorln("Database settings failed: %s", err)
	}

	defer sqldb.Close()

	// Open doesn't open a connection. Validate DSN data:
	err = sqldb.Ping()

	if err != nil {
		log.Errorln("Database Ping failed: %s", err)
	}

	//Find an Item for Hero 1
	for i := 0; i < 10; i++ {

		Find_item(5, rand.Intn(70), "Gandalf", sqldb)
	}

}

// Find_item generates a new item for the hero when they level up
// and notifies the player about the item found
func Find_item(heroID int, hero_level int, hero_name string, sqldb *sql.DB){

	//log.Info("Find Items called, Hero Level: " + strconv.Itoa(hero_level))

	items :=[10]string{"weapon","tunic","shield","leggins","ring","gloves","boots","helm","charm","amulet"}

	find_chance := [51]float32{ 100.00, 91.93227152,84.51542547,77.69695042,71.42857143,65.66590823,60.36816105,55.49782173,
		51.02040816,46.90422016,43.12011504,39.64130124,36.44314869,33.5030144,	30.80008217,28.31521517,26.03082049,
		23.93072457,22.00005869,20.22515369,18.59344321,17.0933747,	15.71432764,14.44653835,13.28103086,12.20955335,
		11.22451974,10.31895597,9.486450616,8.721109539,8.017514101,7.370682832,6.776036155,6.229363956,5.726795786,
		5.264773452,4.840025825,4.449545683,4.090568419,3.760552466,3.457161303,3.178246916,2.921834585,2.686108904,
		2.469400931,2.270176369,2.087024703,1.918649217,1.763857808,1.621554549,1.490731931	}

	var item_type int
	var item_level int
	var item_found_chance float32

	for i := hero_level; i > 0; i-- {

		if(i > 50) {
			//After Hero Level of 50, has a 1% chance to find an item.
			item_found_chance = 1.0

		} else {

			item_found_chance = find_chance[i]
		}
		//Start with highest Level Item and subtract a level as it misses the chance
		if(rand.Intn(100) <= int(item_found_chance)){

			item_gain_percentage := float64(rand.Intn(100))
			item_level = int(float64(i) + (float64(i) * (item_gain_percentage/100)))
			item_type = rand.Intn(10)

			log.Info("Item Found: " + items[item_type] + " | Hero Level: " + strconv.Itoa(hero_level) + " found @ Level: " + strconv.Itoa(i) + " Item Level: " + strconv.Itoa(item_level))

			break
		}
	}

	//check if No items where found

	var current_item_level = Get_Item_By_HeroID(heroID, items[item_type], sqldb)

	if item_level > current_item_level {

		//Replace the current item value with the new one

		//Message back to player that new item is better

	} else {

		//Message back to player that current item level is better

	}

}

func Get_Item_By_HeroID(heroID int, item_type string, conn *sql.DB) int {

	log.Info("Get_item_level_for_user ")

	var current_item_level int

	//Check what is the level of the current Item, update value if needed, msg player
	var query = "SELECT " + item_type + " FROM item WHERE hero_id = ?"

	log.Info("Select Query: " + query)

	stmt, err := conn.Prepare(query)
	if err != nil {
		log.Errorln("DB: Prepare Query failed: %s", err)
	}

	err = stmt.QueryRow(heroID).Scan(&current_item_level)

	if err != nil {
		log.Errorln("DB: QueryRow failed: %s", err)
	}

	log.Info("Item Value: " + strconv.Itoa(current_item_level))

	stmt.Close()

	return current_item_level
}

func Insert_Item_for_Hero(heroID int, item_type string, item_level int, conn *sql.DB) {}

func Insert_World_Event_for_Hero(heroID int, worldEvent string) {}

<commit_msg>completed the find_item code<commit_after>
//Package provides the Data Access Layer (DAL) for the Hero Game.
package main

import (

	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"encoding/json"
	"os"
	"fmt"
	"strconv"
	log "github.com/Sirupsen/logrus"
	"math/rand"
)

//Db_Configuration struct holds the fields for the Database Connection string
type Db_configuration struct {

	Db_user	string 	`json:"Dbuser"`
	Db_pass	string 	`json:"Dbpass"`
	Db_ip	string 	`json:"Dbip"`
	Db_port	string	`json:"Dbport"`
	Db_name	string 	`json:"Dbname"`
}

// Db_connection returns the database connection string
// from a json configuration file
func Db_connection() string {
// Returns the Connection String for the Database

	file,err := os.Open("dbconfig.json")

	if err != nil {
		fmt.Println("File Error:", err)
	}

	decoder := json.NewDecoder(file)

	var db_conf = Db_configuration{}

	err2 := decoder.Decode(&db_conf)

	if err2 != nil {
		fmt.Println("Decoder Error:", err2)
	}

	return db_conf.Db_user + ":" + db_conf.Db_pass + "@tcp(" + db_conf.Db_ip + ":" + db_conf.Db_port + ")/" + db_conf.Db_name

}

func main() {

	var dbconn = Db_connection()

	log.Info("Database Connection: " + dbconn)

	sqldb, err := sql.Open("mysql", dbconn)

	if err != nil {
		log.Errorln("Database settings failed: %s", err)
	}

	defer sqldb.Close()

	// Open doesn't open a connection. Validate DSN data:
	err = sqldb.Ping()

	if err != nil {
		log.Errorln("Database Ping failed: %s", err)
	}

	//Find an Item for Hero 1
	for i := 0; i < 100; i++ {
		log.Info("Hero # " + strconv.Itoa(i))
		Find_item(rand.Intn(100)+1, rand.Intn(70), "Gandalf", sqldb)
	}

}

// Find_item generates a new item for the hero when they level up
// and notifies the player about the item found
func Find_item(heroID int, hero_level int, hero_name string, conn *sql.DB){

	//log.Info("Find Items called, Hero Level: " + strconv.Itoa(hero_level))

	items :=[10]string{"weapon","tunic","shield","leggins","ring","gloves","boots","helm","charm","amulet"}

	find_chance := [51]float32{ 100.00, 91.93227152,84.51542547,77.69695042,71.42857143,65.66590823,60.36816105,55.49782173,
		51.02040816,46.90422016,43.12011504,39.64130124,36.44314869,33.5030144,	30.80008217,28.31521517,26.03082049,
		23.93072457,22.00005869,20.22515369,18.59344321,17.0933747,	15.71432764,14.44653835,13.28103086,12.20955335,
		11.22451974,10.31895597,9.486450616,8.721109539,8.017514101,7.370682832,6.776036155,6.229363956,5.726795786,
		5.264773452,4.840025825,4.449545683,4.090568419,3.760552466,3.457161303,3.178246916,2.921834585,2.686108904,
		2.469400931,2.270176369,2.087024703,1.918649217,1.763857808,1.621554549,1.490731931	}

	var item_type int
	var item_name string
	var item_level int
	var item_found_chance float32

	for i := hero_level; i > 0; i-- {

		if(i > 50) {
			//After Hero Level of 50, has a 1% chance to find an item.
			item_found_chance = 1.0

		} else {

			item_found_chance = find_chance[i]
		}
		//Start with highest Level Item and subtract a level as it misses the chance
		if(rand.Intn(100) <= int(item_found_chance)){

			item_gain_percentage := float64(rand.Intn(100))
			item_level = int(float64(i) + (float64(i) * (item_gain_percentage/100)))
			item_type = rand.Intn(10)
			item_name = items[item_type]

			var log_msg = "Item Found: " + item_name + " | Hero Level: " + strconv.Itoa(hero_level)+ " found @ Level: " + strconv.Itoa(i)+ " Item Level: " + strconv.Itoa(item_level)

			log.Info(log_msg)

			break
		}
	}

	//check if No items where found

	var current_item_level = Get_Item_By_HeroID(heroID, item_name, conn)

	log.Info("Items: Current: " + strconv.Itoa(current_item_level) + " | New: " + strconv.Itoa(item_level))

	var message string
	var message_plural string

	if (item_name == items[3] || item_name == items[5] || item_name == items[6]) {

		message_plural =  " are only level "

	} else {

		message_plural =  " is only level "
	}

	if item_level > current_item_level {

		//Replace the current item value with the new one
		Update_Item_for_Hero(heroID,item_name, item_level, conn)

		message = "You found a level " + strconv.Itoa(item_level) + " " + item_name + "! Your current " + item_name + message_plural +  strconv.Itoa(current_item_level) + ", so it seems Luck is with you!"

	} else {

		//Message back to player that current item level is better
		message = "You found a level " + strconv.Itoa(item_level) + " " + item_name + "! Your current " + item_name + message_plural +  strconv.Itoa(current_item_level) + ", so it seems Luck is against you. You toss the " + item_name + "."

	}

	log.Info(message)

	//Message back to player that new item is better
	Insert_World_Event_for_Hero(heroID, message, conn)

}

func Get_Item_By_HeroID(heroID int, item_type string, conn *sql.DB) int {

	log.Info("Get_item_level_for_user ")

	var current_item_level int

	//Check what is the level of the current Item, update value if needed, msg player
	var query = "SELECT " + item_type + " FROM item WHERE hero_id = ?"

	log.Info("Select Query: " + query)

	stmt, err := conn.Prepare(query)
	if err != nil {
		log.Errorln("DB: Get_Item_By_HeroID: Prepare Query failed: %s", err)
	}

	err = stmt.QueryRow(heroID).Scan(&current_item_level)

	if err != nil {
		log.Errorln("DB: Get_Item_By_HeroID: QueryRow failed: %s", err)
	}

	log.Info("Item Value: " + strconv.Itoa(current_item_level))

	defer stmt.Close()

	return current_item_level
}

func Update_Item_for_Hero(heroID int, item_type string, item_level int, conn *sql.DB) {

	log.Info("Updating Item: " + item_type + " with Level: " + strconv.Itoa(item_level) +" from Hero ID: " + strconv.Itoa(heroID))

	var query = "UPDATE item SET " + item_type + "=" + strconv.Itoa(item_level) +" WHERE hero_id="+ strconv.Itoa(heroID)

	_, err := conn.Exec(query)

	if err != nil {
		log.Errorln("DB: Insert_Item_for_Hero: Item Insert failed: %s", err)
	}

	log.Info("Executed: " + query)
}

func Insert_World_Event_for_Hero(heroID int, worldEvent string, conn *sql.DB) {

	log.Info("Inserting World Event: HeroID: " + strconv.Itoa(heroID) + " | Event: " + worldEvent)

	var query = "INSERT INTO worldevent (event_text) VALUES ('" + worldEvent + "')"

	log.Info("World Event Query: " + query)

	statement, err := conn.Exec(query)

	last_worldevent_Id, err := statement.LastInsertId()

	if err != nil {
		log.Errorln("DB: Insert_Item_for_Hero: Worldevent Insert failed: %s", err)
	}

	if last_worldevent_Id != 0 {

		Insert_Hero_World_Event(heroID, int(last_worldevent_Id), conn)
	}
}

func Insert_Hero_World_Event(heroID int, woldEvent_id int, conn *sql.DB) {

	var query = "INSERT INTO heroworldevent (hero_id, worldevent_id ) VALUES (" + strconv.Itoa(heroID) + ", " + strconv.Itoa(woldEvent_id) + ")"

	log.Info("Heroworldevent Query: " + query)

	_, err := conn.Exec(query)

	if err != nil {
		log.Errorln("DB: Insert_Item_for_Hero: Heroworldevent Insert failed: %s", err)
	}
}

<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake_test

import (
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcstesting"
	"github.com/jacobsa/ogletest"
)

func TestOgletest(t *testing.T) { ogletest.RunTests(t) }

func init() {
	gcstesting.RegisterBucketTests(func() gcs.Bucket {
		return gcstest.NewFakeBucket("some_bucket")
	})
}
<commit_msg>Fixed a build error.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake_test

import (
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsfake"
	"github.com/jacobsa/gcloud/gcs/gcstesting"
	"github.com/jacobsa/ogletest"
)

func TestOgletest(t *testing.T) { ogletest.RunTests(t) }

func init() {
	gcstesting.RegisterBucketTests(func() gcs.Bucket {
		return gcsfake.NewFakeBucket("some_bucket")
	})
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package harness

import (
	"bytes"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/mtime"
	"github.com/apache/beam/sdks/go/pkg/beam/core/metrics"
	"github.com/apache/beam/sdks/go/pkg/beam/core/runtime/exec"
	fnpb "github.com/apache/beam/sdks/go/pkg/beam/model/fnexecution_v1"
	ppb "github.com/apache/beam/sdks/go/pkg/beam/model/pipeline_v1"
	"github.com/golang/protobuf/ptypes"
)

type mUrn uint32
type mType uint32

// TODO: Pull these from the protos.
var sUrns = []string{
	"beam:metric:user:sum_int64:v1",
	"beam:metric:user:sum_double:v1",
	"beam:metric:user:distribution_int64:v1",
	"beam:metric:user:distribution_double:v1",
	"beam:metric:user:latest_int64:v1",
	"beam:metric:user:latest_double:v1",
	"beam:metric:user:top_n_int64:v1",
	"beam:metric:user:top_n_double:v1",
	"beam:metric:user:bottom_n_int64:v1",
	"beam:metric:user:bottom_n_double:v1",

	"beam:metric:element_count:v1",
	"beam:metric:sampled_byte_size:v1",

	"beam:metric:pardo_execution_time:start_bundle_msecs:v1",
	"beam:metric:pardo_execution_time:process_bundle_msecs:v1",
	"beam:metric:pardo_execution_time:finish_bundle_msecs:v1",
	"beam:metric:ptransform_execution_time:total_msecs:v1",

	"beam:metric:ptransform_progress:remaining:v1",
	"beam:metric:ptransform_progress:completed:v1",

	"TestingSentinelUrn", // Must remain last.
}

const (
	urnUserSumInt64 mUrn = iota
	urnUserSumFloat64
	urnUserDistInt64
	urnUserDistFloat64
	urnUserLatestMsInt64
	urnUserLatestMsFloat64
	urnUserTopNInt64
	urnUserTopNFloat64
	urnUserBottomNInt64
	urnUserBottomNFloat64

	urnElementCount
	urnSampledByteSize

	urnStartBundle
	urnProcessBundle
	urnFinishBundle
	urnTransformTotalTime

	urnProgressRemaining
	urnProgressCompleted

	urnTestSentinel // Must remain last.
)

var sTypes = []string{
	"beam:metrics:sum_int64:v1",
	"beam:metrics:sum_double:v1",
	"beam:metrics:distribution_int64:v1",
	"beam:metrics:distribution_double:v1",
	"beam:metrics:latest_int64:v1",
	"beam:metrics:latest_double:v1",
	"beam:metrics:top_n_int64:v1",
	"beam:metrics:top_n_double:v1",
	"beam:metrics:bottom_n_int64:v1",
	"beam:metrics:bottom_n_double:v1",
	"beam:metrics:monitoring_table:v1",
	"beam:metrics:progress:v1",

	"TestingSentinelType", // Must remain last.
}

const (
	typeSumInt64 mType = iota
	typeSumFloat64
	typeDistInt64
	typeDistFloat64
	typeLatestMsInt64
	typeLatestMsFloat64
	typeTopNInt64
	typeTopNFloat64
	typeBottomNInt64
	typeBottomNFloat64

	typeMonitoringTable
	typeProgress

	typeTestSentinel // Must remain last.
)

// urnToType maps the urn to it's encoding type.
// This function is written to be inlinable by the compiler.
func urnToType(u mUrn) mType {
	switch u {
	case urnUserSumInt64, urnElementCount, urnStartBundle, urnProcessBundle, urnFinishBundle, urnTransformTotalTime:
		return typeSumInt64
	case urnUserSumFloat64:
		return typeSumFloat64
	case urnUserDistInt64, urnSampledByteSize:
		return typeDistInt64
	case urnUserDistFloat64:
		return typeDistFloat64
	case urnUserLatestMsInt64:
		return typeLatestMsInt64
	case urnUserLatestMsFloat64:
		return typeLatestMsFloat64
	case urnUserTopNInt64:
		return typeTopNInt64
	case urnUserTopNFloat64:
		return typeTopNFloat64
	case urnUserBottomNInt64:
		return typeSumInt64
	case urnUserBottomNFloat64:
		return typeBottomNFloat64

	case urnProgressRemaining, urnProgressCompleted:
		return typeProgress

	case urnTestSentinel:
		return typeTestSentinel

	default:
		panic("metric urn without specified type" + sUrns[u])
	}
}

type shortKey struct {
	metrics.Labels
	Urn mUrn // Urns fully specify their type.
}

// shortIDCache retains lookup caches for short ids to the full monitoring
// info metadata.
//
// TODO: 2020/03/26 - measure mutex overhead vs sync.Map for this case.
// sync.Map might have lower contention for this read heavy load.
type shortIDCache struct {
	mu              sync.Mutex
	labels2ShortIds map[shortKey]string
	shortIds2Infos  map[string]*ppb.MonitoringInfo

	lastShortID int64
}

func newShortIDCache() *shortIDCache {
	return &shortIDCache{
		labels2ShortIds: make(map[shortKey]string),
		shortIds2Infos:  make(map[string]*ppb.MonitoringInfo),
	}
}

func (c *shortIDCache) getNextShortID() string {
	id := atomic.AddInt64(&c.lastShortID, 1)
	// No reason not to use the smallest string short ids possible.
	return strconv.FormatInt(id, 36)
}

// getShortID returns the short id for the given metric, and if
// it doesn't exist yet, stores the metadata.
// Assumes shortMu lock is held.
func (c *shortIDCache) getShortID(l metrics.Labels, urn mUrn) string {
	k := shortKey{l, urn}
	s, ok := c.labels2ShortIds[k]
	if ok {
		return s
	}
	s = c.getNextShortID()
	c.labels2ShortIds[k] = s
	c.shortIds2Infos[s] = &ppb.MonitoringInfo{
		Urn:    sUrns[urn],
		Type:   sTypes[urnToType(urn)],
		Labels: userLabels(l),
	}
	return s
}

func (c *shortIDCache) shortIdsToInfos(shortids []string) map[string]*ppb.MonitoringInfo {
	c.mu.Lock()
	defer c.mu.Unlock()
	m := make(map[string]*ppb.MonitoringInfo, len(shortids))
	for _, s := range shortids {
		m[s] = c.shortIds2Infos[s]
	}
	return m
}

// Convenience package functions for production.
var defaultShortIDCache *shortIDCache

func init() {
	defaultShortIDCache = newShortIDCache()
}

func getShortID(l metrics.Labels, urn mUrn) string {
	return defaultShortIDCache.getShortID(l, urn)
}

func shortIdsToInfos(shortids []string) map[string]*ppb.MonitoringInfo {
	return defaultShortIDCache.shortIdsToInfos(shortids)
}

func monitoring(p *exec.Plan) (*fnpb.Metrics, []*ppb.MonitoringInfo, map[string][]byte) {
	store := p.Store()
	if store == nil {
		return nil, nil, nil
	}

	// Get the legacy style metrics.
	transforms := make(map[string]*fnpb.Metrics_PTransform)
	metrics.Extractor{
		SumInt64: func(l metrics.Labels, v int64) {
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_CounterData_{
					CounterData: &fnpb.Metrics_User_CounterData{
						Value: v,
					},
				},
			})
		},
		DistributionInt64: func(l metrics.Labels, count, sum, min, max int64) {
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_DistributionData_{
					DistributionData: &fnpb.Metrics_User_DistributionData{
						Count: count,
						Sum:   sum,
						Min:   min,
						Max:   max,
					},
				},
			})
		},
		GaugeInt64: func(l metrics.Labels, v int64, t time.Time) {
			ts, err := ptypes.TimestampProto(t)
			if err != nil {
				panic(err)
			}
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_GaugeData_{
					GaugeData: &fnpb.Metrics_User_GaugeData{
						Value:     v,
						Timestamp: ts,
					},
				},
			})
		},
	}.ExtractFrom(store)

	defaultShortIDCache.mu.Lock()
	defer defaultShortIDCache.mu.Unlock()

	// Get the MonitoringInfo versions.
	var monitoringInfo []*ppb.MonitoringInfo
	payloads := make(map[string][]byte)
	metrics.Extractor{
		SumInt64: func(l metrics.Labels, v int64) {
			payload, err := int64Counter(v)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserSumInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserSumInt64],
					Type:    sTypes[typeSumInt64],
					Labels:  userLabels(l),
					Payload: payload,
				})
		},
		DistributionInt64: func(l metrics.Labels, count, sum, min, max int64) {
			payload, err := int64Distribution(count, sum, min, max)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserDistInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserDistInt64],
					Type:    sTypes[typeDistInt64],
					Labels:  userLabels(l),
					Payload: payload,
				})
		},
		GaugeInt64: func(l metrics.Labels, v int64, t time.Time) {
			payload, err := int64Latest(t, v)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserLatestMsInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserLatestMsInt64],
					Type:    sTypes[typeLatestMsInt64],
					Labels:  userLabels(l),
					Payload: payload,
				})

		},
	}.ExtractFrom(store)

	// Get the execution monitoring information from the bundle plan.
	if snapshot, ok := p.Progress(); ok {
		// Legacy version.
		transforms[snapshot.ID] = &fnpb.Metrics_PTransform{
			ProcessedElements: &fnpb.Metrics_PTransform_ProcessedElements{
				Measured: &fnpb.Metrics_PTransform_Measured{
					OutputElementCounts: map[string]int64{
						snapshot.Name: snapshot.Count,
					},
				},
			},
		}
		// Monitoring info version.
		payload, err := int64Counter(snapshot.Count)
		if err == nil {
			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:  sUrns[urnElementCount],
					Type: sTypes[typeSumInt64],
					Labels: map[string]string{
						"PCOLLECTION": snapshot.PID,
					},
					Payload: payload,
				})
		}
	}

	return &fnpb.Metrics{
			Ptransforms: transforms,
		}, monitoringInfo,
		payloads
}

func userLabels(l metrics.Labels) map[string]string {
	return map[string]string{
		"PTRANSFORM": l.Transform(),
		"NAMESPACE":  l.Namespace(),
		"NAME":       l.Name(),
	}
}

func int64Counter(v int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(v, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func int64Latest(t time.Time, v int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(mtime.FromTime(t).Milliseconds(), &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(v, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func int64Distribution(count, sum, min, max int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(count, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(sum, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(min, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(max, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func getTransform(transforms map[string]*fnpb.Metrics_PTransform, l metrics.Labels) *fnpb.Metrics_PTransform {
	if pb, ok := transforms[l.Transform()]; ok {
		return pb
	}
	pb := &fnpb.Metrics_PTransform{}
	transforms[l.Transform()] = pb
	return pb
}

func toName(l metrics.Labels) *fnpb.Metrics_User_MetricName {
	return &fnpb.Metrics_User_MetricName{
		Name:      l.Name(),
		Namespace: l.Namespace(),
	}
}
<commit_msg>Remove mType and move type urns to urnToType<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package harness

import (
	"bytes"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/mtime"
	"github.com/apache/beam/sdks/go/pkg/beam/core/metrics"
	"github.com/apache/beam/sdks/go/pkg/beam/core/runtime/exec"
	fnpb "github.com/apache/beam/sdks/go/pkg/beam/model/fnexecution_v1"
	ppb "github.com/apache/beam/sdks/go/pkg/beam/model/pipeline_v1"
	"github.com/golang/protobuf/ptypes"
)

type mUrn uint32

// TODO: Pull these from the protos.
var sUrns = [...]string{
	"beam:metric:user:sum_int64:v1",
	"beam:metric:user:sum_double:v1",
	"beam:metric:user:distribution_int64:v1",
	"beam:metric:user:distribution_double:v1",
	"beam:metric:user:latest_int64:v1",
	"beam:metric:user:latest_double:v1",
	"beam:metric:user:top_n_int64:v1",
	"beam:metric:user:top_n_double:v1",
	"beam:metric:user:bottom_n_int64:v1",
	"beam:metric:user:bottom_n_double:v1",

	"beam:metric:element_count:v1",
	"beam:metric:sampled_byte_size:v1",

	"beam:metric:pardo_execution_time:start_bundle_msecs:v1",
	"beam:metric:pardo_execution_time:process_bundle_msecs:v1",
	"beam:metric:pardo_execution_time:finish_bundle_msecs:v1",
	"beam:metric:ptransform_execution_time:total_msecs:v1",

	"beam:metric:ptransform_progress:remaining:v1",
	"beam:metric:ptransform_progress:completed:v1",

	"TestingSentinelUrn", // Must remain last.
}

const (
	urnUserSumInt64 mUrn = iota
	urnUserSumFloat64
	urnUserDistInt64
	urnUserDistFloat64
	urnUserLatestMsInt64
	urnUserLatestMsFloat64
	urnUserTopNInt64
	urnUserTopNFloat64
	urnUserBottomNInt64
	urnUserBottomNFloat64

	urnElementCount
	urnSampledByteSize

	urnStartBundle
	urnProcessBundle
	urnFinishBundle
	urnTransformTotalTime

	urnProgressRemaining
	urnProgressCompleted

	urnTestSentinel // Must remain last.
)

// urnToType maps the urn to it's encoding type.
// This function is written to be inlinable by the compiler.
func urnToType(u mUrn) string {
	switch u {
	case urnUserSumInt64, urnElementCount, urnStartBundle, urnProcessBundle, urnFinishBundle, urnTransformTotalTime:
		return "beam:metrics:sum_int64:v1"
	case urnUserSumFloat64:
		return "beam:metrics:sum_double:v1"
	case urnUserDistInt64, urnSampledByteSize:
		return "beam:metrics:distribution_int64:v1"
	case urnUserDistFloat64:
		return "beam:metrics:distribution_double:v1"
	case urnUserLatestMsInt64:
		return "beam:metrics:latest_int64:v1"
	case urnUserLatestMsFloat64:
		return "beam:metrics:latest_double:v1"
	case urnUserTopNInt64:
		return "beam:metrics:top_n_int64:v1"
	case urnUserTopNFloat64:
		return "beam:metrics:top_n_double:v1"
	case urnUserBottomNInt64:
		return "beam:metrics:bottom_n_int64:v1"
	case urnUserBottomNFloat64:
		return "beam:metrics:bottom_n_double:v1"

	case urnProgressRemaining, urnProgressCompleted:
		return "beam:metrics:progress:v1"

	// Monitoring Table isn't currently in the protos.
	// case ???:
	//	return "beam:metrics:monitoring_table:v1"

	case urnTestSentinel:
		return "TestingSentinelType"

	default:
		panic("metric urn without specified type" + sUrns[u])
	}
}

type shortKey struct {
	metrics.Labels
	Urn mUrn // Urns fully specify their type.
}

// shortIDCache retains lookup caches for short ids to the full monitoring
// info metadata.
//
// TODO: 2020/03/26 - measure mutex overhead vs sync.Map for this case.
// sync.Map might have lower contention for this read heavy load.
type shortIDCache struct {
	mu              sync.Mutex
	labels2ShortIds map[shortKey]string
	shortIds2Infos  map[string]*ppb.MonitoringInfo

	lastShortID int64
}

func newShortIDCache() *shortIDCache {
	return &shortIDCache{
		labels2ShortIds: make(map[shortKey]string),
		shortIds2Infos:  make(map[string]*ppb.MonitoringInfo),
	}
}

func (c *shortIDCache) getNextShortID() string {
	id := atomic.AddInt64(&c.lastShortID, 1)
	// No reason not to use the smallest string short ids possible.
	return strconv.FormatInt(id, 36)
}

// getShortID returns the short id for the given metric, and if
// it doesn't exist yet, stores the metadata.
// Assumes c.mu lock is held.
func (c *shortIDCache) getShortID(l metrics.Labels, urn mUrn) string {
	k := shortKey{l, urn}
	s, ok := c.labels2ShortIds[k]
	if ok {
		return s
	}
	s = c.getNextShortID()
	c.labels2ShortIds[k] = s
	c.shortIds2Infos[s] = &ppb.MonitoringInfo{
		Urn:    sUrns[urn],
		Type:   urnToType(urn),
		Labels: userLabels(l),
	}
	return s
}

func (c *shortIDCache) shortIdsToInfos(shortids []string) map[string]*ppb.MonitoringInfo {
	c.mu.Lock()
	defer c.mu.Unlock()
	m := make(map[string]*ppb.MonitoringInfo, len(shortids))
	for _, s := range shortids {
		m[s] = c.shortIds2Infos[s]
	}
	return m
}

// Convenience package functions for production.
var defaultShortIDCache *shortIDCache

func init() {
	defaultShortIDCache = newShortIDCache()
}

func getShortID(l metrics.Labels, urn mUrn) string {
	return defaultShortIDCache.getShortID(l, urn)
}

func shortIdsToInfos(shortids []string) map[string]*ppb.MonitoringInfo {
	return defaultShortIDCache.shortIdsToInfos(shortids)
}

func monitoring(p *exec.Plan) (*fnpb.Metrics, []*ppb.MonitoringInfo, map[string][]byte) {
	store := p.Store()
	if store == nil {
		return nil, nil, nil
	}

	// Get the legacy style metrics.
	transforms := make(map[string]*fnpb.Metrics_PTransform)
	metrics.Extractor{
		SumInt64: func(l metrics.Labels, v int64) {
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_CounterData_{
					CounterData: &fnpb.Metrics_User_CounterData{
						Value: v,
					},
				},
			})
		},
		DistributionInt64: func(l metrics.Labels, count, sum, min, max int64) {
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_DistributionData_{
					DistributionData: &fnpb.Metrics_User_DistributionData{
						Count: count,
						Sum:   sum,
						Min:   min,
						Max:   max,
					},
				},
			})
		},
		GaugeInt64: func(l metrics.Labels, v int64, t time.Time) {
			ts, err := ptypes.TimestampProto(t)
			if err != nil {
				panic(err)
			}
			pb := getTransform(transforms, l)
			pb.User = append(pb.User, &fnpb.Metrics_User{
				MetricName: toName(l),
				Data: &fnpb.Metrics_User_GaugeData_{
					GaugeData: &fnpb.Metrics_User_GaugeData{
						Value:     v,
						Timestamp: ts,
					},
				},
			})
		},
	}.ExtractFrom(store)

	defaultShortIDCache.mu.Lock()
	defer defaultShortIDCache.mu.Unlock()

	// Get the MonitoringInfo versions.
	var monitoringInfo []*ppb.MonitoringInfo
	payloads := make(map[string][]byte)
	metrics.Extractor{
		SumInt64: func(l metrics.Labels, v int64) {
			payload, err := int64Counter(v)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserSumInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserSumInt64],
					Type:    urnToType(urnUserSumInt64),
					Labels:  userLabels(l),
					Payload: payload,
				})
		},
		DistributionInt64: func(l metrics.Labels, count, sum, min, max int64) {
			payload, err := int64Distribution(count, sum, min, max)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserDistInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserDistInt64],
					Type:    urnToType(urnUserDistInt64),
					Labels:  userLabels(l),
					Payload: payload,
				})
		},
		GaugeInt64: func(l metrics.Labels, v int64, t time.Time) {
			payload, err := int64Latest(t, v)
			if err != nil {
				panic(err)
			}
			payloads[getShortID(l, urnUserLatestMsInt64)] = payload

			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:     sUrns[urnUserLatestMsInt64],
					Type:    urnToType(urnUserLatestMsInt64),
					Labels:  userLabels(l),
					Payload: payload,
				})

		},
	}.ExtractFrom(store)

	// Get the execution monitoring information from the bundle plan.
	if snapshot, ok := p.Progress(); ok {
		// Legacy version.
		transforms[snapshot.ID] = &fnpb.Metrics_PTransform{
			ProcessedElements: &fnpb.Metrics_PTransform_ProcessedElements{
				Measured: &fnpb.Metrics_PTransform_Measured{
					OutputElementCounts: map[string]int64{
						snapshot.Name: snapshot.Count,
					},
				},
			},
		}
		// Monitoring info version.
		payload, err := int64Counter(snapshot.Count)
		if err == nil {
			monitoringInfo = append(monitoringInfo,
				&ppb.MonitoringInfo{
					Urn:  sUrns[urnElementCount],
					Type: urnToType(urnElementCount),
					Labels: map[string]string{
						"PCOLLECTION": snapshot.PID,
					},
					Payload: payload,
				})
		}
	}

	return &fnpb.Metrics{
			Ptransforms: transforms,
		}, monitoringInfo,
		payloads
}

func userLabels(l metrics.Labels) map[string]string {
	return map[string]string{
		"PTRANSFORM": l.Transform(),
		"NAMESPACE":  l.Namespace(),
		"NAME":       l.Name(),
	}
}

func int64Counter(v int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(v, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func int64Latest(t time.Time, v int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(mtime.FromTime(t).Milliseconds(), &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(v, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func int64Distribution(count, sum, min, max int64) ([]byte, error) {
	var buf bytes.Buffer
	if err := coder.EncodeVarInt(count, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(sum, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(min, &buf); err != nil {
		return nil, err
	}
	if err := coder.EncodeVarInt(max, &buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func getTransform(transforms map[string]*fnpb.Metrics_PTransform, l metrics.Labels) *fnpb.Metrics_PTransform {
	if pb, ok := transforms[l.Transform()]; ok {
		return pb
	}
	pb := &fnpb.Metrics_PTransform{}
	transforms[l.Transform()] = pb
	return pb
}

func toName(l metrics.Labels) *fnpb.Metrics_User_MetricName {
	return &fnpb.Metrics_User_MetricName{
		Name:      l.Name(),
		Namespace: l.Namespace(),
	}
}
<|endoftext|>