<commit_before>package lapi

import (
	"net/http"
)

// Request represents for an application's request
type Request interface {
	RequestCookies
	RequestInputer
	RequestResolver
	RequestParameter
	RequestAncestor
}

// RequestAncestor keeps original http.Request
type RequestAncestor interface {
	Ancestor() *http.Request
}

// RequestResolver returns routing information
type RequestResolver interface {
	// Route returns matched route for request
	Route() Route

	// WithRoute sets request's routes
	WithRoute(route Route) Request
}

// RequestCookies handles request's cookies
type RequestCookies interface {
	// Cookie returns an appropriate cookie by name
	Cookie(name string) (*http.Cookie, bool)

	// WithCookie sets cookie
	WithCookie(cookie *http.Cookie) Request

	// Cookies returns all cookies
	Cookies() map[string]*http.Cookie

	// WithCookies sets request's cookies
	WithCookies(cookies []*http.Cookie) Request
}

// RequestInputer handles request's input (body)
type RequestInputer interface {
	// Input returns request's input
	Input() interface{}

	// WithInput sets request's input
	WithInput(input interface{}) Request
}

// RequestParameter handles request's query parameters and additional parameters
type RequestParameter interface {
	// Param returns value of a proposed key if exists, ok will be false if key is not found
	Param(key string) (value interface{}, ok bool)

	// WithParam sets parameter by key
	WithParam(key string, value interface{}) Request
}

func NewRequest(ancestor *http.Request) Request {
	return &FactoryRequest{
		ancestor: ancestor,
		cookies:  make(map[string]*http.Cookie),
		params:   NewBag(),
	}
}

type FactoryRequest struct {
	ancestor *http.Request
	input    interface{}
	cookies  map[string]*http.Cookie
	params   Bag
	route    Route
}

func (r *FactoryRequest) Ancestor() *http.Request {
	return r.ancestor
}

func (r *FactoryRequest) Route() Route {
	return r.route
}

func (r *FactoryRequest) WithRoute(route Route) Request {
	r.route = route
	return r
}

func (r *FactoryRequest) Cookie(name string) (*http.Cookie, bool) {
	cookie, ok := r.cookies[name]
	return cookie, ok
}

func (r *FactoryRequest) WithCookie(cookie *http.Cookie) Request {
	r.cookies[cookie.Name] = cookie
	return r
}

func (r *FactoryRequest) Cookies() map[string]*http.Cookie {
	return r.cookies
}

func (r *FactoryRequest) WithCookies(cookies []*http.Cookie) Request {
	r.cookies = make(map[string]*http.Cookie)
	for _, cookie := range cookies {
		r.WithCookie(cookie)
	}
	return r
}

func (r *FactoryRequest) Input() interface{} {
	return r.input
}

func (r *FactoryRequest) WithInput(input interface{}) Request {
	r.input = input
	return r
}

func (r *FactoryRequest) Param(key string) (value interface{}, ok bool) {
	return r.params.Get(key)
}

func (r *FactoryRequest) WithParam(key string, value interface{}) Request {
	r.params.Set(key, value)
	return r
}
<commit_msg>Update request with header<commit_after>package lapi

import (
	"net/http"
)

// Request represents for an application's request
type Request interface {
	RequestHeader
	RequestCookies
	RequestInputer
	RequestAncestor
	RequestResolver
	RequestParameter
}

// RequestAncestor keeps original http.Request
type RequestAncestor interface {
	Ancestor() *http.Request
}

// RequestResolver returns routing information
type RequestResolver interface {
	// Route returns matched route for request
	Route() Route

	// WithRoute sets request's routes
	WithRoute(route Route) Request
}

// RequestHeader manages request's header
type RequestHeader interface {
	// Header returns an instance of Header
	Header() Header

	// WithHeader allows to set Header
	WithHeader(header Header) Request
}

// RequestCookies handles request's cookies
type RequestCookies interface {
	// Cookie returns an appropriate cookie by name
	Cookie(name string) (*http.Cookie, bool)

	// WithCookie sets cookie
	WithCookie(cookie *http.Cookie) Request

	// Cookies returns all cookies
	Cookies() map[string]*http.Cookie

	// WithCookies sets request's cookies
	WithCookies(cookies []*http.Cookie) Request
}

// RequestInputer handles request's input (body)
type RequestInputer interface {
	// Input returns request's input
	Input() interface{}

	// WithInput sets request's input
	WithInput(input interface{}) Request
}

// RequestParameter handles request's query parameters and additional parameters
type RequestParameter interface {
	// Param returns value of a proposed key if exists, ok will be false if key is not found
	Param(key string) (value interface{}, ok bool)

	// WithParam sets parameter by key
	WithParam(key string, value interface{}) Request
}

func NewRequest(req *http.Request) Request {
	r := &FactoryRequest{
		ancestor: req,
		cookies:  make(map[string]*http.Cookie),
		params:   NewBag(),
	}
	r.parseRequest()
	return r
}

type FactoryRequest struct {
	ancestor *http.Request
	header   Header
	input    interface{}
	cookies  map[string]*http.Cookie
	params   Bag
	route    Route
}

func (r *FactoryRequest) Ancestor() *http.Request {
	return r.ancestor
}

func (r *FactoryRequest) Route() Route {
	return r.route
}

func (r *FactoryRequest) WithRoute(route Route) Request {
	r.route = route
	return r
}

func (r *FactoryRequest) Header() Header {
	return r.header
}

func (r *FactoryRequest) WithHeader(header Header) Request {
	r.header = header
	return r
}

func (r *FactoryRequest) Cookie(name string) (*http.Cookie, bool) {
	cookie, ok := r.cookies[name]
	return cookie, ok
}

func (r *FactoryRequest) WithCookie(cookie *http.Cookie) Request {
	r.cookies[cookie.Name] = cookie
	return r
}

func (r *FactoryRequest) Cookies() map[string]*http.Cookie {
	return r.cookies
}

func (r *FactoryRequest) WithCookies(cookies []*http.Cookie) Request {
	r.cookies = make(map[string]*http.Cookie)
	for _, cookie := range cookies {
		r.WithCookie(cookie)
	}
	return r
}

func (r *FactoryRequest) Input() interface{} {
	return r.input
}

func (r *FactoryRequest) WithInput(input interface{}) Request {
	r.input = input
	return r
}

func (r *FactoryRequest) Param(key string) (value interface{}, ok bool) {
	return r.params.Get(key)
}

func (r *FactoryRequest) WithParam(key string, value interface{}) Request {
	r.params.Set(key, value)
	return r
}

func (r *FactoryRequest) parseRequest() {
	r.parseRequestHeader()
}

func (r *FactoryRequest) parseRequestHeader() {
	for key := range r.ancestor.Header {
		r.Header().Set(key, r.ancestor.Header.Get(key))
	}
}
<|endoftext|>


<commit_before>package resolv

import (
	"net"
	"strings"

	"github.com/miekg/dns"
)

// Request represents a DNS request.
type Request struct {
	Addr    string // Remote host:port
	Mode    string // Mode tcp/udp
	Name    string // Query name
	Type    uint16 // Query type
	Class   uint16 // Query class
	Recurse bool   // Recursion desired
}

// RequestOption represents an option function.
type RequestOption func(*Request)

// SetTCPMode configures the query to run in TCP mode.
func SetTCPMode(req *Request) {
	req.Mode = "tcp"
}

// SetCHAOSClass sets the class to CHAOS for a request.
func SetCHAOSClass(req *Request) {
	req.Class = dns.ClassCHAOS
}

// SetRD enables recursion for a request.
func SetRD(req *Request) {
	req.Recurse = true
}

func NewRequest(addr, name string, type_ uint16, options ...RequestOption) *Request {
	if !strings.Contains(addr, ":") {
		addr = net.JoinHostPort(addr, DefaultPort)
	}

	req := &Request{
		Addr:  addr,
		Name:  dns.Fqdn(strings.ToLower(name)),
		Type:  type_,
		Class: dns.ClassINET,
	}

	// Apply configuration functions
	for _, opt := range options {
		opt(req)
	}

	return req
}
<commit_msg>Fix port detection<commit_after>package resolv

import (
	"net"
	"strings"

	"github.com/miekg/dns"
)

// Request represents a DNS request.
type Request struct {
	Addr    string // Remote host:port
	Mode    string // Mode tcp/udp
	Name    string // Query name
	Type    uint16 // Query type
	Class   uint16 // Query class
	Recurse bool   // Recursion desired
}

// RequestOption represents an option function.
type RequestOption func(*Request)

// SetTCPMode configures the query to run in TCP mode.
func SetTCPMode(req *Request) {
	req.Mode = "tcp"
}

// SetCHAOSClass sets the class to CHAOS for a request.
func SetCHAOSClass(req *Request) {
	req.Class = dns.ClassCHAOS
}

// SetRD enables recursion for a request.
func SetRD(req *Request) {
	req.Recurse = true
}

func NewRequest(addr, name string, type_ uint16, options ...RequestOption) *Request {
	if _, _, err := net.SplitHostPort(addr); err != nil {
		addr = net.JoinHostPort(addr, DefaultPort)

	}

	req := &Request{
		Addr:  addr,
		Name:  dns.Fqdn(strings.ToLower(name)),
		Type:  type_,
		Class: dns.ClassINET,
	}

	// Apply configuration functions
	for _, opt := range options {
		opt(req)
	}

	return req
}
<|endoftext|>


<commit_before>package sugoi
import (
	"strconv"
	"net/http"
)

func NewRequestFromHttp(attrs map[string]string, req *http.Request) *Request {
	return &Request{
		attrs: attrs,
		httpRequest: req,
	}
}

type Request struct {
	attrs 		map[string]string
	httpRequest *http.Request
	// Query
	// Cookies
	// Sessions
	// get attribute
	// set attribute
}

func (c *Request) GetHttpRequest() *http.Request {
	return c.httpRequest
}

func (c *Request) GetAttributes() map[string]string {

	return c.attrs
}

func (c *Request) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *Request) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

func NewWrappedHandler() (*WrappedHandler) {
	return &WrappedHandler{
		routes : []*Route{},
		beforeFilters: []BeforeFilter{},
		defaultHandlers: make(map[int]RouteHandler),
	}
}<commit_msg>By default, set staticUrl and staticDir<commit_after>package sugoi
import (
	"strconv"
	"net/http"
)

func NewRequestFromHttp(attrs map[string]string, req *http.Request) *Request {
	return &Request{
		attrs: attrs,
		httpRequest: req,
	}
}

type Request struct {
	attrs 		map[string]string
	httpRequest *http.Request
	// Query
	// Cookies
	// Sessions
	// get attribute
	// set attribute
}

func (c *Request) GetHttpRequest() *http.Request {
	return c.httpRequest
}

func (c *Request) GetAttributes() map[string]string {
	return c.attrs
}

func (c *Request) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *Request) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

func NewWrappedHandler() (*WrappedHandler) {
	return &WrappedHandler{
		routes : []*Route{},
		beforeFilters: []BeforeFilter{},
		defaultHandlers: make(map[int]RouteHandler),
		staticUrl: "/static",
		staticDir: "static",
	}
}<|endoftext|>


<commit_before>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// Action defines the basic intent of the request.
type Action int

// The following actions translate to standard HTTP methods:
const (
	_ Action = iota
	Fetch
	Create
	Update
	Delete
)

var methodActionMap = map[string]Action{
	"GET":    Fetch,
	"POST":   Create,
	"PATCH":  Update,
	"DELETE": Delete,
}

// Target specifies the format of the primary data the request ist targeting.
type Target int

// The following targets translate to JSON API URL patterns:
const (
	_ Target = iota
	ResourceCollection
	SingleResource
	RelatedResource
	Relationship
)

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// Action
	Action Action

	// Target
	Target Target

	// TODO: Add Request Intent like FetchCollection, FetchSingleResource?

	// Location
	ResourceType    string
	ResourceID      string
	RelatedResource string
	Relationship    string

	// Inclusion of Related Resources
	Include []string

	// Pagination
	PageNumber int
	PageSize   int

	// Sorting
	Sorting []string

	// Sparse Fieldsets
	Fields map[string][]string

	// Filtering
	Filters map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type or url is invalid.
//
// Note: The returned error can directly be written using WriteError.
func ParseRequest(req *http.Request, prefix string) (*Request, error) {
	// set overridden method if available
	if req.Header.Get("X-HTTP-Method-Override") != "" {
		req.Method = req.Header.Get("X-HTTP-Method-Override")
	}

	// map method to action
	action, ok := methodActionMap[req.Method]
	if !ok {
		return nil, badRequest("Unsupported method")
	}

	// allocate new request
	r := &Request{}

	// write action
	r.Action = action

	// check content type header
	contentType := req.Header.Get("Content-Type")
	if contentType != "" && contentType != ContentType {
		return nil, badRequest("Invalid content type header")
	}

	// check if request should come with a document and has content type set
	if r.DocumentExpected() && contentType == "" {
		return nil, badRequest("Missing content type header")
	}

	// check accept header
	accept := req.Header.Get("Accept")
	if accept != ContentType {
		return nil, badRequest("Invalid accept header")
	}

	// de-prefix and trim path
	url := strings.Trim(strings.TrimPrefix(req.URL.Path, prefix), "/")

	// split path
	segments := strings.Split(url, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, badRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, badRequest("Found empty URL segments")
		}
	}

	// set resource
	r.ResourceType = segments[0]
	r.Target = ResourceCollection

	// set resource id
	if len(segments) > 1 {
		r.ResourceID = segments[1]
		r.Target = SingleResource
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		r.RelatedResource = segments[2]
		r.Target = RelatedResource
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		r.Relationship = segments[3]
		r.Target = Relationship
	}

	// final check
	if len(segments) > 2 && (r.RelatedResource == "" && r.Relationship == "") {
		return nil, badRequest("Invalid URL relationship format")
	}

	// TODO: Check if action is generally allowed on the URL?

	for key, values := range req.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				r.Include = append(r.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				r.Sorting = append(r.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, badRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, badRequestParam("Not a number", "page[number]")
			}

			r.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, badRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, badRequestParam("Not a number", "page[size]")
			}

			r.PageSize = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if r.Fields == nil {
				r.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Fields[typ] = append(r.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if r.Filters == nil {
				r.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Filters[typ] = append(r.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check page size
	if r.PageNumber > 0 && r.PageSize <= 0 {
		return nil, badRequestParam("Missing page size", "page[number]")
	}

	// check page number
	if r.PageSize > 0 && r.PageNumber <= 0 {
		return nil, badRequestParam("Missing page number", "page[size]")
	}

	return r, nil
}

// DocumentExpected returns whether the request is expected to come with a
// JSON API document.
func (r *Request) DocumentExpected() bool {
	return r.Action == Create || r.Action == Update
}

func badRequest(detail string) *Error {
	return &Error{
		Status: http.StatusBadRequest,
		Title:  http.StatusText(http.StatusBadRequest),
		Detail: detail,
	}
}

func badRequestParam(detail, param string) *Error {
	return &Error{
		Status: http.StatusBadRequest,
		Title:  http.StatusText(http.StatusBadRequest),
		Detail: detail,
		Source: &ErrorSource{
			Parameter: param,
		},
	}
}
<commit_msg>do not alter the request method<commit_after>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// Action defines the basic intent of the request.
type Action int

// The following actions translate to standard HTTP methods:
const (
	_ Action = iota
	Fetch
	Create
	Update
	Delete
)

var methodActionMap = map[string]Action{
	"GET":    Fetch,
	"POST":   Create,
	"PATCH":  Update,
	"DELETE": Delete,
}

// Target specifies the format of the primary data the request ist targeting.
type Target int

// The following targets translate to JSON API URL patterns:
const (
	_ Target = iota
	ResourceCollection
	SingleResource
	RelatedResource
	Relationship
)

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// Action
	Action Action

	// Target
	Target Target

	// TODO: Add Request Intent like FetchCollection, FetchSingleResource?

	// Location
	ResourceType    string
	ResourceID      string
	RelatedResource string
	Relationship    string

	// Inclusion of Related Resources
	Include []string

	// Pagination
	PageNumber int
	PageSize   int

	// Sorting
	Sorting []string

	// Sparse Fieldsets
	Fields map[string][]string

	// Filtering
	Filters map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type or url is invalid.
//
// Note: The returned error can directly be written using WriteError.
func ParseRequest(req *http.Request, prefix string) (*Request, error) {
	// get method
	method := req.Method

	// set overridden method if available
	if req.Header.Get("X-HTTP-Method-Override") != "" {
		method = req.Header.Get("X-HTTP-Method-Override")
	}

	// map method to action
	action, ok := methodActionMap[method]
	if !ok {
		return nil, badRequest("Unsupported method")
	}

	// allocate new request
	r := &Request{}

	// write action
	r.Action = action

	// check content type header
	contentType := req.Header.Get("Content-Type")
	if contentType != "" && contentType != ContentType {
		return nil, badRequest("Invalid content type header")
	}

	// check if request should come with a document and has content type set
	if r.DocumentExpected() && contentType == "" {
		return nil, badRequest("Missing content type header")
	}

	// check accept header
	accept := req.Header.Get("Accept")
	if accept != ContentType {
		return nil, badRequest("Invalid accept header")
	}

	// de-prefix and trim path
	url := strings.Trim(strings.TrimPrefix(req.URL.Path, prefix), "/")

	// split path
	segments := strings.Split(url, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, badRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, badRequest("Found empty URL segments")
		}
	}

	// set resource
	r.ResourceType = segments[0]
	r.Target = ResourceCollection

	// set resource id
	if len(segments) > 1 {
		r.ResourceID = segments[1]
		r.Target = SingleResource
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		r.RelatedResource = segments[2]
		r.Target = RelatedResource
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		r.Relationship = segments[3]
		r.Target = Relationship
	}

	// final check
	if len(segments) > 2 && (r.RelatedResource == "" && r.Relationship == "") {
		return nil, badRequest("Invalid URL relationship format")
	}

	// TODO: Check if action is generally allowed on the URL?

	for key, values := range req.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				r.Include = append(r.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				r.Sorting = append(r.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, badRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, badRequestParam("Not a number", "page[number]")
			}

			r.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, badRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, badRequestParam("Not a number", "page[size]")
			}

			r.PageSize = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if r.Fields == nil {
				r.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Fields[typ] = append(r.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if r.Filters == nil {
				r.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Filters[typ] = append(r.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check page size
	if r.PageNumber > 0 && r.PageSize <= 0 {
		return nil, badRequestParam("Missing page size", "page[number]")
	}

	// check page number
	if r.PageSize > 0 && r.PageNumber <= 0 {
		return nil, badRequestParam("Missing page number", "page[size]")
	}

	return r, nil
}

// DocumentExpected returns whether the request is expected to come with a
// JSON API document.
func (r *Request) DocumentExpected() bool {
	return r.Action == Create || r.Action == Update
}

func badRequest(detail string) *Error {
	return &Error{
		Status: http.StatusBadRequest,
		Title:  http.StatusText(http.StatusBadRequest),
		Detail: detail,
	}
}

func badRequestParam(detail, param string) *Error {
	return &Error{
		Status: http.StatusBadRequest,
		Title:  http.StatusText(http.StatusBadRequest),
		Detail: detail,
		Source: &ErrorSource{
			Parameter: param,
		},
	}
}
<|endoftext|>


<commit_before>// Packege pidctrl implements a PID controller.
//
// see http://en.wikipedia.org/wiki/PID_controller
package pidctrl

import (
	"time"
)

type DeriveMethodType int

const (
	DeriveOnErr DeriveMethodType = iota
	DeriveOnValue
)

// NewPIDController returns a new PIDController using the given gain values.
func NewPIDController(p, i, d float64) *PIDController {
	return &PIDController{p: p, i: i, d: d}
}

// PIDController implements a PID controller.
type PIDController struct {
	p            float64          // proportional gain
	i            float64          // integral gain
	d            float64          // derrivate gain
	setpoint     float64          // current setpoint
	prevSetpoint float64          // last setpoint
	prevValue    float64          // last process value
	prevErr      float64          // error from last update
	integral     float64          // integral sum
	lastUpdate   time.Time        // time of last update
	deriveOn     DeriveMethodType // What do we derive on?
}

// Set changes the setpoint of the controller.
func (c *PIDController) Set(setpoint float64) {
	c.setpoint = setpoint
}

// Get returns the setpoint of the controller.
func (c *PIDController) Get() float64 {
	return c.setpoint
}

// SetDeriveMethod changes the derivation method.
func (c *PIDController) SetDeriveMethod(dm DeriveMethodType) {
	c.deriveOn = dm
}

// GetDeriveMethod returns the derivation method.
func (c *PIDController) GetDeriveMethod() DeriveMethodType {
	return c.deriveOn
}

// Update is identical to UpdateDuration, but automatically keeps track of the
// durations between updates.
func (c *PIDController) Update(value float64) float64 {
	var duration time.Duration
	if !c.lastUpdate.IsZero() {
		duration = time.Since(c.lastUpdate)
	}
	c.lastUpdate = time.Now()
	return c.UpdateDuration(value, duration)
}

// UpdateDuration updates the controller with the given value and duration since
// the last update. It returns the new output.
//
// see http://en.wikipedia.org/wiki/PID_controller#Pseudocode
func (c *PIDController) UpdateDuration(value float64, duration time.Duration) float64 {
	var (
		dt  = duration.Seconds()
		err = c.setpoint - value
		d   float64
	)
	c.integral += err * dt
	if dt > 0 {
		if c.deriveOn == DeriveOnErr {
			d = (err - c.prevErr) / dt
		} else {
			d = ((c.setpoint - c.prevSetpoint) / dt) - ((value - c.prevValue) / dt)
		}
	}
	c.prevValue = value
	c.prevSetpoint = c.setpoint
	c.prevErr = err
	return (c.p * err) + (c.i * c.integral) + (c.d * d)
}
<commit_msg>Make constant tuning more stable by putting I into the intergral sum<commit_after>// Packege pidctrl implements a PID controller.
//
// see http://en.wikipedia.org/wiki/PID_controller
package pidctrl

import (
	"time"
)

type DeriveMethodType int

const (
	DeriveOnErr DeriveMethodType = iota
	DeriveOnValue
)

// NewPIDController returns a new PIDController using the given gain values.
func NewPIDController(p, i, d float64) *PIDController {
	return &PIDController{p: p, i: i, d: d}
}

// PIDController implements a PID controller.
type PIDController struct {
	p            float64          // proportional gain
	i            float64          // integral gain
	d            float64          // derrivate gain
	setpoint     float64          // current setpoint
	prevSetpoint float64          // last setpoint
	prevValue    float64          // last process value
	prevErr      float64          // error from last update
	integral     float64          // integral sum
	lastUpdate   time.Time        // time of last update
	deriveOn     DeriveMethodType // What do we derive on?
}

// Set changes the setpoint of the controller.
func (c *PIDController) Set(setpoint float64) {
	c.setpoint = setpoint
}

// Get returns the setpoint of the controller.
func (c *PIDController) Get() float64 {
	return c.setpoint
}

// SetDeriveMethod changes the derivation method.
func (c *PIDController) SetDeriveMethod(dm DeriveMethodType) {
	c.deriveOn = dm
}

// GetDeriveMethod returns the derivation method.
func (c *PIDController) GetDeriveMethod() DeriveMethodType {
	return c.deriveOn
}

// SetPID changes the P, I, and D constants
func (c *PIDController) SetPID(p, i, d float64) {
	c.p = p
	c.i = i
	c.d = d
}

// SetPID returns the P, I, and D constants
func (c *PIDController) GetPID() (p, i, d float64) {
	return c.p, c.i, c.d
}

// Update is identical to UpdateDuration, but automatically keeps track of the
// durations between updates.
func (c *PIDController) Update(value float64) float64 {
	var duration time.Duration
	if !c.lastUpdate.IsZero() {
		duration = time.Since(c.lastUpdate)
	}
	c.lastUpdate = time.Now()
	return c.UpdateDuration(value, duration)
}

// UpdateDuration updates the controller with the given value and duration since
// the last update. It returns the new output.
//
// see http://en.wikipedia.org/wiki/PID_controller#Pseudocode
func (c *PIDController) UpdateDuration(value float64, duration time.Duration) float64 {
	var (
		dt  = duration.Seconds()
		err = c.setpoint - value
		d   float64
	)
	c.integral += err * dt * c.i
	if dt > 0 {
		if c.deriveOn == DeriveOnErr {
			d = (err - c.prevErr) / dt
		} else {
			d = ((c.setpoint - c.prevSetpoint) / dt) - ((value - c.prevValue) / dt)
		}
	}
	c.prevValue = value
	c.prevSetpoint = c.setpoint
	c.prevErr = err
	return (c.p * err) + c.integral + (c.d * d)
}
<|endoftext|>


<commit_before>//+build integration

package integration

import (
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"
	"time"

	ct "github.com/google/certificate-transparency/go"
	"github.com/google/certificate-transparency/go/client"
	"github.com/google/certificate-transparency/go/jsonclient"
	"golang.org/x/net/context"
)

var httpServerFlag = flag.String("ct_http_server", "localhost:8092", "Server address:port")
var pubKey = flag.String("public_key_file", "", "Name of file containing log's public key")
var testdata = flag.String("testdata", "testdata", "Name of directory with test data")

func TestCTIntegration(t *testing.T) {
	flag.Parse()

	logURI := "http://" + (*httpServerFlag)

	opts := jsonclient.Options{}
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			t.Fatalf("Failed to get public key contents: %v", err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(logURI, nil, opts)
	if err != nil {
		t.Fatalf("Failed to create LogClient instance: %v", err)
	}
	ctx := context.Background()

	// Stage 0: get accepted roots, which should just be the fake CA.
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		t.Fatalf("Failed to get roots: %v", err)
	}
	if len(roots) != 1 {
		t.Errorf("len(GetAcceptableRoots())=%d; want 1", len(roots))
	}

	// Stage 1: get the STH, which should be empty.
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		t.Fatalf("Failed to get STH: %v", err)
	}
	if sth.Version != 0 {
		t.Errorf("sth.Version=%v; want V1(0)", sth.Version)
	}
	if sth.TreeSize != 0 {
		t.Errorf("sth.TreeSize=%d; want 0", sth.TreeSize)
	}
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH: %x %v %v\n", when, sth.Version, sth.TreeSize, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))

	// Stage 2: add a single cert (the intermediate CA), get an SCT.
	certdata, err := ioutil.ReadFile(filepath.Join(*testdata, "int-ca.cert"))
	if err != nil {
		t.Fatalf("Failed to load certificate: %v", err.Error())
	}
	sct, err := logClient.AddChain(ctx, certsFromPEM(certdata))
	if err != nil {
		t.Fatalf("Failed to AddChain(0): %v", err)
	}
	// Display the SCT
	when = ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded certs to %v log, got SCT:\n", when, sct.SCTVersion)
	fmt.Printf("%v\n", signatureToString(&sct.Signature))

	// Stage 3: keep getting the STH until tree size becomes 1.
	// TODO(drysdale)

	// Stage 4: get a consistency proof from 0->1.
	// TODO(drysdale)

	// Stage 5: add certificates 2, 3, 4, 5,...N, for some random N in [4,25]
	// TODO(drysdale)

	// Stage 6: keep getting the STH until tree size becomes N.
	// TODO(drysdale)

	// Stage 7: get a consistency proof from 1->N.
	// TODO(drysdale)

	// Stage 8: get entries [1, N]
	// TODO(drysdale)

	// Stage 9: get an audit proof for cert M, randomly chosen in [1,N]
	// TODO(drysdale)
}

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func certsFromPEM(data []byte) []ct.ASN1Cert {
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	return chain
}
<commit_msg>integration/ct: fix output format<commit_after>//+build integration

package integration

import (
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"
	"time"

	ct "github.com/google/certificate-transparency/go"
	"github.com/google/certificate-transparency/go/client"
	"github.com/google/certificate-transparency/go/jsonclient"
	"golang.org/x/net/context"
)

var httpServerFlag = flag.String("ct_http_server", "localhost:8092", "Server address:port")
var pubKey = flag.String("public_key_file", "", "Name of file containing log's public key")
var testdata = flag.String("testdata", "testdata", "Name of directory with test data")

func TestCTIntegration(t *testing.T) {
	flag.Parse()

	logURI := "http://" + (*httpServerFlag)

	opts := jsonclient.Options{}
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			t.Fatalf("Failed to get public key contents: %v", err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(logURI, nil, opts)
	if err != nil {
		t.Fatalf("Failed to create LogClient instance: %v", err)
	}
	ctx := context.Background()

	// Stage 0: get accepted roots, which should just be the fake CA.
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		t.Fatalf("Failed to get roots: %v", err)
	}
	if len(roots) != 1 {
		t.Errorf("len(GetAcceptableRoots())=%d; want 1", len(roots))
	}

	// Stage 1: get the STH, which should be empty.
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		t.Fatalf("Failed to get STH: %v", err)
	}
	if sth.Version != 0 {
		t.Errorf("sth.Version=%v; want V1(0)", sth.Version)
	}
	if sth.TreeSize != 0 {
		t.Errorf("sth.TreeSize=%d; want 0", sth.TreeSize)
	}
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH(size=%d): roothash=%x\n", when, sth.TreeSize, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))

	// Stage 2: add a single cert (the intermediate CA), get an SCT.
	certdata, err := ioutil.ReadFile(filepath.Join(*testdata, "int-ca.cert"))
	if err != nil {
		t.Fatalf("Failed to load certificate: %v", err.Error())
	}
	sct, err := logClient.AddChain(ctx, certsFromPEM(certdata))
	if err != nil {
		t.Fatalf("Failed to AddChain(0): %v", err)
	}
	// Display the SCT
	when = ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded certs to %v log, got SCT:\n", when, sct.SCTVersion)
	fmt.Printf("%v\n", signatureToString(&sct.Signature))

	// Stage 3: keep getting the STH until tree size becomes 1.
	// TODO(drysdale)

	// Stage 4: get a consistency proof from 0->1.
	// TODO(drysdale)

	// Stage 5: add certificates 2, 3, 4, 5,...N, for some random N in [4,25]
	// TODO(drysdale)

	// Stage 6: keep getting the STH until tree size becomes N.
	// TODO(drysdale)

	// Stage 7: get a consistency proof from 1->N.
	// TODO(drysdale)

	// Stage 8: get entries [1, N]
	// TODO(drysdale)

	// Stage 9: get an audit proof for cert M, randomly chosen in [1,N]
	// TODO(drysdale)
}

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func certsFromPEM(data []byte) []ct.ASN1Cert {
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	return chain
}
<|endoftext|>


<commit_before>package approvals

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strings"

	"github.com/approvals/go-approval-tests/reporters"
	"github.com/approvals/go-approval-tests/utils"
)

var (
	defaultReporter            = reporters.NewDiffReporter()
	defaultFrontLoadedReporter = reporters.NewFrontLoadedReporter()
	defaultFolder              = ""
)

// Failable is an interface wrapper around testing.T
type Failable interface {
	Fail()
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Name() string
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Helper()
}

// VerifyWithExtension Example:
//   VerifyWithExtension(t, strings.NewReader("Hello"), ".json")
// Deprecated: Please use Verify with the Options() fluent syntax.
func VerifyWithExtension(t Failable, reader io.Reader, extWithDot string, opts ...verifyOptions) {
	t.Helper()

	var v verifyOptions
	if len(opts) == 0 {
		v = Options()
	} else {
		v = opts[0]
	}

	v = v.WithExtension(extWithDot)
	Verify(t, reader, v)
}

// Verify Example:
//   Verify(t, strings.NewReader("Hello"))
func Verify(t Failable, reader io.Reader, opts ...verifyOptions) {
	t.Helper()

	if len(opts) > 1 {
		panic("Please use fluent syntax for options, see documentation for more information")
	}

	var extWithDot string
	if len(opts) == 0 || opts[0].extWithDot == "" {
		extWithDot = ".txt"
	} else {
		extWithDot = opts[0].extWithDot
	}

	namer := getApprovalName(t)

	if len(opts) > 0 {
		b, err := io.ReadAll(reader)
		if err != nil {
			// TODO: do something
		}

		result := string(b)
		for _, o := range opts {
			for _, sb := range o.scrubbers {
				result = sb(result)
			}
		}

		reader = strings.NewReader(result)
	}

	reporter := getReporter()
	err := namer.compare(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot), reader)
	if err != nil {
		reporter.Report(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot))
		t.Log("Failed Approval: received does not match approved.")
		t.Fail()
	} else {
		_ = os.Remove(namer.getReceivedFile(extWithDot))
	}
}

// VerifyString stores the passed string into the received file and confirms
// that it matches the approved local file. On failure, it will launch a reporter.
func VerifyString(t Failable, s string, opts ...verifyOptions) {
	t.Helper()
	reader := strings.NewReader(s)
	Verify(t, reader, opts...)
}

// VerifyXMLStruct Example:
//   VerifyXMLStruct(t, xml)
func VerifyXMLStruct(t Failable, obj interface{}, opts ...verifyOptions) {
	t.Helper()
	xmlContent, err := xml.MarshalIndent(obj, "", "  ")
	if err != nil {
		tip := ""
		if reflect.TypeOf(obj).Name() == "" {
			tip = "when using anonymous types be sure to include\n  XMLName xml.Name `xml:\"Your_Name_Here\"`\n"
		}
		message := fmt.Sprintf("error while pretty printing XML\n%verror:\n  %v\nXML:\n  %v\n", tip, err, obj)
		VerifyWithExtension(t, strings.NewReader(message), ".xml", opts...)
	} else {
		VerifyWithExtension(t, bytes.NewReader(xmlContent), ".xml", opts...)
	}
}

// VerifyXMLBytes Example:
//   VerifyXMLBytes(t, []byte("<Test/>"))
func VerifyXMLBytes(t Failable, bs []byte, opts ...verifyOptions) {
	t.Helper()
	type node struct {
		Attr     []xml.Attr
		XMLName  xml.Name
		Children []node `xml:",any"`
		Text     string `xml:",chardata"`
	}
	x := node{}

	err := xml.Unmarshal(bs, &x)
	if err != nil {
		message := fmt.Sprintf("error while parsing XML\nerror:\n  %s\nXML:\n  %s\n", err, string(bs))
		VerifyWithExtension(t, strings.NewReader(message), ".xml", opts...)
	} else {
		VerifyXMLStruct(t, x, opts...)
	}
}

// VerifyJSONStruct Example:
//   VerifyJSONStruct(t, json)
func VerifyJSONStruct(t Failable, obj interface{}, opts ...verifyOptions) {
	t.Helper()

	jsonb, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		message := fmt.Sprintf("error while pretty printing JSON\nerror:\n  %s\nJSON:\n  %s\n", err, obj)
		VerifyWithExtension(t, strings.NewReader(message), ".json", opts...)
	} else {
		VerifyWithExtension(t, bytes.NewReader(jsonb), ".json", opts...)
	}
}

// VerifyJSONBytes Example:
//   VerifyJSONBytes(t, []byte("{ \"Greeting\": \"Hello\" }"))
func VerifyJSONBytes(t Failable, bs []byte, opts ...verifyOptions) {
	t.Helper()
	var obj map[string]interface{}
	err := json.Unmarshal(bs, &obj)
	if err != nil {
		message := fmt.Sprintf("error while parsing JSON\nerror:\n  %s\nJSON:\n  %s\n", err, string(bs))
		VerifyWithExtension(t, strings.NewReader(message), ".json", opts...)
	} else {
		VerifyJSONStruct(t, obj, opts...)
	}
}

// VerifyMap Example:
//   VerifyMap(t, map[string][string] { "dog": "bark" })
func VerifyMap(t Failable, m interface{}, opts ...verifyOptions) {
	t.Helper()
	outputText := utils.PrintMap(m)
	VerifyString(t, outputText, opts...)
}

// VerifyArray Example:
//   VerifyArray(t, []string{"dog", "cat"})
func VerifyArray(t Failable, array interface{}, opts ...verifyOptions) {
	t.Helper()
	outputText := utils.PrintArray(array)
	VerifyString(t, outputText, opts...)
}

// VerifyAll Example:
//   VerifyAll(t, "uppercase", []string("dog", "cat"}, func(x interface{}) string { return strings.ToUpper(x.(string)) })
func VerifyAll(t Failable, header string, collection interface{}, transform func(interface{}) string, opts ...verifyOptions) {
	t.Helper()
	if len(header) != 0 {
		header = fmt.Sprintf("%s\n\n\n", header)
	}

	outputText := header + strings.Join(utils.MapToString(collection, transform), "\n")
	VerifyString(t, outputText, opts...)
}

type reporterCloser struct {
	reporter reporters.Reporter
}

func (s *reporterCloser) Close() error {
	defaultReporter = s.reporter
	return nil
}

type frontLoadedReporterCloser struct {
	reporter reporters.Reporter
}

func (s *frontLoadedReporterCloser) Close() error {
	defaultFrontLoadedReporter = s.reporter
	return nil
}

// UseReporter configures which reporter to use on failure.
// Add at the test or method level to configure your reporter.
//
// The following examples shows how to use a reporter for all of your test cases
// in a package directory through go's setup feature.
//
//
//	func TestMain(m *testing.M) {
//		r := approvals.UseReporter(reporters.NewBeyondCompareReporter())
//		defer r.Close()
//
//		os.Exit(m.Run())
//	}
//
func UseReporter(reporter reporters.Reporter) io.Closer {
	closer := &reporterCloser{
		reporter: defaultReporter,
	}

	defaultReporter = reporter
	return closer
}

// UseFrontLoadedReporter configures reporters ahead of all other reporters to
// handle situations like CI. These reporters usually prevent reporting in
// scenarios that are headless.
func UseFrontLoadedReporter(reporter reporters.Reporter) io.Closer {
	closer := &frontLoadedReporterCloser{
		reporter: defaultFrontLoadedReporter,
	}

	defaultFrontLoadedReporter = reporter
	return closer
}

func getReporter() reporters.Reporter {
	return reporters.NewFirstWorkingReporter(
		defaultFrontLoadedReporter,
		defaultReporter,
	)
}

// UseFolder configures which folder to use to store approval files.
// By default, the approval files will be stored at the same level as the code.
//
// The following examples shows how to use the idiomatic 'testdata' folder
// for all of your test cases in a package directory.
//
//
//	func TestMain(m *testing.M) {
//		approvals.UseFolder("testdata")
//
//		os.Exit(m.Run())
//	}
//
func UseFolder(f string) {
	defaultFolder = f
}

type scrubber func(s string) string

// verifyOptions can be accessed via the approvals.Options() API enabling configuration of scrubbers
type verifyOptions struct {
	scrubbers  []scrubber
	extWithDot string
}

// Options enables providing individual Verify functions with customisations such as scrubbers
func Options() verifyOptions {
	return verifyOptions{}
}

// WithRegexScrubber allows you to 'scrub' dynamic data such as timestamps within your test input
// and replace it with a static placeholder
func (v verifyOptions) WithRegexScrubber(scrubber *regexp.Regexp, replacer string) verifyOptions {
	v.scrubbers = append(v.scrubbers, func(s string) string {
		return scrubber.ReplaceAllString(s, replacer)
	})
	return v
}

// WithExtension overrides the default file extension (.txt) for approval files.
func (v verifyOptions) WithExtension(extension string) verifyOptions {
	v.extWithDot = extension
	return v
}
<commit_msg>Stop using VerifyWithExtension internally<commit_after>package approvals

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strings"

	"github.com/approvals/go-approval-tests/reporters"
	"github.com/approvals/go-approval-tests/utils"
)

var (
	defaultReporter            = reporters.NewDiffReporter()
	defaultFrontLoadedReporter = reporters.NewFrontLoadedReporter()
	defaultFolder              = ""
)

// Failable is an interface wrapper around testing.T
type Failable interface {
	Fail()
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Name() string
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Helper()
}

// VerifyWithExtension Example:
//   VerifyWithExtension(t, strings.NewReader("Hello"), ".json")
// Deprecated: Please use Verify with the Options() fluent syntax.
func VerifyWithExtension(t Failable, reader io.Reader, extWithDot string, opts ...verifyOptions) {
	t.Helper()
	Verify(t, reader, alwaysOption(opts).WithExtension(extWithDot))
}

// Verify Example:
//   Verify(t, strings.NewReader("Hello"))
func Verify(t Failable, reader io.Reader, opts ...verifyOptions) {
	t.Helper()

	if len(opts) > 1 {
		panic("Please use fluent syntax for options, see documentation for more information")
	}

	var extWithDot string
	if len(opts) == 0 || opts[0].extWithDot == "" {
		extWithDot = ".txt"
	} else {
		extWithDot = opts[0].extWithDot
	}

	namer := getApprovalName(t)

	if len(opts) > 0 {
		b, err := io.ReadAll(reader)
		if err != nil {
			// TODO: do something
		}

		result := string(b)
		for _, o := range opts {
			for _, sb := range o.scrubbers {
				result = sb(result)
			}
		}

		reader = strings.NewReader(result)
	}

	reporter := getReporter()
	err := namer.compare(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot), reader)
	if err != nil {
		reporter.Report(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot))
		t.Log("Failed Approval: received does not match approved.")
		t.Fail()
	} else {
		_ = os.Remove(namer.getReceivedFile(extWithDot))
	}
}

// VerifyString stores the passed string into the received file and confirms
// that it matches the approved local file. On failure, it will launch a reporter.
func VerifyString(t Failable, s string, opts ...verifyOptions) {
	t.Helper()
	reader := strings.NewReader(s)
	Verify(t, reader, opts...)
}

// VerifyXMLStruct Example:
//   VerifyXMLStruct(t, xml)
func VerifyXMLStruct(t Failable, obj interface{}, opts ...verifyOptions) {
	t.Helper()
	xmlContent, err := xml.MarshalIndent(obj, "", "  ")
	if err != nil {
		tip := ""
		if reflect.TypeOf(obj).Name() == "" {
			tip = "when using anonymous types be sure to include\n  XMLName xml.Name `xml:\"Your_Name_Here\"`\n"
		}
		message := fmt.Sprintf("error while pretty printing XML\n%verror:\n  %v\nXML:\n  %v\n", tip, err, obj)
		Verify(t, strings.NewReader(message), alwaysOption(opts).WithExtension(".xml"))
	} else {
		Verify(t, bytes.NewReader(xmlContent), alwaysOption(opts).WithExtension(".xml"))
	}
}

// VerifyXMLBytes Example:
//   VerifyXMLBytes(t, []byte("<Test/>"))
func VerifyXMLBytes(t Failable, bs []byte, opts ...verifyOptions) {
	t.Helper()
	type node struct {
		Attr     []xml.Attr
		XMLName  xml.Name
		Children []node `xml:",any"`
		Text     string `xml:",chardata"`
	}
	x := node{}

	err := xml.Unmarshal(bs, &x)
	if err != nil {
		message := fmt.Sprintf("error while parsing XML\nerror:\n  %s\nXML:\n  %s\n", err, string(bs))
		Verify(t, strings.NewReader(message), alwaysOption(opts).WithExtension(".xml"))
	} else {
		VerifyXMLStruct(t, x, opts...)
	}
}

// VerifyJSONStruct Example:
//   VerifyJSONStruct(t, json)
func VerifyJSONStruct(t Failable, obj interface{}, opts ...verifyOptions) {
	t.Helper()

	jsonb, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		message := fmt.Sprintf("error while pretty printing JSON\nerror:\n  %s\nJSON:\n  %s\n", err, obj)
		Verify(t, strings.NewReader(message), alwaysOption(opts).WithExtension(".json"))
	} else {
		Verify(t, bytes.NewReader(jsonb), alwaysOption(opts).WithExtension(".json"))
	}
}

// VerifyJSONBytes Example:
//   VerifyJSONBytes(t, []byte("{ \"Greeting\": \"Hello\" }"))
func VerifyJSONBytes(t Failable, bs []byte, opts ...verifyOptions) {
	t.Helper()
	var obj map[string]interface{}
	err := json.Unmarshal(bs, &obj)
	if err != nil {
		message := fmt.Sprintf("error while parsing JSON\nerror:\n  %s\nJSON:\n  %s\n", err, string(bs))
		Verify(t, strings.NewReader(message), alwaysOption(opts).WithExtension(".json"))
	} else {
		VerifyJSONStruct(t, obj, opts...)
	}
}

// VerifyMap Example:
//   VerifyMap(t, map[string][string] { "dog": "bark" })
func VerifyMap(t Failable, m interface{}, opts ...verifyOptions) {
	t.Helper()
	outputText := utils.PrintMap(m)
	VerifyString(t, outputText, opts...)
}

// VerifyArray Example:
//   VerifyArray(t, []string{"dog", "cat"})
func VerifyArray(t Failable, array interface{}, opts ...verifyOptions) {
	t.Helper()
	outputText := utils.PrintArray(array)
	VerifyString(t, outputText, opts...)
}

// VerifyAll Example:
//   VerifyAll(t, "uppercase", []string("dog", "cat"}, func(x interface{}) string { return strings.ToUpper(x.(string)) })
func VerifyAll(t Failable, header string, collection interface{}, transform func(interface{}) string, opts ...verifyOptions) {
	t.Helper()
	if len(header) != 0 {
		header = fmt.Sprintf("%s\n\n\n", header)
	}

	outputText := header + strings.Join(utils.MapToString(collection, transform), "\n")
	VerifyString(t, outputText, opts...)
}

type reporterCloser struct {
	reporter reporters.Reporter
}

func (s *reporterCloser) Close() error {
	defaultReporter = s.reporter
	return nil
}

type frontLoadedReporterCloser struct {
	reporter reporters.Reporter
}

func (s *frontLoadedReporterCloser) Close() error {
	defaultFrontLoadedReporter = s.reporter
	return nil
}

// UseReporter configures which reporter to use on failure.
// Add at the test or method level to configure your reporter.
//
// The following examples shows how to use a reporter for all of your test cases
// in a package directory through go's setup feature.
//
//
//	func TestMain(m *testing.M) {
//		r := approvals.UseReporter(reporters.NewBeyondCompareReporter())
//		defer r.Close()
//
//		os.Exit(m.Run())
//	}
//
func UseReporter(reporter reporters.Reporter) io.Closer {
	closer := &reporterCloser{
		reporter: defaultReporter,
	}

	defaultReporter = reporter
	return closer
}

// UseFrontLoadedReporter configures reporters ahead of all other reporters to
// handle situations like CI. These reporters usually prevent reporting in
// scenarios that are headless.
func UseFrontLoadedReporter(reporter reporters.Reporter) io.Closer {
	closer := &frontLoadedReporterCloser{
		reporter: defaultFrontLoadedReporter,
	}

	defaultFrontLoadedReporter = reporter
	return closer
}

func getReporter() reporters.Reporter {
	return reporters.NewFirstWorkingReporter(
		defaultFrontLoadedReporter,
		defaultReporter,
	)
}

// UseFolder configures which folder to use to store approval files.
// By default, the approval files will be stored at the same level as the code.
//
// The following examples shows how to use the idiomatic 'testdata' folder
// for all of your test cases in a package directory.
//
//
//	func TestMain(m *testing.M) {
//		approvals.UseFolder("testdata")
//
//		os.Exit(m.Run())
//	}
//
func UseFolder(f string) {
	defaultFolder = f
}

type scrubber func(s string) string

// verifyOptions can be accessed via the approvals.Options() API enabling configuration of scrubbers
type verifyOptions struct {
	scrubbers  []scrubber
	extWithDot string
}

// Options enables providing individual Verify functions with customisations such as scrubbers
func Options() verifyOptions {
	return verifyOptions{}
}

// WithRegexScrubber allows you to 'scrub' dynamic data such as timestamps within your test input
// and replace it with a static placeholder
func (v verifyOptions) WithRegexScrubber(scrubber *regexp.Regexp, replacer string) verifyOptions {
	v.scrubbers = append(v.scrubbers, func(s string) string {
		return scrubber.ReplaceAllString(s, replacer)
	})
	return v
}

// WithExtension overrides the default file extension (.txt) for approval files.
func (v verifyOptions) WithExtension(extension string) verifyOptions {
	v.extWithDot = extension
	return v
}

func alwaysOption(opts []verifyOptions) verifyOptions {
	var v verifyOptions
	if len(opts) == 0 {
		v = Options()
	} else {
		v = opts[0]
	}

	return v
}
<|endoftext|>


<commit_before>package utils

import (
	"os/exec"
	"time"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const (
	agentID = "123-456-789"
)

type Agent struct {
	ID   string
	tail *gexec.Session
}

func BuildAgent() error {
	command := exec.Command("./build_agent.bash")
	session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
	if err != nil {
		return err
	}
	gomega.Eventually(session, 20*time.Minute).Should(gexec.Exit(0))
	return nil
}

func StartVagrant(provider string) (Agent, error) {
	if len(provider) == 0 {
		provider = "virtualbox"
	}
	command := exec.Command("./setup_vagrant.bash", provider)
	session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
	if err != nil {
		return Agent{}, err
	}
	gomega.Eventually(session, 20*time.Minute).Should(gexec.Exit(0))

	return Agent{
		ID: agentID,
	}, nil
}

func (a Agent) Stop() {
}
<commit_msg>Windows Integration: bump Vagrant timeout to 40 minutes<commit_after>package utils

import (
	"os/exec"
	"time"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const (
	agentID = "123-456-789"
)

type Agent struct {
	ID   string
	tail *gexec.Session
}

func BuildAgent() error {
	command := exec.Command("./build_agent.bash")
	session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
	if err != nil {
		return err
	}
	gomega.Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
	return nil
}

func StartVagrant(provider string) (Agent, error) {
	if len(provider) == 0 {
		provider = "virtualbox"
	}
	command := exec.Command("./setup_vagrant.bash", provider)
	session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
	if err != nil {
		return Agent{}, err
	}
	gomega.Eventually(session, 40*time.Minute).Should(gexec.Exit(0))

	return Agent{
		ID: agentID,
	}, nil
}

func (a Agent) Stop() {
}
<|endoftext|>


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

// Package fieldtags defines an analyzer that identifies struct fields identified
// as sources via a field tag.
package fieldtags

import (
	"go/ast"
	"go/types"
	"reflect"
	"strings"

	"github.com/google/go-flow-levee/internal/pkg/config"
	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/ast/inspector"
	"golang.org/x/tools/go/ssa"
)

// ResultType is a map from types.Object to bool.
// It can be used to determine whether a field is a tagged Source field.
type ResultType map[types.Object]bool

var Analyzer = &analysis.Analyzer{
	Name: "fieldtags",
	Doc:  "This analyzer identifies Source fields based on their tags. Tags are expected to satisfy the `go vet -structtag` format.",
	Run:  run,
	Requires: []*analysis.Analyzer{
		inspect.Analyzer,
	},
	ResultType: reflect.TypeOf(new(ResultType)).Elem(),
}

type fieldTag struct {
	key string
	val string
}

func run(pass *analysis.Pass) (interface{}, error) {
	conf, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}

	inspectResult := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
	taggedFields := map[types.Object]bool{}

	nodeFilter := []ast.Node{
		(*ast.TypeSpec)(nil),
	}

	inspectResult.Preorder(nodeFilter, func(n ast.Node) {
		t, ok := n.(*ast.TypeSpec)
		if !ok {
			return
		}
		s, ok := t.Type.(*ast.StructType)
		if !ok || s.Fields == nil {
			return
		}
		for _, f := range (*s).Fields.List {
			if f.Tag == nil || len(f.Names) == 0 || !conf.IsSourceFieldTag(f.Tag.Value) {
				continue
			}
			fNames := make([]string, len(f.Names))
			for i, ident := range f.Names {
				fNames[i] = ident.Name
				taggedFields[pass.TypesInfo.ObjectOf(ident)] = true
			}
			pass.Reportf(f.Pos(), "tagged field: %s", strings.Join(fNames, ", "))
		}
	})

	return ResultType(taggedFields), nil
}

// IsSourceFieldAddr determines whether a ssa.FieldAddr is a source, that is whether it refers to a field previously identified as a source.
func (r ResultType) IsSourceFieldAddr(fa *ssa.FieldAddr) bool {
	// incantation plundered from the docstring for ssa.FieldAddr.Field
	field := fa.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(fa.Field)
	return r.IsSource(field)
}

// IsSource determines whether a types.Var is a source, that is whether it refers to a field previously identified as a source.
func (r ResultType) IsSource(field *types.Var) bool {
	return r[(types.Object)(field)]
}
<commit_msg>remove unused type (#167)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package fieldtags defines an analyzer that identifies struct fields identified
// as sources via a field tag.
package fieldtags

import (
	"go/ast"
	"go/types"
	"reflect"
	"strings"

	"github.com/google/go-flow-levee/internal/pkg/config"
	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/ast/inspector"
	"golang.org/x/tools/go/ssa"
)

// ResultType is a map from types.Object to bool.
// It can be used to determine whether a field is a tagged Source field.
type ResultType map[types.Object]bool

var Analyzer = &analysis.Analyzer{
	Name: "fieldtags",
	Doc:  "This analyzer identifies Source fields based on their tags. Tags are expected to satisfy the `go vet -structtag` format.",
	Run:  run,
	Requires: []*analysis.Analyzer{
		inspect.Analyzer,
	},
	ResultType: reflect.TypeOf(new(ResultType)).Elem(),
}

func run(pass *analysis.Pass) (interface{}, error) {
	conf, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}

	inspectResult := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
	taggedFields := map[types.Object]bool{}

	nodeFilter := []ast.Node{
		(*ast.TypeSpec)(nil),
	}

	inspectResult.Preorder(nodeFilter, func(n ast.Node) {
		t, ok := n.(*ast.TypeSpec)
		if !ok {
			return
		}
		s, ok := t.Type.(*ast.StructType)
		if !ok || s.Fields == nil {
			return
		}
		for _, f := range (*s).Fields.List {
			if f.Tag == nil || len(f.Names) == 0 || !conf.IsSourceFieldTag(f.Tag.Value) {
				continue
			}
			fNames := make([]string, len(f.Names))
			for i, ident := range f.Names {
				fNames[i] = ident.Name
				taggedFields[pass.TypesInfo.ObjectOf(ident)] = true
			}
			pass.Reportf(f.Pos(), "tagged field: %s", strings.Join(fNames, ", "))
		}
	})

	return ResultType(taggedFields), nil
}

// IsSourceFieldAddr determines whether a ssa.FieldAddr is a source, that is whether it refers to a field previously identified as a source.
func (r ResultType) IsSourceFieldAddr(fa *ssa.FieldAddr) bool {
	// incantation plundered from the docstring for ssa.FieldAddr.Field
	field := fa.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(fa.Field)
	return r.IsSource(field)
}

// IsSource determines whether a types.Var is a source, that is whether it refers to a field previously identified as a source.
func (r ResultType) IsSource(field *types.Var) bool {
	return r[(types.Object)(field)]
}
<|endoftext|>


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

package rocserv

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
	// now use 73a8ef737e8ea002281a28b4cb92a1de121ad4c6
	//"github.com/coreos/go-etcd/etcd"
	etcd "github.com/coreos/etcd/client"

	"github.com/shawnfeng/sutil/dbrouter"
	"github.com/shawnfeng/sutil/sconf"
	"github.com/shawnfeng/sutil/slog"
	"github.com/shawnfeng/sutil/slowid"
	"github.com/shawnfeng/sutil/ssync"

	"golang.org/x/net/context"
)

const (
	BASE_LOC_DIST = "dist"
	// 调整了服务注册结构，为兼容老版本，BASE_LOC_DIST下也要完成之前方式的注册
	// dist2 2为版本2
	BASE_LOC_DIST_V2        = "dist2"
	BASE_LOC_BREAKER        = "breaker"
	BASE_LOC_BREAKER_GLOBAL = "breaker/global"
	BASE_LOC_ETC            = "etc"
	BASE_LOC_ETC_GLOBAL     = "etc/global"
	BASE_LOC_SKEY           = "skey"
	BASE_LOC_OP             = "op"
	BASE_LOC_DB             = "db/route"
	// 服务内分布式锁，只在单个服务副本之间使用
	BASE_LOC_LOCAL_DIST_LOCK = "lock/local"
	// 全局分布式锁，跨服务使用
	BASE_LOC_GLOBAL_DIST_LOCK = "lock/global"

	// thrift 服务注册的位置
	BASE_LOC_REG_SERV = "serve"

	// 后门注册的位置
	BASE_LOC_REG_BACKDOOR = "backdoor"

	// 服务手动配置位置
	BASE_LOC_REG_MANUAL = "manual"
	// sla metrics注册的位置
	BASE_LOC_REG_METRICS = "metrics"

	PROCESSOR_GRPC_PROPERTY_NAME = "proc_grpc"

	PROCESSOR_THRIFT_PROPERTY_NAME = "proc_thrift"
)

type configEtcd struct {
	etcdAddrs  []string
	useBaseloc string
}

type ServBaseV2 struct {
	IdGenerator

	confEtcd configEtcd

	dbLocation   string
	servLocation string
	servGroup    string
	servName     string
	copyName     string
	sessKey      string

	etcdClient etcd.KeysAPI
	servId     int

	dbRouter *dbrouter.Router

	muLocks ssync.Mutex
	locks   map[string]*ssync.Mutex

	muHearts ssync.Mutex
	hearts   map[string]*distLockHeart
}

func (m *ServBaseV2) RegisterBackDoor(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterBackDoor -->"
	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_BACKDOOR)

	return m.doRegister(path, string(js), true)

}

func (m *ServBaseV2) RegisterMetrics(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterMetrics -->"
	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_METRICS)

	return m.doRegister(path, string(js), true)

}

// {type:http/thrift, addr:10.3.3.3:23233, processor:fuck}
func (m *ServBaseV2) RegisterService(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterService -->"

	err := m.RegisterServiceV2(servs, BASE_LOC_REG_SERV)
	if err != nil {
		slog.Errorf("%s reg v2 err:%s", fun, err)
		return err
	}

	err = m.RegisterServiceV1(servs)
	if err != nil {
		slog.Errorf("%s reg v1 err:%s", fun, err)
		return err
	}

	slog.Infof("%s regist ok", fun)

	return nil
}

func (m *ServBaseV2) RegisterServiceV2(servs map[string]*ServInfo, dir string) error {
	fun := "ServBaseV2.RegisterServiceV2 -->"

	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, dir)

	return m.doRegister(path, string(js), true)
}

// 为兼容老的client发现服务，保留的
func (m *ServBaseV2) RegisterServiceV1(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterServiceV1 -->"

	js, err := json.Marshal(servs)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d", m.confEtcd.useBaseloc, BASE_LOC_DIST, m.servLocation, m.servId)

	return m.doRegister(path, string(js), true)
}

func (m *ServBaseV2) SetGroupAndDisable(group string, disable bool) error {
	fun := "ServBaseV2.SetGroupAndDisable -->"

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_MANUAL)
	value, err := m.getValueFromEtcd(path)
	if err != nil {
		slog.Warnf("%s getValueFromEtcd err, path:%s, err:%v", fun, path, err)
	}

	manual := &ManualData{}
	err = json.Unmarshal([]byte(value), manual)
	if len(value) > 0 && err != nil {
		slog.Errorf("%s unmarshal err, value:%s, err:%v", fun, value, err)
		return err
	}

	if manual.Ctrl == nil {
		manual.Ctrl = &ServCtrl{}
	}

	isFind := false
	for _, g := range manual.Ctrl.Groups {
		if g == group {
			isFind = true
			break
		}
	}

	if isFind == false {
		manual.Ctrl.Groups = append(manual.Ctrl.Groups, group)
	}
	if manual.Ctrl.Weight == 0 {
		manual.Ctrl.Weight = 100
	}
	manual.Ctrl.Disable = disable

	newValue, err := json.Marshal(manual)
	if err != nil {
		slog.Errorf("%s marshal err, manual:%v, err:%v", fun, manual, err)
		return err
	}

	slog.Infof("%s path:%s old value:%s new value:%s", fun, path, value, newValue)
	err = m.setValueToEtcd(path, string(newValue), nil)
	if err != nil {
		slog.Errorf("%s setValueToEtcd err, path:%s value:%s", fun, path, newValue)
	}

	return err
}

func (m *ServBaseV2) getValueFromEtcd(path string) (value string, err error) {
	fun := "ServBaseV2.getValueFromEtcd -->"

	r, err := m.etcdClient.Get(context.Background(), path, &etcd.GetOptions{Recursive: false, Sort: false})
	if err != nil {
		slog.Warnf("%s path:%s err:%v", fun, path, err)
		return "", err
	}

	for _, n := range r.Node.Nodes {
		for _, nc := range n.Nodes {
			return nc.Value, nil
		}
	}

	return "", nil
}

func (m *ServBaseV2) setValueToEtcd(path, value string, opts *etcd.SetOptions) error {
	fun := "ServBaseV2.setValueToEtcd -->"

	_, err := m.etcdClient.Set(context.Background(), path, value, opts)
	if err != nil {
		slog.Errorf("%s path:%s value:%s opts:%v", fun, path, value, opts)
	}

	return err
}

func (m *ServBaseV2) doRegister(path, js string, refresh bool) error {
	fun := "ServBaseV2.doRegister -->"
	// 创建完成标志
	var iscreate bool

	slog.Infof("%s path:%s data:%s refresh:%t", fun, path, js, refresh)

	go func() {

		for i := 0; ; i++ {
			var err error
			var r *etcd.Response
			if !iscreate {
				slog.Warnf("%s create idx:%d servs:%s", fun, i, js)
				r, err = m.etcdClient.Set(context.Background(), path, js, &etcd.SetOptions{
					TTL: time.Second * 60,
				})
			} else {
				if refresh {
					// 在刷新ttl时候，不允许变更value
					// 节点超时时间为120秒
					slog.Infof("%s refresh ttl idx:%d servs:%s", fun, i, js)
					r, err = m.etcdClient.Set(context.Background(), path, "", &etcd.SetOptions{
						PrevExist: etcd.PrevExist,
						TTL:       time.Second * 60,
						Refresh:   true,
					})
				} else {
					r, err = m.etcdClient.Set(context.Background(), path, js, &etcd.SetOptions{
						TTL: time.Second * 60,
					})
				}

			}

			if err != nil {
				iscreate = false
				slog.Errorf("%s reg idx:%d err:%s", fun, i, err)

			} else {
				iscreate = true
				jr, _ := json.Marshal(r)
				slog.Infof("%s reg idx:%d ok:%s", fun, i, jr)
			}

			// 每分发起一次注册
			time.Sleep(time.Second * 20)
		}

	}()

	return nil
}

func (m *ServBaseV2) Servid() int {
	return m.servId
}

func (m *ServBaseV2) Copyname() string {
	return fmt.Sprintf("%s%d", m.servLocation, m.servId)

}

func (m *ServBaseV2) Servname() string {
	return m.servLocation
}

func (m *ServBaseV2) Dbrouter() *dbrouter.Router {
	return m.dbRouter
}

func (m *ServBaseV2) ServConfig(cfg interface{}) error {
	fun := "ServBaseV2.ServConfig -->"
	// 获取全局配置
	path := fmt.Sprintf("%s/%s", m.confEtcd.useBaseloc, BASE_LOC_ETC_GLOBAL)
	scfg_global, err := getValue(m.etcdClient, path)
	if err != nil {
		slog.Warnf("%s serv config global value path:%s err:%s", fun, path, err)
	}
	slog.Infof("%s global cfg:%s path:%s", fun, scfg_global, path)

	path = fmt.Sprintf("%s/%s/%s", m.confEtcd.useBaseloc, BASE_LOC_ETC, m.servLocation)
	scfg, err := getValue(m.etcdClient, path)
	if err != nil {
		slog.Warnf("%s serv config value path:%s err:%s", fun, path, err)
	}

	slog.Infof("%s cfg:%s path:%s", fun, scfg, path)
	tf := sconf.NewTierConf()
	err = tf.Load(scfg_global)
	if err != nil {
		return err
	}

	err = tf.Load(scfg)
	if err != nil {
		return err
	}

	err = tf.Unmarshal(cfg)
	if err != nil {
		return err
	}

	return nil
}

// etcd v2 接口
func NewServBaseV2(confEtcd configEtcd, servLocation, skey string) (*ServBaseV2, error) {
	fun := "NewServBaseV2 -->"

	cfg := etcd.Config{
		Endpoints: confEtcd.etcdAddrs,
		Transport: etcd.DefaultTransport,
	}

	c, err := etcd.New(cfg)
	if err != nil {
		return nil, fmt.Errorf("create etchd client cfg error")
	}

	client := etcd.NewKeysAPI(c)
	if client == nil {
		return nil, fmt.Errorf("create etchd api error")
	}

	path := fmt.Sprintf("%s/%s/%s", confEtcd.useBaseloc, BASE_LOC_SKEY, servLocation)

	sid, err := retryGenSid(client, path, skey, 3)
	if err != nil {
		return nil, err
	}

	slog.Infof("%s path:%s sid:%d skey:%s", fun, path, sid, skey)

	dbloc := fmt.Sprintf("%s/%s", confEtcd.useBaseloc, BASE_LOC_DB)

	var dr *dbrouter.Router
	jscfg, err := getValue(client, dbloc)
	if err != nil {
		slog.Warnf("%s db:%s config notfound", fun, dbloc)
	} else {
		dr, err = dbrouter.NewRouter(jscfg)
		if err != nil {
			return nil, err
		}
	}

	svrInfo := strings.Split(servLocation, "/")

	reg := &ServBaseV2{
		confEtcd:     confEtcd,
		dbLocation:   dbloc,
		servLocation: servLocation,
		servGroup:    svrInfo[0],
		servName:     svrInfo[1],
		sessKey:      skey,
		etcdClient:   client,
		servId:       sid,
		locks:        make(map[string]*ssync.Mutex),
		hearts:       make(map[string]*distLockHeart),

		dbRouter: dr,
	}

	sf, err := initSnowflake(sid)
	if err != nil {
		return nil, err
	}

	reg.IdGenerator.snow = sf
	reg.IdGenerator.slow = make(map[string]*slowid.Slowid)
	reg.IdGenerator.servId = sid

	return reg, nil

}

// mutex
<commit_msg>modify<commit_after>// Copyright 2014 The roc Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rocserv

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
	// now use 73a8ef737e8ea002281a28b4cb92a1de121ad4c6
	//"github.com/coreos/go-etcd/etcd"
	etcd "github.com/coreos/etcd/client"

	"github.com/shawnfeng/sutil/dbrouter"
	"github.com/shawnfeng/sutil/sconf"
	"github.com/shawnfeng/sutil/slog"
	"github.com/shawnfeng/sutil/slowid"
	"github.com/shawnfeng/sutil/ssync"

	"golang.org/x/net/context"
)

const (
	BASE_LOC_DIST = "dist"
	// 调整了服务注册结构，为兼容老版本，BASE_LOC_DIST下也要完成之前方式的注册
	// dist2 2为版本2
	BASE_LOC_DIST_V2        = "dist2"
	BASE_LOC_BREAKER        = "breaker"
	BASE_LOC_BREAKER_GLOBAL = "breaker/global"
	BASE_LOC_ETC            = "etc"
	BASE_LOC_ETC_GLOBAL     = "etc/global"
	BASE_LOC_SKEY           = "skey"
	BASE_LOC_OP             = "op"
	BASE_LOC_DB             = "db/route"
	// 服务内分布式锁，只在单个服务副本之间使用
	BASE_LOC_LOCAL_DIST_LOCK = "lock/local"
	// 全局分布式锁，跨服务使用
	BASE_LOC_GLOBAL_DIST_LOCK = "lock/global"

	// thrift 服务注册的位置
	BASE_LOC_REG_SERV = "serve"

	// 后门注册的位置
	BASE_LOC_REG_BACKDOOR = "backdoor"

	// 服务手动配置位置
	BASE_LOC_REG_MANUAL = "manual"
	// sla metrics注册的位置
	BASE_LOC_REG_METRICS = "metrics"

	PROCESSOR_GRPC_PROPERTY_NAME = "proc_grpc"

	PROCESSOR_THRIFT_PROPERTY_NAME = "proc_thrift"
)

type configEtcd struct {
	etcdAddrs  []string
	useBaseloc string
}

type ServBaseV2 struct {
	IdGenerator

	confEtcd configEtcd

	dbLocation   string
	servLocation string
	servGroup    string
	servName     string
	copyName     string
	sessKey      string

	etcdClient etcd.KeysAPI
	servId     int

	dbRouter *dbrouter.Router

	muLocks ssync.Mutex
	locks   map[string]*ssync.Mutex

	muHearts ssync.Mutex
	hearts   map[string]*distLockHeart
}

func (m *ServBaseV2) RegisterBackDoor(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterBackDoor -->"
	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_BACKDOOR)

	return m.doRegister(path, string(js), true)

}

func (m *ServBaseV2) RegisterMetrics(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterMetrics -->"
	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_METRICS)

	return m.doRegister(path, string(js), true)

}

// {type:http/thrift, addr:10.3.3.3:23233, processor:fuck}
func (m *ServBaseV2) RegisterService(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterService -->"

	err := m.RegisterServiceV2(servs, BASE_LOC_REG_SERV)
	if err != nil {
		slog.Errorf("%s reg v2 err:%s", fun, err)
		return err
	}

	err = m.RegisterServiceV1(servs)
	if err != nil {
		slog.Errorf("%s reg v1 err:%s", fun, err)
		return err
	}

	slog.Infof("%s regist ok", fun)

	return nil
}

func (m *ServBaseV2) RegisterServiceV2(servs map[string]*ServInfo, dir string) error {
	fun := "ServBaseV2.RegisterServiceV2 -->"

	rd := &RegData{
		Servs: servs,
	}

	js, err := json.Marshal(rd)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, dir)

	return m.doRegister(path, string(js), true)
}

// 为兼容老的client发现服务，保留的
func (m *ServBaseV2) RegisterServiceV1(servs map[string]*ServInfo) error {
	fun := "ServBaseV2.RegisterServiceV1 -->"

	js, err := json.Marshal(servs)
	if err != nil {
		return err
	}

	slog.Infof("%s servs:%s", fun, js)

	path := fmt.Sprintf("%s/%s/%s/%d", m.confEtcd.useBaseloc, BASE_LOC_DIST, m.servLocation, m.servId)

	return m.doRegister(path, string(js), true)
}

func (m *ServBaseV2) SetGroupAndDisable(group string, disable bool) error {
	fun := "ServBaseV2.SetGroupAndDisable -->"

	path := fmt.Sprintf("%s/%s/%s/%d/%s", m.confEtcd.useBaseloc, BASE_LOC_DIST_V2, m.servLocation, m.servId, BASE_LOC_REG_MANUAL)
	value, err := m.getValueFromEtcd(path)
	if err != nil {
		slog.Warnf("%s getValueFromEtcd err, path:%s, err:%v", fun, path, err)
	}

	manual := &ManualData{}
	err = json.Unmarshal([]byte(value), manual)
	if len(value) > 0 && err != nil {
		slog.Errorf("%s unmarshal err, value:%s, err:%v", fun, value, err)
		return err
	}

	if manual.Ctrl == nil {
		manual.Ctrl = &ServCtrl{}
	}

	isFind := false
	for _, g := range manual.Ctrl.Groups {
		if g == group {
			isFind = true
			break
		}
	}

	if isFind == false {
		manual.Ctrl.Groups = append(manual.Ctrl.Groups, group)
	}
	if manual.Ctrl.Weight == 0 {
		manual.Ctrl.Weight = 100
	}
	manual.Ctrl.Disable = disable

	newValue, err := json.Marshal(manual)
	if err != nil {
		slog.Errorf("%s marshal err, manual:%v, err:%v", fun, manual, err)
		return err
	}

	slog.Infof("%s path:%s old value:%s new value:%s", fun, path, value, newValue)
	err = m.setValueToEtcd(path, string(newValue), nil)
	if err != nil {
		slog.Errorf("%s setValueToEtcd err, path:%s value:%s", fun, path, newValue)
	}

	return err
}

func (m *ServBaseV2) getValueFromEtcd(path string) (value string, err error) {
	fun := "ServBaseV2.getValueFromEtcd -->"

	r, err := m.etcdClient.Get(context.Background(), path, &etcd.GetOptions{Recursive: false, Sort: false})
	if err != nil {
		slog.Warnf("%s path:%s err:%v", fun, path, err)
		return "", err
	}

	for _, n := range r.Node.Nodes {
		for _, nc := range n.Nodes {
			return nc.Value, nil
		}
	}

	return "", nil
}

func (m *ServBaseV2) setValueToEtcd(path, value string, opts *etcd.SetOptions) error {
	fun := "ServBaseV2.setValueToEtcd -->"

	_, err := m.etcdClient.Set(context.Background(), path, value, opts)
	if err != nil {
		slog.Errorf("%s path:%s value:%s opts:%v", fun, path, value, opts)
	}

	return err
}

func (m *ServBaseV2) doRegister(path, js string, refresh bool) error {
	fun := "ServBaseV2.doRegister -->"
	// 创建完成标志
	var iscreate bool

	slog.Infof("%s path:%s data:%s refresh:%t", fun, path, js, refresh)

	go func() {

		for i := 0; ; i++ {
			var err error
			var r *etcd.Response
			if !iscreate {
				slog.Warnf("%s create idx:%d servs:%s", fun, i, js)
				r, err = m.etcdClient.Set(context.Background(), path, js, &etcd.SetOptions{
					TTL: time.Second * 60,
				})
			} else {
				if refresh {
					// 在刷新ttl时候，不允许变更value
					// 节点超时时间为120秒
					slog.Infof("%s refresh ttl idx:%d servs:%s", fun, i, js)
					r, err = m.etcdClient.Set(context.Background(), path, "", &etcd.SetOptions{
						PrevExist: etcd.PrevExist,
						TTL:       time.Second * 60,
						Refresh:   true,
					})
				} else {
					r, err = m.etcdClient.Set(context.Background(), path, js, &etcd.SetOptions{
						TTL: time.Second * 60,
					})
				}

			}

			if err != nil {
				iscreate = false
				slog.Errorf("%s reg idx:%d err:%s", fun, i, err)

			} else {
				iscreate = true
				jr, _ := json.Marshal(r)
				slog.Infof("%s reg idx:%d ok:%s", fun, i, jr)
			}

			// 每分发起一次注册
			time.Sleep(time.Second * 20)
		}

	}()

	return nil
}

func (m *ServBaseV2) Servid() int {
	return m.servId
}

func (m *ServBaseV2) Copyname() string {
	return fmt.Sprintf("%s%d", m.servLocation, m.servId)

}

func (m *ServBaseV2) Servname() string {
	return m.servLocation
}

func (m *ServBaseV2) Dbrouter() *dbrouter.Router {
	return m.dbRouter
}

func (m *ServBaseV2) ServConfig(cfg interface{}) error {
	fun := "ServBaseV2.ServConfig -->"
	// 获取全局配置
	path := fmt.Sprintf("%s/%s", m.confEtcd.useBaseloc, BASE_LOC_ETC_GLOBAL)
	scfg_global, err := getValue(m.etcdClient, path)
	if err != nil {
		slog.Warnf("%s serv config global value path:%s err:%s", fun, path, err)
	}
	slog.Infof("%s global cfg:%s path:%s", fun, scfg_global, path)

	path = fmt.Sprintf("%s/%s/%s", m.confEtcd.useBaseloc, BASE_LOC_ETC, m.servLocation)
	scfg, err := getValue(m.etcdClient, path)
	if err != nil {
		slog.Warnf("%s serv config value path:%s err:%s", fun, path, err)
	}

	slog.Infof("%s cfg:%s path:%s", fun, scfg, path)
	tf := sconf.NewTierConf()
	err = tf.Load(scfg_global)
	if err != nil {
		return err
	}

	err = tf.Load(scfg)
	if err != nil {
		return err
	}

	err = tf.Unmarshal(cfg)
	if err != nil {
		return err
	}

	return nil
}

// etcd v2 接口
func NewServBaseV2(confEtcd configEtcd, servLocation, skey string) (*ServBaseV2, error) {
	fun := "NewServBaseV2 -->"

	cfg := etcd.Config{
		Endpoints: confEtcd.etcdAddrs,
		Transport: etcd.DefaultTransport,
	}

	c, err := etcd.New(cfg)
	if err != nil {
		return nil, fmt.Errorf("create etchd client cfg error")
	}

	client := etcd.NewKeysAPI(c)
	if client == nil {
		return nil, fmt.Errorf("create etchd api error")
	}

	path := fmt.Sprintf("%s/%s/%s", confEtcd.useBaseloc, BASE_LOC_SKEY, servLocation)

	sid, err := retryGenSid(client, path, skey, 3)
	if err != nil {
		return nil, err
	}

	slog.Infof("%s path:%s sid:%d skey:%s", fun, path, sid, skey)

	dbloc := fmt.Sprintf("%s/%s", confEtcd.useBaseloc, BASE_LOC_DB)

	var dr *dbrouter.Router
	jscfg, err := getValue(client, dbloc)
	if err != nil {
		slog.Warnf("%s db:%s config notfound", fun, dbloc)
	} else {
		dr, err = dbrouter.NewRouter(jscfg)
		if err != nil {
			return nil, err
		}
	}

	reg := &ServBaseV2{
		confEtcd:     confEtcd,
		dbLocation:   dbloc,
		servLocation: servLocation,
		sessKey:      skey,
		etcdClient:   client,
		servId:       sid,
		locks:        make(map[string]*ssync.Mutex),
		hearts:       make(map[string]*distLockHeart),

		dbRouter: dr,
	}

	svrInfo := strings.SplitN(servLocation, "/", 2)
	if len(svrInfo) == 2 {
		reg.servGroup = svrInfo[0]
		reg.servName = svrInfo[1]
	} else {
		slog.Warnf("%s servLocation:%s do not match group/service format", fun, servLocation)
	}

	sf, err := initSnowflake(sid)
	if err != nil {
		return nil, err
	}

	reg.IdGenerator.snow = sf
	reg.IdGenerator.slow = make(map[string]*slowid.Slowid)
	reg.IdGenerator.servId = sid

	return reg, nil

}

// mutex
<|endoftext|>


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

// Package arco provides functions for accessing a gridengine ARCo database
package arco

import (
	"database/sql"
	pq "github.com/bmizerany/pq"
	"time"
)

type scannable interface {
	Scan(v ...interface{}) error
}

type DB struct {
	db *sql.DB
}

// Open creates a new connection to the Arco database.
// URL is a Postgres connection string in the form:
//    "postgres://bob:secret@1.2.3.4:5432/mydb?option=value"
func Open(url string) (*DB, error) {
	dsn, err := pq.ParseURL(url)
	if err != nil {
		return nil, err
	}
	db, err := sql.Open("postgres", dsn)
	return &DB{db}, err
}

const jobQuery = `SELECT j_job_number, j_task_number, j_pe_taskid, j_job_name, j_group, j_owner,
j_account, j_priority, j_submission_time, j_project, j_department
FROM sge_job
WHERE j_job_number = $1 AND j_task_number = -1
ORDER BY j_job_number DESC`

type Job struct {
	JobNumber      int
	TaskNumber     int
	PETaskId       string
	JobName        string
	Group          string
	Owner          string
	Account        string
	Priority       string
	SubmissionTime time.Time
	Project        string
	Department     string
}

// QueryJob queries the job table for information about a job number
func (d DB) QueryJob(n int) (*Job, error) {
	var j Job
	r := d.db.QueryRow(jobQuery, n)
	err := r.Scan(&j.JobNumber, &j.TaskNumber, &j.PETaskId, &j.JobName, &j.Group, &j.Owner,
		&j.Account, &j.Priority, &j.SubmissionTime, &j.Project, &j.Department)
	return &j, err
}

type Accounting struct {
	JobNumber      int
	TaskNumber     int
	PETaskId       string
	Name           string
	Group          string
	Username       string
	Account        string
	Project        string
	Department     string
	SubmissionTime time.Time
	ARParent       int
	StartTime      time.Time
	EndTime        time.Time
	WallClockTime  int
	CPU            float64
	Memory         float64
	IO             float64
	IOWait         float64
	MaxVMem        float64
	ExitStatus     int
	MaxRSS         int
}

// scanAccounting scans a scannable in to an Accounting struct
func scanAccounting(r scannable) (*Accounting, error) {
	var a Accounting
	err := r.Scan(&a.JobNumber, &a.TaskNumber, &a.PETaskId, &a.Name, &a.Group, &a.Username,
		&a.Account, &a.Project, &a.Department, &a.SubmissionTime, &a.ARParent, &a.StartTime,
		&a.EndTime, &a.WallClockTime, &a.CPU, &a.Memory, &a.IO, &a.IOWait, &a.MaxVMem, &a.ExitStatus, &a.MaxRSS)
	return &a, err
}

const accountingQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE job_number = $1
ORDER BY task_number`

// QueryAccounting queries the view_accounting view for accounting information for a job number j.
// It returns accounting records for all tasks.
func (d DB) QueryAccounting(j int) ([]Accounting, error) {
	rows, err := d.db.Query(accountingQuery, j)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var as []Accounting

	for rows.Next() {
		a, err := scanAccounting(rows)
		if err != nil {
			return nil, err
		}
		as = append(as, *a)
	}

	return as, rows.Err()
}

const accountingTaskQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE job_number = $1 AND task_number = $2`

// QueryAccountingTask queries the view_accounting view for accounting information of a task t of a job j.
func (d DB) QueryAccountingTask(j, t int) (*Accounting, error) {
	row := d.db.QueryRow(accountingTaskQuery, j, t)
	return scanAccounting(row)
}

const accountingTimesQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE start_time < $1 AND end_time > $2
ORDER BY job_number, task_number, pe_taskid`

// QueryAccountingTimes queries the view_accounting view for all accounting records of jobs that ran in a given
// time period.
func (d DB) QueryAccountingTimes(start, end time.Time) ([]Accounting, error) {
	rows, err := d.db.Query(accountingTimesQuery, start, end)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var as []Accounting

	for rows.Next() {
		a, err := scanAccounting(rows)
		if err != nil {
			return nil, err
		}
		as = append(as, *a)
	}

	return as, rows.Err()
}

type Log struct {
	JobNumber  int
	TaskNumber int
	PETaskId   int
	JobName    string
	User       string
	Account    string
	Project    string
	Department string
	Time       time.Time
	Event      string
	State      string
	Initiator  string
	Host       string
	Message    string
}

const logQuery = `SELECT job_number, task_number, pe_taskid, name, user, account, project, department,
time, event, state, initiator, host, message
FROM view_job_log_ordered
WHERE job_number = $1 AND task_number = $2`

// QueryLogs returns a list of all log entries for a job and task number. A task number of -1 returns a log summary for an
// array job.
func (d DB) QueryLogs(j, t int) ([]Log, error) {
	rows, err := d.db.Query(logQuery, j, t)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var logs []Log

	for rows.Next() {
		var l Log
		err := rows.Scan(&l.JobNumber, &l.TaskNumber, &l.PETaskId, &l.JobName, &l.User, &l.Account, &l.Project, &l.Department,
			&l.Time, &l.Event, &l.State, &l.Initiator, &l.Host, &l.Message)
		if err != nil {
			return nil, err
		}
		logs = append(logs, l)
	}

	return logs, err
}
<commit_msg>s/bmizerany/lib<commit_after>// Copyright 2012 Kamil Kisiel. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package arco provides functions for accessing a gridengine ARCo database
package arco

import (
	"database/sql"
	pq "github.com/lib/pq"
	"time"
)

type scannable interface {
	Scan(v ...interface{}) error
}

type DB struct {
	db *sql.DB
}

// Open creates a new connection to the Arco database.
// URL is a Postgres connection string in the form:
//    "postgres://bob:secret@1.2.3.4:5432/mydb?option=value"
func Open(url string) (*DB, error) {
	dsn, err := pq.ParseURL(url)
	if err != nil {
		return nil, err
	}
	db, err := sql.Open("postgres", dsn)
	return &DB{db}, err
}

const jobQuery = `SELECT j_job_number, j_task_number, j_pe_taskid, j_job_name, j_group, j_owner,
j_account, j_priority, j_submission_time, j_project, j_department
FROM sge_job
WHERE j_job_number = $1 AND j_task_number = -1
ORDER BY j_job_number DESC`

type Job struct {
	JobNumber      int
	TaskNumber     int
	PETaskId       string
	JobName        string
	Group          string
	Owner          string
	Account        string
	Priority       string
	SubmissionTime time.Time
	Project        string
	Department     string
}

// QueryJob queries the job table for information about a job number
func (d DB) QueryJob(n int) (*Job, error) {
	var j Job
	r := d.db.QueryRow(jobQuery, n)
	err := r.Scan(&j.JobNumber, &j.TaskNumber, &j.PETaskId, &j.JobName, &j.Group, &j.Owner,
		&j.Account, &j.Priority, &j.SubmissionTime, &j.Project, &j.Department)
	return &j, err
}

type Accounting struct {
	JobNumber      int
	TaskNumber     int
	PETaskId       string
	Name           string
	Group          string
	Username       string
	Account        string
	Project        string
	Department     string
	SubmissionTime time.Time
	ARParent       int
	StartTime      time.Time
	EndTime        time.Time
	WallClockTime  int
	CPU            float64
	Memory         float64
	IO             float64
	IOWait         float64
	MaxVMem        float64
	ExitStatus     int
	MaxRSS         int
}

// scanAccounting scans a scannable in to an Accounting struct
func scanAccounting(r scannable) (*Accounting, error) {
	var a Accounting
	err := r.Scan(&a.JobNumber, &a.TaskNumber, &a.PETaskId, &a.Name, &a.Group, &a.Username,
		&a.Account, &a.Project, &a.Department, &a.SubmissionTime, &a.ARParent, &a.StartTime,
		&a.EndTime, &a.WallClockTime, &a.CPU, &a.Memory, &a.IO, &a.IOWait, &a.MaxVMem, &a.ExitStatus, &a.MaxRSS)
	return &a, err
}

const accountingQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE job_number = $1
ORDER BY task_number`

// QueryAccounting queries the view_accounting view for accounting information for a job number j.
// It returns accounting records for all tasks.
func (d DB) QueryAccounting(j int) ([]Accounting, error) {
	rows, err := d.db.Query(accountingQuery, j)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var as []Accounting

	for rows.Next() {
		a, err := scanAccounting(rows)
		if err != nil {
			return nil, err
		}
		as = append(as, *a)
	}

	return as, rows.Err()
}

const accountingTaskQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE job_number = $1 AND task_number = $2`

// QueryAccountingTask queries the view_accounting view for accounting information of a task t of a job j.
func (d DB) QueryAccountingTask(j, t int) (*Accounting, error) {
	row := d.db.QueryRow(accountingTaskQuery, j, t)
	return scanAccounting(row)
}

const accountingTimesQuery = `SELECT job_number, task_number, pe_taskid, name, \"group\",
username, account, project, department, submission_time, ar_parent, start_time, end_time,
wallclock_time, cpu, mem, io, iow, maxvmem, exit_status, maxrss,
FROM view_accounting
WHERE start_time < $1 AND end_time > $2
ORDER BY job_number, task_number, pe_taskid`

// QueryAccountingTimes queries the view_accounting view for all accounting records of jobs that ran in a given
// time period.
func (d DB) QueryAccountingTimes(start, end time.Time) ([]Accounting, error) {
	rows, err := d.db.Query(accountingTimesQuery, start, end)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var as []Accounting

	for rows.Next() {
		a, err := scanAccounting(rows)
		if err != nil {
			return nil, err
		}
		as = append(as, *a)
	}

	return as, rows.Err()
}

type Log struct {
	JobNumber  int
	TaskNumber int
	PETaskId   int
	JobName    string
	User       string
	Account    string
	Project    string
	Department string
	Time       time.Time
	Event      string
	State      string
	Initiator  string
	Host       string
	Message    string
}

const logQuery = `SELECT job_number, task_number, pe_taskid, name, user, account, project, department,
time, event, state, initiator, host, message
FROM view_job_log_ordered
WHERE job_number = $1 AND task_number = $2`

// QueryLogs returns a list of all log entries for a job and task number. A task number of -1 returns a log summary for an
// array job.
func (d DB) QueryLogs(j, t int) ([]Log, error) {
	rows, err := d.db.Query(logQuery, j, t)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var logs []Log

	for rows.Next() {
		var l Log
		err := rows.Scan(&l.JobNumber, &l.TaskNumber, &l.PETaskId, &l.JobName, &l.User, &l.Account, &l.Project, &l.Department,
			&l.Time, &l.Event, &l.State, &l.Initiator, &l.Host, &l.Message)
		if err != nil {
			return nil, err
		}
		logs = append(logs, l)
	}

	return logs, err
}
<|endoftext|>