<commit_before>package vault

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/SermoDigital/jose/crypto"
	"github.com/SermoDigital/jose/jws"
	"github.com/SermoDigital/jose/jwt"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/vault/helper/jsonutil"
	"github.com/hashicorp/vault/logical"
)

const (
	// The location of the key used to generate response-wrapping JWTs
	coreWrappingJWTKeyPath = "core/wrapping/jwtkey"
)

func (c *Core) ensureWrappingKey() error {
	entry, err := c.barrier.Get(coreWrappingJWTKeyPath)
	if err != nil {
		return err
	}

	var keyParams clusterKeyParams

	if entry == nil {
		key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
		if err != nil {
			return errwrap.Wrapf("failed to generate wrapping key: {{err}}", err)
		}
		keyParams.D = key.D
		keyParams.X = key.X
		keyParams.Y = key.Y
		keyParams.Type = corePrivateKeyTypeP521
		val, err := jsonutil.EncodeJSON(keyParams)
		if err != nil {
			return errwrap.Wrapf("failed to encode wrapping key: {{err}}", err)
		}
		entry = &Entry{
			Key:   coreWrappingJWTKeyPath,
			Value: val,
		}
		if err = c.barrier.Put(entry); err != nil {
			return errwrap.Wrapf("failed to store wrapping key: {{err}}", err)
		}
	}

	// Redundant if we just created it, but in this case serves as a check anyways
	if err = jsonutil.DecodeJSON(entry.Value, &keyParams); err != nil {
		return errwrap.Wrapf("failed to decode wrapping key parameters: {{err}}", err)
	}

	c.wrappingJWTKey = &ecdsa.PrivateKey{
		PublicKey: ecdsa.PublicKey{
			Curve: elliptic.P521(),
			X:     keyParams.X,
			Y:     keyParams.Y,
		},
		D: keyParams.D,
	}

	c.logger.Info("core: loaded wrapping token key")

	return nil
}

func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*logical.Response, error) {
	// Before wrapping, obey special rules for listing: if no entries are
	// found, 404. This prevents unwrapping only to find empty data.
	if req.Operation == logical.ListOperation {
		if resp == nil || len(resp.Data) == 0 {
			return nil, logical.ErrUnsupportedPath
		}
		keysRaw, ok := resp.Data["keys"]
		if !ok || keysRaw == nil {
			return nil, logical.ErrUnsupportedPath
		}
		keys, ok := keysRaw.([]string)
		if !ok {
			return nil, logical.ErrUnsupportedPath
		}
		if len(keys) == 0 {
			return nil, logical.ErrUnsupportedPath
		}
	}

	var err error

	// If we are wrapping, the first part (performed in this functions) happens
	// before auditing so that resp.WrapInfo.Token can contain the HMAC'd
	// wrapping token ID in the audit logs, so that it can be determined from
	// the audit logs whether the token was ever actually used.
	creationTime := time.Now()
	te := TokenEntry{
		Path:           req.Path,
		Policies:       []string{"response-wrapping"},
		CreationTime:   creationTime.Unix(),
		TTL:            resp.WrapInfo.TTL,
		NumUses:        1,
		ExplicitMaxTTL: resp.WrapInfo.TTL,
	}

	if err := c.tokenStore.create(&te); err != nil {
		c.logger.Error("core: failed to create wrapping token", "error", err)
		return nil, ErrInternalError
	}

	resp.WrapInfo.Token = te.ID
	resp.WrapInfo.CreationTime = creationTime

	// This will only be non-nil if this response contains a token, so in that
	// case put the accessor in the wrap info.
	if resp.Auth != nil {
		resp.WrapInfo.WrappedAccessor = resp.Auth.Accessor
	}

	switch resp.WrapInfo.Format {
	case "jwt":
		// Create the JWT
		claims := jws.Claims{}
		// Map the JWT ID to the token ID for ease ofuse
		claims.SetJWTID(te.ID)
		// Set the issue time to the creation time
		claims.SetIssuedAt(creationTime)
		// Set the expiration to the TTL
		claims.SetExpiration(creationTime.Add(resp.WrapInfo.TTL))
		if resp.Auth != nil {
			claims.Set("accessor", resp.Auth.Accessor)
		}
		claims.Set("type", "wrapping")
		claims.Set("addr", c.redirectAddr)
		jwt := jws.NewJWT(claims, crypto.SigningMethodES512)
		serWebToken, err := jwt.Serialize(c.wrappingJWTKey)
		if err != nil {
			c.logger.Error("core: failed to serialize JWT", "error", err)
			return nil, ErrInternalError
		}
		resp.WrapInfo.Token = string(serWebToken)
	}

	cubbyReq := &logical.Request{
		Operation:   logical.CreateOperation,
		Path:        "cubbyhole/response",
		ClientToken: te.ID,
	}

	// During a rewrap, store the original response, don't wrap it again.
	if req.Path == "sys/wrapping/rewrap" {
		cubbyReq.Data = map[string]interface{}{
			"response": resp.Data["response"],
		}
	} else {
		httpResponse := logical.LogicalResponseToHTTPResponse(resp)

		// Add the unique identifier of the original request to the response
		httpResponse.RequestID = req.ID

		// Because of the way that JSON encodes (likely just in Go) we actually get
		// mixed-up values for ints if we simply put this object in the response
		// and encode the whole thing; so instead we marshal it first, then store
		// the string response. This actually ends up making it easier on the
		// client side, too, as it becomes a straight read-string-pass-to-unmarshal
		// operation.

		marshaledResponse, err := json.Marshal(httpResponse)
		if err != nil {
			c.logger.Error("core: failed to marshal wrapped response", "error", err)
			return nil, ErrInternalError
		}

		cubbyReq.Data = map[string]interface{}{
			"response": string(marshaledResponse),
		}
	}

	cubbyResp, err := c.router.Route(cubbyReq)
	if err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapped response information", "error", err)
		return nil, ErrInternalError
	}
	if cubbyResp != nil && cubbyResp.IsError() {
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapped response information", "error", cubbyResp.Data["error"])
		return cubbyResp, nil
	}

	// Store info for lookup
	cubbyReq.Path = "cubbyhole/wrapinfo"
	cubbyReq.Data = map[string]interface{}{
		"creation_ttl":  resp.WrapInfo.TTL,
		"creation_time": creationTime,
	}
	cubbyResp, err = c.router.Route(cubbyReq)
	if err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapping information", "error", err)
		return nil, ErrInternalError
	}
	if cubbyResp != nil && cubbyResp.IsError() {
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapping information", "error", cubbyResp.Data["error"])
		return cubbyResp, nil
	}

	auth := &logical.Auth{
		ClientToken: te.ID,
		Policies:    []string{"response-wrapping"},
		LeaseOptions: logical.LeaseOptions{
			TTL:       te.TTL,
			Renewable: false,
		},
	}

	// Register the wrapped token with the expiration manager
	if err := c.expiration.RegisterAuth(te.Path, auth); err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to register cubbyhole wrapping token lease", "request_path", req.Path, "error", err)
		return nil, ErrInternalError
	}

	return nil, nil
}

func (c *Core) ValidateWrappingToken(req *logical.Request) (bool, error) {
	if req == nil {
		return false, fmt.Errorf("invalid request")
	}

	var err error

	var token string
	if req.Data != nil && req.Data["token"] != nil {
		if tokenStr, ok := req.Data["token"].(string); !ok {
			return false, fmt.Errorf("could not decode token in request body")
		} else if tokenStr == "" {
			return false, fmt.Errorf("empty token in request body")
		} else {
			token = tokenStr
		}
	} else {
		token = req.ClientToken
	}

	// Check for it being a JWT. If it is, and it is valid, we extract the
	// internal client token from it and use that during lookup.
	if strings.Count(token, ".") == 2 {
		wt, err := jws.ParseJWT([]byte(token))
		// If there's an error we simply fall back to attempting to use it as a regular token
		if err == nil && wt != nil {
			validator := &jwt.Validator{}
			validator.SetClaim("type", "wrapping")
			if err = wt.Validate(&c.wrappingJWTKey.PublicKey, crypto.SigningMethodES512, []*jwt.Validator{validator}...); err != nil {
				return false, errwrap.Wrapf("wrapping token signature could not be validated: {{err}}", err)
			}
			token, _ = wt.Claims().JWTID()
		}
	}

	if token == "" {
		return false, fmt.Errorf("token is empty")
	}

	c.stateLock.RLock()
	defer c.stateLock.RUnlock()
	if c.sealed {
		return nil, ErrSealed
	}
	if c.standby {
		return nil, ErrStandby
	}

	te, err := c.tokenStore.Lookup(token)
	if err != nil {
		return false, err
	}
	if te == nil {
		return false, nil
	}

	if len(te.Policies) != 1 {
		return false, nil
	}

	if te.Policies[0] != responseWrappingPolicyName {
		return false, nil
	}

	return true, nil
}

// parseVaultTokenFromJWT returns a string iff the token was a JWT and we could
// extract the original token ID from inside
func (c *Core) parseVaultTokenFromJWT(token string) *string {
	var result string
	if strings.Count(token, ".") != 2 {
		return nil
	}

	wt, err := jws.ParseJWT([]byte(token))
	if err != nil || wt == nil {
		return nil
	}

	result, _ = wt.Claims().JWTID()

	return &result
}
<commit_msg>Fixed return types<commit_after>package vault

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/SermoDigital/jose/crypto"
	"github.com/SermoDigital/jose/jws"
	"github.com/SermoDigital/jose/jwt"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/vault/helper/jsonutil"
	"github.com/hashicorp/vault/logical"
)

const (
	// The location of the key used to generate response-wrapping JWTs
	coreWrappingJWTKeyPath = "core/wrapping/jwtkey"
)

func (c *Core) ensureWrappingKey() error {
	entry, err := c.barrier.Get(coreWrappingJWTKeyPath)
	if err != nil {
		return err
	}

	var keyParams clusterKeyParams

	if entry == nil {
		key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
		if err != nil {
			return errwrap.Wrapf("failed to generate wrapping key: {{err}}", err)
		}
		keyParams.D = key.D
		keyParams.X = key.X
		keyParams.Y = key.Y
		keyParams.Type = corePrivateKeyTypeP521
		val, err := jsonutil.EncodeJSON(keyParams)
		if err != nil {
			return errwrap.Wrapf("failed to encode wrapping key: {{err}}", err)
		}
		entry = &Entry{
			Key:   coreWrappingJWTKeyPath,
			Value: val,
		}
		if err = c.barrier.Put(entry); err != nil {
			return errwrap.Wrapf("failed to store wrapping key: {{err}}", err)
		}
	}

	// Redundant if we just created it, but in this case serves as a check anyways
	if err = jsonutil.DecodeJSON(entry.Value, &keyParams); err != nil {
		return errwrap.Wrapf("failed to decode wrapping key parameters: {{err}}", err)
	}

	c.wrappingJWTKey = &ecdsa.PrivateKey{
		PublicKey: ecdsa.PublicKey{
			Curve: elliptic.P521(),
			X:     keyParams.X,
			Y:     keyParams.Y,
		},
		D: keyParams.D,
	}

	c.logger.Info("core: loaded wrapping token key")

	return nil
}

func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*logical.Response, error) {
	// Before wrapping, obey special rules for listing: if no entries are
	// found, 404. This prevents unwrapping only to find empty data.
	if req.Operation == logical.ListOperation {
		if resp == nil || len(resp.Data) == 0 {
			return nil, logical.ErrUnsupportedPath
		}
		keysRaw, ok := resp.Data["keys"]
		if !ok || keysRaw == nil {
			return nil, logical.ErrUnsupportedPath
		}
		keys, ok := keysRaw.([]string)
		if !ok {
			return nil, logical.ErrUnsupportedPath
		}
		if len(keys) == 0 {
			return nil, logical.ErrUnsupportedPath
		}
	}

	var err error

	// If we are wrapping, the first part (performed in this functions) happens
	// before auditing so that resp.WrapInfo.Token can contain the HMAC'd
	// wrapping token ID in the audit logs, so that it can be determined from
	// the audit logs whether the token was ever actually used.
	creationTime := time.Now()
	te := TokenEntry{
		Path:           req.Path,
		Policies:       []string{"response-wrapping"},
		CreationTime:   creationTime.Unix(),
		TTL:            resp.WrapInfo.TTL,
		NumUses:        1,
		ExplicitMaxTTL: resp.WrapInfo.TTL,
	}

	if err := c.tokenStore.create(&te); err != nil {
		c.logger.Error("core: failed to create wrapping token", "error", err)
		return nil, ErrInternalError
	}

	resp.WrapInfo.Token = te.ID
	resp.WrapInfo.CreationTime = creationTime

	// This will only be non-nil if this response contains a token, so in that
	// case put the accessor in the wrap info.
	if resp.Auth != nil {
		resp.WrapInfo.WrappedAccessor = resp.Auth.Accessor
	}

	switch resp.WrapInfo.Format {
	case "jwt":
		// Create the JWT
		claims := jws.Claims{}
		// Map the JWT ID to the token ID for ease ofuse
		claims.SetJWTID(te.ID)
		// Set the issue time to the creation time
		claims.SetIssuedAt(creationTime)
		// Set the expiration to the TTL
		claims.SetExpiration(creationTime.Add(resp.WrapInfo.TTL))
		if resp.Auth != nil {
			claims.Set("accessor", resp.Auth.Accessor)
		}
		claims.Set("type", "wrapping")
		claims.Set("addr", c.redirectAddr)
		jwt := jws.NewJWT(claims, crypto.SigningMethodES512)
		serWebToken, err := jwt.Serialize(c.wrappingJWTKey)
		if err != nil {
			c.logger.Error("core: failed to serialize JWT", "error", err)
			return nil, ErrInternalError
		}
		resp.WrapInfo.Token = string(serWebToken)
	}

	cubbyReq := &logical.Request{
		Operation:   logical.CreateOperation,
		Path:        "cubbyhole/response",
		ClientToken: te.ID,
	}

	// During a rewrap, store the original response, don't wrap it again.
	if req.Path == "sys/wrapping/rewrap" {
		cubbyReq.Data = map[string]interface{}{
			"response": resp.Data["response"],
		}
	} else {
		httpResponse := logical.LogicalResponseToHTTPResponse(resp)

		// Add the unique identifier of the original request to the response
		httpResponse.RequestID = req.ID

		// Because of the way that JSON encodes (likely just in Go) we actually get
		// mixed-up values for ints if we simply put this object in the response
		// and encode the whole thing; so instead we marshal it first, then store
		// the string response. This actually ends up making it easier on the
		// client side, too, as it becomes a straight read-string-pass-to-unmarshal
		// operation.

		marshaledResponse, err := json.Marshal(httpResponse)
		if err != nil {
			c.logger.Error("core: failed to marshal wrapped response", "error", err)
			return nil, ErrInternalError
		}

		cubbyReq.Data = map[string]interface{}{
			"response": string(marshaledResponse),
		}
	}

	cubbyResp, err := c.router.Route(cubbyReq)
	if err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapped response information", "error", err)
		return nil, ErrInternalError
	}
	if cubbyResp != nil && cubbyResp.IsError() {
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapped response information", "error", cubbyResp.Data["error"])
		return cubbyResp, nil
	}

	// Store info for lookup
	cubbyReq.Path = "cubbyhole/wrapinfo"
	cubbyReq.Data = map[string]interface{}{
		"creation_ttl":  resp.WrapInfo.TTL,
		"creation_time": creationTime,
	}
	cubbyResp, err = c.router.Route(cubbyReq)
	if err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapping information", "error", err)
		return nil, ErrInternalError
	}
	if cubbyResp != nil && cubbyResp.IsError() {
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to store wrapping information", "error", cubbyResp.Data["error"])
		return cubbyResp, nil
	}

	auth := &logical.Auth{
		ClientToken: te.ID,
		Policies:    []string{"response-wrapping"},
		LeaseOptions: logical.LeaseOptions{
			TTL:       te.TTL,
			Renewable: false,
		},
	}

	// Register the wrapped token with the expiration manager
	if err := c.expiration.RegisterAuth(te.Path, auth); err != nil {
		// Revoke since it's not yet being tracked for expiration
		c.tokenStore.Revoke(te.ID)
		c.logger.Error("core: failed to register cubbyhole wrapping token lease", "request_path", req.Path, "error", err)
		return nil, ErrInternalError
	}

	return nil, nil
}

func (c *Core) ValidateWrappingToken(req *logical.Request) (bool, error) {
	if req == nil {
		return false, fmt.Errorf("invalid request")
	}

	var err error

	var token string
	if req.Data != nil && req.Data["token"] != nil {
		if tokenStr, ok := req.Data["token"].(string); !ok {
			return false, fmt.Errorf("could not decode token in request body")
		} else if tokenStr == "" {
			return false, fmt.Errorf("empty token in request body")
		} else {
			token = tokenStr
		}
	} else {
		token = req.ClientToken
	}

	// Check for it being a JWT. If it is, and it is valid, we extract the
	// internal client token from it and use that during lookup.
	if strings.Count(token, ".") == 2 {
		wt, err := jws.ParseJWT([]byte(token))
		// If there's an error we simply fall back to attempting to use it as a regular token
		if err == nil && wt != nil {
			validator := &jwt.Validator{}
			validator.SetClaim("type", "wrapping")
			if err = wt.Validate(&c.wrappingJWTKey.PublicKey, crypto.SigningMethodES512, []*jwt.Validator{validator}...); err != nil {
				return false, errwrap.Wrapf("wrapping token signature could not be validated: {{err}}", err)
			}
			token, _ = wt.Claims().JWTID()
		}
	}

	if token == "" {
		return false, fmt.Errorf("token is empty")
	}

	c.stateLock.RLock()
	defer c.stateLock.RUnlock()
	if c.sealed {
		return false, ErrSealed
	}
	if c.standby {
		return false, ErrStandby
	}

	te, err := c.tokenStore.Lookup(token)
	if err != nil {
		return false, err
	}
	if te == nil {
		return false, nil
	}

	if len(te.Policies) != 1 {
		return false, nil
	}

	if te.Policies[0] != responseWrappingPolicyName {
		return false, nil
	}

	return true, nil
}

// parseVaultTokenFromJWT returns a string iff the token was a JWT and we could
// extract the original token ID from inside
func (c *Core) parseVaultTokenFromJWT(token string) *string {
	var result string
	if strings.Count(token, ".") != 2 {
		return nil
	}

	wt, err := jws.ParseJWT([]byte(token))
	if err != nil || wt == nil {
		return nil
	}

	result, _ = wt.Claims().JWTID()

	return &result
}
<|endoftext|>


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

// +build js

package ui

import (
	"strconv"
	"strings"

	"github.com/gopherjs/gopherjs/js"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

var canvas *js.Object

type userInterface struct {
	scale       float64
	deviceScale float64
	sizeChanged bool
	windowFocus bool
}

var currentUI = &userInterface{
	sizeChanged: true,
	windowFocus: true,
}

// NOTE: This returns true even when the browser is not active.
func shown() bool {
	return !js.Global.Get("document").Get("hidden").Bool()
}

func SetScreenSize(width, height int) bool {
	return currentUI.setScreenSize(width, height, currentUI.scale)
}

func SetScreenScale(scale float64) bool {
	width, height := currentUI.size()
	return currentUI.setScreenSize(width, height, scale)
}

func ScreenScale() float64 {
	return currentUI.scale
}

func SetFullscreen(fullscreen bool) bool {
	// TODO: Implement
	return false
}

func IsFullscreen() bool {
	// TODO: Implement
	return false
}

func ScreenOffset() (float64, float64) {
	return 0, 0
}

func adjustCursorPosition(x, y int) (int, int) {
	return x, y
}

func SetCursorVisibility(visibility bool) {
	if visibility {
		canvas.Get("style").Set("cursor", "auto")
	} else {
		canvas.Get("style").Set("cursor", "none")
	}
}

func (u *userInterface) actualScreenScale() float64 {
	return u.scale * u.deviceScale
}

func (u *userInterface) update(g GraphicsContext) error {
	if !u.windowFocus {
		return nil
	}
	if opengl.GetContext().IsContextLost() {
		opengl.GetContext().RestoreContext()
		g.Invalidate()
	}
	currentInput.updateGamepads()
	if u.sizeChanged {
		u.sizeChanged = false
		w, h := u.size()
		g.SetSize(w, h, u.actualScreenScale())
		return nil
	}
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	ch := make(chan error)
	var f func()
	f = func() {
		go func() {
			if err := u.update(g); err != nil {
				ch <- err
				close(ch)
				return
			}
			js.Global.Get("window").Call("requestAnimationFrame", f)
		}()
	}
	f()
	return <-ch
}

func touchEventToTouches(e *js.Object) []touch {
	scale := currentUI.scale
	j := e.Get("targetTouches")
	rect := canvas.Call("getBoundingClientRect")
	left, top := rect.Get("left").Int(), rect.Get("top").Int()
	t := make([]touch, j.Get("length").Int())
	for i := 0; i < len(t); i++ {
		jj := j.Call("item", i)
		t[i].id = jj.Get("identifier").Int()
		t[i].x = int(float64(jj.Get("clientX").Int()-left) / scale)
		t[i].y = int(float64(jj.Get("clientY").Int()-top) / scale)
	}
	return t
}

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	// Do nothing in node.js.
	if js.Global.Get("require") != js.Undefined {
		return nil
	}

	doc := js.Global.Get("document")
	window := js.Global.Get("window")
	if doc.Get("body") == nil {
		ch := make(chan struct{})
		window.Call("addEventListener", "load", func() {
			close(ch)
		})
		<-ch
	}
	window.Call("addEventListener", "focus", func() {
		currentUI.windowFocus = true
	})
	window.Call("addEventListener", "blur", func() {
		currentUI.windowFocus = false
	})

	// Adjust the initial scale to 1.
	// https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
	meta := doc.Call("createElement", "meta")
	meta.Set("name", "viewport")
	meta.Set("content", "width=device-width, initial-scale=1")
	doc.Get("body").Call("appendChild", meta)

	canvas = doc.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)
	doc.Get("body").Call("appendChild", canvas)

	htmlStyle := doc.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := doc.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("position", "relative")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")
	// TODO: This is OK as long as the game is in an independent iframe.
	// What if the canvas is embedded in a HTML directly?
	doc.Get("body").Call("addEventListener", "click", func() {
		canvas.Call("focus")
	})

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("position", "absolute")

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	// Keyboard
	canvas.Call("addEventListener", "keydown", func(e *js.Object) {
		e.Call("preventDefault")
		if e.Get("code") == js.Undefined {
			// Assume that UA is Safari.
			code := e.Get("keyCode").Int()
			currentInput.keyDownSafari(code)
			return
		}
		code := e.Get("code").String()
		currentInput.keyDown(code)
	})
	canvas.Call("addEventListener", "keyup", func(e *js.Object) {
		e.Call("preventDefault")
		if e.Get("code") == js.Undefined {
			// Assume that UA is Safari.
			code := e.Get("keyCode").Int()
			currentInput.keyUpSafari(code)
		}
		code := e.Get("code").String()
		currentInput.keyUp(code)
	})

	// Mouse
	canvas.Call("addEventListener", "mousedown", func(e *js.Object) {
		e.Call("preventDefault")
		button := e.Get("button").Int()
		currentInput.mouseDown(button)
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "mouseup", func(e *js.Object) {
		e.Call("preventDefault")
		button := e.Get("button").Int()
		currentInput.mouseUp(button)
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "mousemove", func(e *js.Object) {
		e.Call("preventDefault")
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "contextmenu", func(e *js.Object) {
		e.Call("preventDefault")
	})

	// Touch
	canvas.Call("addEventListener", "touchstart", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})
	canvas.Call("addEventListener", "touchend", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})
	canvas.Call("addEventListener", "touchmove", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})

	// Gamepad
	window.Call("addEventListener", "gamepadconnected", func(e *js.Object) {
		// Do nothing.
	})

	canvas.Call("addEventListener", "webglcontextlost", func(e *js.Object) {
		e.Call("preventDefault")
	})
	canvas.Call("addEventListener", "webglcontextrestored", func(e *js.Object) {
		// Do nothing.
	})

	return nil
}

func setMouseCursorFromEvent(e *js.Object) {
	scale := currentUI.scale
	rect := canvas.Call("getBoundingClientRect")
	x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
	x -= rect.Get("left").Int()
	y -= rect.Get("top").Int()
	currentInput.setMouseCursor(int(float64(x)/scale), int(float64(y)/scale))
}

func devicePixelRatio() float64 {
	ratio := js.Global.Get("window").Get("devicePixelRatio").Float()
	if ratio == 0 {
		ratio = 1
	}
	return ratio
}

func RunMainThreadLoop(ch <-chan error) error {
	return <-ch
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	doc := js.Global.Get("document")
	doc.Set("title", title)
	u.setScreenSize(width, height, scale)
	canvas.Call("focus")
	if err := opengl.Init(); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) size() (width, height int) {
	a := u.actualScreenScale()
	if a == 0 {
		// a == 0 only on the initial state.
		return
	}
	width = int(canvas.Get("width").Float() / a)
	height = int(canvas.Get("height").Float() / a)
	return
}

func isSafari() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "Safari") {
		return false
	}
	if strings.Contains(ua, "Chrome") {
		return false
	}
	return true
}

func (u *userInterface) setScreenSize(width, height int, scale float64) bool {
	w, h := u.size()
	s := u.scale
	if w == width && h == height && s == scale {
		return false
	}
	u.scale = scale
	// CSS imageRendering seems useful to enlarge the screen,
	// but doesn't work in some cases (#306):
	// * Chrome just after restoring the lost context
	// * Safari
	// Let's use the pixel ratio as it is here.
	u.deviceScale = devicePixelRatio()
	canvas.Set("width", int(float64(width)*u.actualScreenScale()))
	canvas.Set("height", int(float64(height)*u.actualScreenScale()))
	canvasStyle := canvas.Get("style")

	cssWidth := int(float64(width) * scale)
	cssHeight := int(float64(height) * scale)
	canvasStyle.Set("width", strconv.Itoa(cssWidth)+"px")
	canvasStyle.Set("height", strconv.Itoa(cssHeight)+"px")
	// CSS calc requires space chars.
	canvasStyle.Set("left", "calc((100% - "+strconv.Itoa(cssWidth)+"px) / 2)")
	canvasStyle.Set("top", "calc((100% - "+strconv.Itoa(cssHeight)+"px) / 2)")
	u.sizeChanged = true
	return true
}
<commit_msg>ui: Bug fix: Should add <meta> to <head> instead of <body><commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build js

package ui

import (
	"strconv"
	"strings"

	"github.com/gopherjs/gopherjs/js"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

var canvas *js.Object

type userInterface struct {
	scale       float64
	deviceScale float64
	sizeChanged bool
	windowFocus bool
}

var currentUI = &userInterface{
	sizeChanged: true,
	windowFocus: true,
}

// NOTE: This returns true even when the browser is not active.
func shown() bool {
	return !js.Global.Get("document").Get("hidden").Bool()
}

func SetScreenSize(width, height int) bool {
	return currentUI.setScreenSize(width, height, currentUI.scale)
}

func SetScreenScale(scale float64) bool {
	width, height := currentUI.size()
	return currentUI.setScreenSize(width, height, scale)
}

func ScreenScale() float64 {
	return currentUI.scale
}

func SetFullscreen(fullscreen bool) bool {
	// TODO: Implement
	return false
}

func IsFullscreen() bool {
	// TODO: Implement
	return false
}

func ScreenOffset() (float64, float64) {
	return 0, 0
}

func adjustCursorPosition(x, y int) (int, int) {
	return x, y
}

func SetCursorVisibility(visibility bool) {
	if visibility {
		canvas.Get("style").Set("cursor", "auto")
	} else {
		canvas.Get("style").Set("cursor", "none")
	}
}

func (u *userInterface) actualScreenScale() float64 {
	return u.scale * u.deviceScale
}

func (u *userInterface) update(g GraphicsContext) error {
	if !u.windowFocus {
		return nil
	}
	if opengl.GetContext().IsContextLost() {
		opengl.GetContext().RestoreContext()
		g.Invalidate()
	}
	currentInput.updateGamepads()
	if u.sizeChanged {
		u.sizeChanged = false
		w, h := u.size()
		g.SetSize(w, h, u.actualScreenScale())
		return nil
	}
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	ch := make(chan error)
	var f func()
	f = func() {
		go func() {
			if err := u.update(g); err != nil {
				ch <- err
				close(ch)
				return
			}
			js.Global.Get("window").Call("requestAnimationFrame", f)
		}()
	}
	f()
	return <-ch
}

func touchEventToTouches(e *js.Object) []touch {
	scale := currentUI.scale
	j := e.Get("targetTouches")
	rect := canvas.Call("getBoundingClientRect")
	left, top := rect.Get("left").Int(), rect.Get("top").Int()
	t := make([]touch, j.Get("length").Int())
	for i := 0; i < len(t); i++ {
		jj := j.Call("item", i)
		t[i].id = jj.Get("identifier").Int()
		t[i].x = int(float64(jj.Get("clientX").Int()-left) / scale)
		t[i].y = int(float64(jj.Get("clientY").Int()-top) / scale)
	}
	return t
}

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	// Do nothing in node.js.
	if js.Global.Get("require") != js.Undefined {
		return nil
	}

	doc := js.Global.Get("document")
	window := js.Global.Get("window")
	if doc.Get("body") == nil {
		ch := make(chan struct{})
		window.Call("addEventListener", "load", func() {
			close(ch)
		})
		<-ch
	}
	window.Call("addEventListener", "focus", func() {
		currentUI.windowFocus = true
	})
	window.Call("addEventListener", "blur", func() {
		currentUI.windowFocus = false
	})

	// Adjust the initial scale to 1.
	// https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
	meta := doc.Call("createElement", "meta")
	meta.Set("name", "viewport")
	meta.Set("content", "width=device-width, initial-scale=1")
	doc.Get("head").Call("appendChild", meta)

	canvas = doc.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)
	doc.Get("body").Call("appendChild", canvas)

	htmlStyle := doc.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := doc.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("position", "relative")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")
	// TODO: This is OK as long as the game is in an independent iframe.
	// What if the canvas is embedded in a HTML directly?
	doc.Get("body").Call("addEventListener", "click", func() {
		canvas.Call("focus")
	})

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("position", "absolute")

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	// Keyboard
	canvas.Call("addEventListener", "keydown", func(e *js.Object) {
		e.Call("preventDefault")
		if e.Get("code") == js.Undefined {
			// Assume that UA is Safari.
			code := e.Get("keyCode").Int()
			currentInput.keyDownSafari(code)
			return
		}
		code := e.Get("code").String()
		currentInput.keyDown(code)
	})
	canvas.Call("addEventListener", "keyup", func(e *js.Object) {
		e.Call("preventDefault")
		if e.Get("code") == js.Undefined {
			// Assume that UA is Safari.
			code := e.Get("keyCode").Int()
			currentInput.keyUpSafari(code)
		}
		code := e.Get("code").String()
		currentInput.keyUp(code)
	})

	// Mouse
	canvas.Call("addEventListener", "mousedown", func(e *js.Object) {
		e.Call("preventDefault")
		button := e.Get("button").Int()
		currentInput.mouseDown(button)
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "mouseup", func(e *js.Object) {
		e.Call("preventDefault")
		button := e.Get("button").Int()
		currentInput.mouseUp(button)
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "mousemove", func(e *js.Object) {
		e.Call("preventDefault")
		setMouseCursorFromEvent(e)
	})
	canvas.Call("addEventListener", "contextmenu", func(e *js.Object) {
		e.Call("preventDefault")
	})

	// Touch
	canvas.Call("addEventListener", "touchstart", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})
	canvas.Call("addEventListener", "touchend", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})
	canvas.Call("addEventListener", "touchmove", func(e *js.Object) {
		e.Call("preventDefault")
		currentInput.updateTouches(touchEventToTouches(e))
	})

	// Gamepad
	window.Call("addEventListener", "gamepadconnected", func(e *js.Object) {
		// Do nothing.
	})

	canvas.Call("addEventListener", "webglcontextlost", func(e *js.Object) {
		e.Call("preventDefault")
	})
	canvas.Call("addEventListener", "webglcontextrestored", func(e *js.Object) {
		// Do nothing.
	})

	return nil
}

func setMouseCursorFromEvent(e *js.Object) {
	scale := currentUI.scale
	rect := canvas.Call("getBoundingClientRect")
	x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
	x -= rect.Get("left").Int()
	y -= rect.Get("top").Int()
	currentInput.setMouseCursor(int(float64(x)/scale), int(float64(y)/scale))
}

func devicePixelRatio() float64 {
	ratio := js.Global.Get("window").Get("devicePixelRatio").Float()
	if ratio == 0 {
		ratio = 1
	}
	return ratio
}

func RunMainThreadLoop(ch <-chan error) error {
	return <-ch
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	doc := js.Global.Get("document")
	doc.Set("title", title)
	u.setScreenSize(width, height, scale)
	canvas.Call("focus")
	if err := opengl.Init(); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) size() (width, height int) {
	a := u.actualScreenScale()
	if a == 0 {
		// a == 0 only on the initial state.
		return
	}
	width = int(canvas.Get("width").Float() / a)
	height = int(canvas.Get("height").Float() / a)
	return
}

func isSafari() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "Safari") {
		return false
	}
	if strings.Contains(ua, "Chrome") {
		return false
	}
	return true
}

func (u *userInterface) setScreenSize(width, height int, scale float64) bool {
	w, h := u.size()
	s := u.scale
	if w == width && h == height && s == scale {
		return false
	}
	u.scale = scale
	// CSS imageRendering seems useful to enlarge the screen,
	// but doesn't work in some cases (#306):
	// * Chrome just after restoring the lost context
	// * Safari
	// Let's use the pixel ratio as it is here.
	u.deviceScale = devicePixelRatio()
	canvas.Set("width", int(float64(width)*u.actualScreenScale()))
	canvas.Set("height", int(float64(height)*u.actualScreenScale()))
	canvasStyle := canvas.Get("style")

	cssWidth := int(float64(width) * scale)
	cssHeight := int(float64(height) * scale)
	canvasStyle.Set("width", strconv.Itoa(cssWidth)+"px")
	canvasStyle.Set("height", strconv.Itoa(cssHeight)+"px")
	// CSS calc requires space chars.
	canvasStyle.Set("left", "calc((100% - "+strconv.Itoa(cssWidth)+"px) / 2)")
	canvasStyle.Set("top", "calc((100% - "+strconv.Itoa(cssHeight)+"px) / 2)")
	u.sizeChanged = true
	return true
}
<|endoftext|>


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

package util

import (
	"context"
	"fmt"

	klog "k8s.io/klog/v2"
)

// enum defining logging levels.
const (
	Default klog.Level = iota + 1
	Useful
	Extended
	Debug
	Trace
)

type contextKey string

// CtxKey for context based logging.
var CtxKey = contextKey("ID")

// ReqID for logging request ID.
var ReqID = contextKey("Req-ID")

// Log helps in context based logging.
func Log(ctx context.Context, format string) string {
	id := ctx.Value(CtxKey)
	if id == nil {
		return format
	}
	a := fmt.Sprintf("ID: %v ", id)
	reqID := ctx.Value(ReqID)
	if reqID == nil {
		return a + format
	}
	a += fmt.Sprintf("Req-ID: %v ", reqID)
	return a + format
}

// FatalLog helps in logging fatal errors.
func FatalLog(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.FatalDepth(1, logMessage)
}

// ErrorLogMsg helps in logging errors with message.
func ErrorLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.ErrorDepth(1, logMessage)
}

// ErrorLog helps in logging errors with context.
func ErrorLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	klog.ErrorDepth(1, logMessage)
}

// WarningLog helps in logging warnings.
func WarningLog(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.WarningDepth(1, logMessage)
}

// DefaultLog helps in logging with klog.level 1.
func DefaultLog(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Default).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// UsefulLog helps in logging with klog.level 2.
func UsefulLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Useful).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// ExtendedLogMsg helps in logging a message with klog.level 3.
func ExtendedLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Extended).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// ExtendedLog helps in logging with klog.level 3.
func ExtendedLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Extended).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// DebugLogMsg helps in logging a message with klog.level 4.
func DebugLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Debug).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// DebugLog helps in logging with klog.level 4.
func DebugLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Debug).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// TraceLogMsg helps in logging a message with klog.level 5.
func TraceLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Trace).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// TraceLog helps in logging with klog.level 5.
func TraceLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Trace).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}
<commit_msg>util: rename WarningLog to WarningLogMsg<commit_after>/*
Copyright 2019 The Ceph-CSI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"context"
	"fmt"

	klog "k8s.io/klog/v2"
)

// enum defining logging levels.
const (
	Default klog.Level = iota + 1
	Useful
	Extended
	Debug
	Trace
)

type contextKey string

// CtxKey for context based logging.
var CtxKey = contextKey("ID")

// ReqID for logging request ID.
var ReqID = contextKey("Req-ID")

// Log helps in context based logging.
func Log(ctx context.Context, format string) string {
	id := ctx.Value(CtxKey)
	if id == nil {
		return format
	}
	a := fmt.Sprintf("ID: %v ", id)
	reqID := ctx.Value(ReqID)
	if reqID == nil {
		return a + format
	}
	a += fmt.Sprintf("Req-ID: %v ", reqID)
	return a + format
}

// FatalLog helps in logging fatal errors.
func FatalLog(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.FatalDepth(1, logMessage)
}

// ErrorLogMsg helps in logging errors with message.
func ErrorLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.ErrorDepth(1, logMessage)
}

// ErrorLog helps in logging errors with context.
func ErrorLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	klog.ErrorDepth(1, logMessage)
}

// WarningLogMsg helps in logging warnings with message.
func WarningLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	klog.WarningDepth(1, logMessage)
}

// DefaultLog helps in logging with klog.level 1.
func DefaultLog(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Default).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// UsefulLog helps in logging with klog.level 2.
func UsefulLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Useful).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// ExtendedLogMsg helps in logging a message with klog.level 3.
func ExtendedLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Extended).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// ExtendedLog helps in logging with klog.level 3.
func ExtendedLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Extended).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// DebugLogMsg helps in logging a message with klog.level 4.
func DebugLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Debug).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// DebugLog helps in logging with klog.level 4.
func DebugLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Debug).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// TraceLogMsg helps in logging a message with klog.level 5.
func TraceLogMsg(message string, args ...interface{}) {
	logMessage := fmt.Sprintf(message, args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Trace).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}

// TraceLog helps in logging with klog.level 5.
func TraceLog(ctx context.Context, message string, args ...interface{}) {
	logMessage := fmt.Sprintf(Log(ctx, message), args...)
	// If logging is disabled, don't evaluate the arguments
	if klog.V(Trace).Enabled() {
		klog.InfoDepth(1, logMessage)
	}
}
<|endoftext|>


<commit_before>package seelog

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

// File and directory permitions.
const (
	defaultFilePermissions      = 0666
	defaultDirectoryPermissions = 0767
)

const (
	// Max number of directories can be read asynchronously.
	maxDirNumberReadAsync = 1000
)

type cannotOpenFileError struct {
	baseError
}

func newCannotOpenFileError(fname string) *cannotOpenFileError {
	return &cannotOpenFileError{baseError{message: "Cannot open file: " + fname}}
}

type notDirectoryError struct {
	baseError
}

func newNotDirectoryError(dname string) *notDirectoryError {
	return &notDirectoryError{baseError{message: dname + " is not directory"}}
}

// fileFilter is a filtering criteria function for '*os.File'.
// Must return 'false' to set aside the given file.
type fileFilter func(os.FileInfo, *os.File) bool

// filePathFilter is a filtering creteria function for file path.
// Must return 'false' to set aside the given file.
type filePathFilter func(filePath string) bool

// GetSubdirNames returns a list of directories found in
// the given one with dirPath.
func getSubdirNames(dirPath string) ([]string, error) {
	fi, err := os.Stat(dirPath)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, newNotDirectoryError(dirPath)
	}
	dd, err := os.Open(dirPath)
	// Cannot open file.
	if err != nil {
		if dd != nil {
			dd.Close()
		}
		return nil, err
	}
	defer dd.Close()
	// TODO: Improve performance by buffering reading.
	allEntities, err := dd.Readdir(-1)
	if err != nil {
		return nil, err
	}
	subDirs := []string{}
	for _, entity := range allEntities {
		if entity.IsDir() {
			subDirs = append(subDirs, entity.Name())
		}
	}
	return subDirs, nil
}

// getSubdirAbsPaths recursively visit all the subdirectories
// starting from the given directory and returns absolute paths for them.
func getAllSubdirAbsPaths(dirPath string) (res []string, err error) {
	dps, err := getSubdirAbsPaths(dirPath)
	if err != nil {
		res = []string{}
		return
	}
	res = append(res, dps...)
	for _, dp := range dps {
		sdps, err := getAllSubdirAbsPaths(dp)
		if err != nil {
			return []string{}, err
		}
		res = append(res, sdps...)
	}
	return
}

// getSubdirAbsPaths supplies absolute paths for all subdirectiries in a given directory.
// Input: (I1) dirPath - absolute path of a directory in question.
// Out: (O1) - slice of subdir asbolute paths; (O2) - error of the operation.
// Remark: If error (O2) is non-nil then (O1) is nil and vice versa.
func getSubdirAbsPaths(dirPath string) ([]string, error) {
	sdns, err := getSubdirNames(dirPath)
	if err != nil {
		return nil, err
	}
	rsdns := []string{}
	for _, sdn := range sdns {
		rsdns = append(rsdns, filepath.Join(dirPath, sdn))
	}
	return rsdns, nil
}

// getOpenFilesInDir supplies a slice of os.File pointers to files located in the directory.
// Remark: Ignores files for which fileFilter returns false
func getOpenFilesInDir(dirPath string, fFilter fileFilter) ([]*os.File, error) {
	dfi, err := os.Open(dirPath)
	if err != nil {
		return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
	}
	defer dfi.Close()
	// Size of read buffer (i.e. chunk of items read at a time).
	rbs := 64
	resFiles := []*os.File{}
L:
	for {
		// Read directory entities by reasonable chuncks
		// to prevent overflows on big number of files.
		fis, e := dfi.Readdir(rbs)
		switch e {
		// It's OK.
		case nil:
		// Do nothing, just continue cycle.
		case io.EOF:
			break L
		// Something went wrong.
		default:
			return nil, e
		}
		// THINK: Maybe, use async running.
		for _, fi := range fis {
			// NB: On Linux this could be a problem as
			// there are lots of file types available.
			if !fi.IsDir() {
				f, e := os.Open(filepath.Join(dirPath, fi.Name()))
				if e != nil {
					if f != nil {
						f.Close()
					}
					// THINK: Add nil as indicator that a problem occurred.
					resFiles = append(resFiles, nil)
					continue
				}
				// Check filter condition.
				if fFilter != nil && !fFilter(fi, f) {
					continue
				}
				resFiles = append(resFiles, f)
			}
		}
	}
	return resFiles, nil
}

func isRegular(m os.FileMode) bool {
	return m&os.ModeType == 0
}

// getDirFilePaths return full paths of the files located in the directory.
// Remark: Ignores files for which fileFilter returns false.
func getDirFilePaths(dirPath string, fpFilter filePathFilter, pathIsName bool) ([]string, error) {
	dfi, err := os.Open(dirPath)
	if err != nil {
		return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
	}
	defer dfi.Close()

	var absDirPath string
	if !filepath.IsAbs(dirPath) {
		absDirPath, err = filepath.Abs(dirPath)
		if err != nil {
			return nil, fmt.Errorf("Cannot get absolute path of directory: %s", err.Error())
		}
	} else {
		absDirPath = dirPath
	}

	// TODO: check if dirPath is really directory.
	// Size of read buffer (i.e. chunk of items read at a time).
	rbs := 2 << 5
	filePaths := []string{}

	var fp string
L:
	for {
		// Read directory entities by reasonable chuncks
		// to prevent overflows on big number of files.
		fis, e := dfi.Readdir(rbs)
		switch e {
		// It's OK.
		case nil:
		// Do nothing, just continue cycle.
		case io.EOF:
			break L
		// Indicate that something went wrong.
		default:
			return nil, e
		}
		// THINK: Maybe, use async running.
		for _, fi := range fis {
			// NB: Should work on every Windows and non-Windows OS.
			if isRegular(fi.Mode()) {
				if pathIsName {
					fp = fi.Name()
				} else {
					// Build full path of a file.
					fp = filepath.Join(absDirPath, fi.Name())
				}
				// Check filter condition.
				if fpFilter != nil && !fpFilter(fp) {
					continue
				}
				filePaths = append(filePaths, fp)
			}
		}
	}
	return filePaths, nil
}

// getOpenFilesByDirectoryAsync runs async reading directories 'dirPaths' and inserts pairs
// in map 'filesInDirMap': Key - directory name, value - *os.File slice.
func getOpenFilesByDirectoryAsync(
	dirPaths []string,
	fFilter fileFilter,
	filesInDirMap map[string][]*os.File,
) error {
	n := len(dirPaths)
	if n > maxDirNumberReadAsync {
		return fmt.Errorf("Number of input directories to be read exceeded max value %d", maxDirNumberReadAsync)
	}
	type filesInDirResult struct {
		DirName string
		Files   []*os.File
		Error   error
	}
	dirFilesChan := make(chan *filesInDirResult, n)
	var wg sync.WaitGroup
	// Register n goroutines which are going to do work.
	wg.Add(n)
	for i := 0; i < n; i++ {
		// Launch asynchronously the piece of work.
		go func(dirPath string) {
			fs, e := getOpenFilesInDir(dirPath, fFilter)
			dirFilesChan <- &filesInDirResult{filepath.Base(dirPath), fs, e}
			// Mark the current goroutine as finished (work is done).
			wg.Done()
		}(dirPaths[i])
	}
	// Wait for all goroutines to finish their work.
	wg.Wait()
	// Close the error channel to let for-range clause
	// get all the buffered values without blocking and quit in the end.
	close(dirFilesChan)
	for fidr := range dirFilesChan {
		if fidr.Error == nil {
			// THINK: What will happen if the key is already present?
			filesInDirMap[fidr.DirName] = fidr.Files
		} else {
			return fidr.Error
		}
	}
	return nil
}

func copyFile(sf *os.File, dst string) (int64, error) {
	df, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer df.Close()
	return io.Copy(df, sf)
}

// fileExists return flag whether a given file exists
// and operation error if an unclassified failure occurs.
func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		} else {
			return false, err
		}
	}
	return true, nil
}

// createDirectory makes directory with a given name
// making all parent directories if necessary.
func createDirectory(dirPath string) error {
	var dPath string
	var err error
	if !filepath.IsAbs(dirPath) {
		dPath, err = filepath.Abs(dirPath)
		if err != nil {
			return err
		}
	} else {
		dPath = dirPath
	}
	exists, err := fileExists(dPath)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	return os.MkdirAll(dPath, os.ModeDir)
}

// tryRemoveFile gives a try removing the file
// only ignoring an error when the file does not exist.
func tryRemoveFile(filePath string) (err error) {
	err = os.Remove(filePath)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	return
}

// Unzips a specified zip file. Returns filename->filebytes map.
func unzip(archiveName string) (map[string][]byte, error) {
	// Open a zip archive for reading.
	r, err := zip.OpenReader(archiveName)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	// Files to be added to archive
	// map file name to contents
	files := make(map[string][]byte)

	// Iterate through the files in the archive,
	// printing some of their contents.
	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return nil, err
		}

		bts, err := ioutil.ReadAll(rc)
		rcErr := rc.Close()

		if err != nil {
			return nil, err
		}
		if rcErr != nil {
			return nil, rcErr
		}

		files[f.Name] = bts
	}

	return files, nil
}

// Creates a zip file with the specified file names and byte contents.
func createZip(archiveName string, files map[string][]byte) error {
	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	// Create a new zip archive.
	w := zip.NewWriter(buf)

	// Write files
	for fpath, fcont := range files {
		f, err := w.Create(fpath)
		if err != nil {
			return err
		}
		_, err = f.Write([]byte(fcont))
		if err != nil {
			return err
		}
	}

	// Make sure to check the error on Close.
	err := w.Close()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(archiveName, buf.Bytes(), defaultFilePermissions)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Remove else after block with return statement.<commit_after>package seelog

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

// File and directory permitions.
const (
	defaultFilePermissions      = 0666
	defaultDirectoryPermissions = 0767
)

const (
	// Max number of directories can be read asynchronously.
	maxDirNumberReadAsync = 1000
)

type cannotOpenFileError struct {
	baseError
}

func newCannotOpenFileError(fname string) *cannotOpenFileError {
	return &cannotOpenFileError{baseError{message: "Cannot open file: " + fname}}
}

type notDirectoryError struct {
	baseError
}

func newNotDirectoryError(dname string) *notDirectoryError {
	return &notDirectoryError{baseError{message: dname + " is not directory"}}
}

// fileFilter is a filtering criteria function for '*os.File'.
// Must return 'false' to set aside the given file.
type fileFilter func(os.FileInfo, *os.File) bool

// filePathFilter is a filtering creteria function for file path.
// Must return 'false' to set aside the given file.
type filePathFilter func(filePath string) bool

// GetSubdirNames returns a list of directories found in
// the given one with dirPath.
func getSubdirNames(dirPath string) ([]string, error) {
	fi, err := os.Stat(dirPath)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, newNotDirectoryError(dirPath)
	}
	dd, err := os.Open(dirPath)
	// Cannot open file.
	if err != nil {
		if dd != nil {
			dd.Close()
		}
		return nil, err
	}
	defer dd.Close()
	// TODO: Improve performance by buffering reading.
	allEntities, err := dd.Readdir(-1)
	if err != nil {
		return nil, err
	}
	subDirs := []string{}
	for _, entity := range allEntities {
		if entity.IsDir() {
			subDirs = append(subDirs, entity.Name())
		}
	}
	return subDirs, nil
}

// getSubdirAbsPaths recursively visit all the subdirectories
// starting from the given directory and returns absolute paths for them.
func getAllSubdirAbsPaths(dirPath string) (res []string, err error) {
	dps, err := getSubdirAbsPaths(dirPath)
	if err != nil {
		res = []string{}
		return
	}
	res = append(res, dps...)
	for _, dp := range dps {
		sdps, err := getAllSubdirAbsPaths(dp)
		if err != nil {
			return []string{}, err
		}
		res = append(res, sdps...)
	}
	return
}

// getSubdirAbsPaths supplies absolute paths for all subdirectiries in a given directory.
// Input: (I1) dirPath - absolute path of a directory in question.
// Out: (O1) - slice of subdir asbolute paths; (O2) - error of the operation.
// Remark: If error (O2) is non-nil then (O1) is nil and vice versa.
func getSubdirAbsPaths(dirPath string) ([]string, error) {
	sdns, err := getSubdirNames(dirPath)
	if err != nil {
		return nil, err
	}
	rsdns := []string{}
	for _, sdn := range sdns {
		rsdns = append(rsdns, filepath.Join(dirPath, sdn))
	}
	return rsdns, nil
}

// getOpenFilesInDir supplies a slice of os.File pointers to files located in the directory.
// Remark: Ignores files for which fileFilter returns false
func getOpenFilesInDir(dirPath string, fFilter fileFilter) ([]*os.File, error) {
	dfi, err := os.Open(dirPath)
	if err != nil {
		return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
	}
	defer dfi.Close()
	// Size of read buffer (i.e. chunk of items read at a time).
	rbs := 64
	resFiles := []*os.File{}
L:
	for {
		// Read directory entities by reasonable chuncks
		// to prevent overflows on big number of files.
		fis, e := dfi.Readdir(rbs)
		switch e {
		// It's OK.
		case nil:
		// Do nothing, just continue cycle.
		case io.EOF:
			break L
		// Something went wrong.
		default:
			return nil, e
		}
		// THINK: Maybe, use async running.
		for _, fi := range fis {
			// NB: On Linux this could be a problem as
			// there are lots of file types available.
			if !fi.IsDir() {
				f, e := os.Open(filepath.Join(dirPath, fi.Name()))
				if e != nil {
					if f != nil {
						f.Close()
					}
					// THINK: Add nil as indicator that a problem occurred.
					resFiles = append(resFiles, nil)
					continue
				}
				// Check filter condition.
				if fFilter != nil && !fFilter(fi, f) {
					continue
				}
				resFiles = append(resFiles, f)
			}
		}
	}
	return resFiles, nil
}

func isRegular(m os.FileMode) bool {
	return m&os.ModeType == 0
}

// getDirFilePaths return full paths of the files located in the directory.
// Remark: Ignores files for which fileFilter returns false.
func getDirFilePaths(dirPath string, fpFilter filePathFilter, pathIsName bool) ([]string, error) {
	dfi, err := os.Open(dirPath)
	if err != nil {
		return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
	}
	defer dfi.Close()

	var absDirPath string
	if !filepath.IsAbs(dirPath) {
		absDirPath, err = filepath.Abs(dirPath)
		if err != nil {
			return nil, fmt.Errorf("Cannot get absolute path of directory: %s", err.Error())
		}
	} else {
		absDirPath = dirPath
	}

	// TODO: check if dirPath is really directory.
	// Size of read buffer (i.e. chunk of items read at a time).
	rbs := 2 << 5
	filePaths := []string{}

	var fp string
L:
	for {
		// Read directory entities by reasonable chuncks
		// to prevent overflows on big number of files.
		fis, e := dfi.Readdir(rbs)
		switch e {
		// It's OK.
		case nil:
		// Do nothing, just continue cycle.
		case io.EOF:
			break L
		// Indicate that something went wrong.
		default:
			return nil, e
		}
		// THINK: Maybe, use async running.
		for _, fi := range fis {
			// NB: Should work on every Windows and non-Windows OS.
			if isRegular(fi.Mode()) {
				if pathIsName {
					fp = fi.Name()
				} else {
					// Build full path of a file.
					fp = filepath.Join(absDirPath, fi.Name())
				}
				// Check filter condition.
				if fpFilter != nil && !fpFilter(fp) {
					continue
				}
				filePaths = append(filePaths, fp)
			}
		}
	}
	return filePaths, nil
}

// getOpenFilesByDirectoryAsync runs async reading directories 'dirPaths' and inserts pairs
// in map 'filesInDirMap': Key - directory name, value - *os.File slice.
func getOpenFilesByDirectoryAsync(
	dirPaths []string,
	fFilter fileFilter,
	filesInDirMap map[string][]*os.File,
) error {
	n := len(dirPaths)
	if n > maxDirNumberReadAsync {
		return fmt.Errorf("Number of input directories to be read exceeded max value %d", maxDirNumberReadAsync)
	}
	type filesInDirResult struct {
		DirName string
		Files   []*os.File
		Error   error
	}
	dirFilesChan := make(chan *filesInDirResult, n)
	var wg sync.WaitGroup
	// Register n goroutines which are going to do work.
	wg.Add(n)
	for i := 0; i < n; i++ {
		// Launch asynchronously the piece of work.
		go func(dirPath string) {
			fs, e := getOpenFilesInDir(dirPath, fFilter)
			dirFilesChan <- &filesInDirResult{filepath.Base(dirPath), fs, e}
			// Mark the current goroutine as finished (work is done).
			wg.Done()
		}(dirPaths[i])
	}
	// Wait for all goroutines to finish their work.
	wg.Wait()
	// Close the error channel to let for-range clause
	// get all the buffered values without blocking and quit in the end.
	close(dirFilesChan)
	for fidr := range dirFilesChan {
		if fidr.Error == nil {
			// THINK: What will happen if the key is already present?
			filesInDirMap[fidr.DirName] = fidr.Files
		} else {
			return fidr.Error
		}
	}
	return nil
}

func copyFile(sf *os.File, dst string) (int64, error) {
	df, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer df.Close()
	return io.Copy(df, sf)
}

// fileExists return flag whether a given file exists
// and operation error if an unclassified failure occurs.
func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// createDirectory makes directory with a given name
// making all parent directories if necessary.
func createDirectory(dirPath string) error {
	var dPath string
	var err error
	if !filepath.IsAbs(dirPath) {
		dPath, err = filepath.Abs(dirPath)
		if err != nil {
			return err
		}
	} else {
		dPath = dirPath
	}
	exists, err := fileExists(dPath)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	return os.MkdirAll(dPath, os.ModeDir)
}

// tryRemoveFile gives a try removing the file
// only ignoring an error when the file does not exist.
func tryRemoveFile(filePath string) (err error) {
	err = os.Remove(filePath)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	return
}

// Unzips a specified zip file. Returns filename->filebytes map.
func unzip(archiveName string) (map[string][]byte, error) {
	// Open a zip archive for reading.
	r, err := zip.OpenReader(archiveName)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	// Files to be added to archive
	// map file name to contents
	files := make(map[string][]byte)

	// Iterate through the files in the archive,
	// printing some of their contents.
	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return nil, err
		}

		bts, err := ioutil.ReadAll(rc)
		rcErr := rc.Close()

		if err != nil {
			return nil, err
		}
		if rcErr != nil {
			return nil, rcErr
		}

		files[f.Name] = bts
	}

	return files, nil
}

// Creates a zip file with the specified file names and byte contents.
func createZip(archiveName string, files map[string][]byte) error {
	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	// Create a new zip archive.
	w := zip.NewWriter(buf)

	// Write files
	for fpath, fcont := range files {
		f, err := w.Create(fpath)
		if err != nil {
			return err
		}
		_, err = f.Write([]byte(fcont))
		if err != nil {
			return err
		}
	}

	// Make sure to check the error on Close.
	err := w.Close()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(archiveName, buf.Bytes(), defaultFilePermissions)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package expr

import (
	"github.com/antonmedv/expr/checker"
	"github.com/antonmedv/expr/compiler"
	"github.com/antonmedv/expr/internal/conf"
	"github.com/antonmedv/expr/optimizer"
	"github.com/antonmedv/expr/parser"
	"github.com/antonmedv/expr/vm"
	"reflect"
)

// Eval parses, compiles and runs given input.
func Eval(input string, env interface{}) (interface{}, error) {
	tree, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}

	program, err := compiler.Compile(tree, nil)
	if err != nil {
		return nil, err
	}

	output, err := vm.Run(program, env)
	if err != nil {
		return nil, err
	}

	return output, nil
}

// Env specifies expected input of env for type checks.
// If struct is passed, all fields will be treated as variables,
// as well as all fields of embedded structs and struct itself.
// If map is passed, all items will be treated as variables.
// Methods defined on this type will be available as functions.
func Env(i interface{}) conf.Option {
	return func(c *conf.Config) {
		if _, ok := i.(map[string]interface{}); ok {
			c.MapEnv = true
		}
		c.Types = conf.CreateTypesTable(i)
	}
}

func Operator(operator string, fns ...string) conf.Option {
	return func(c *conf.Config) {
		c.Operators[operator] = append(c.Operators[operator], fns...)
	}
}

// AsBool tells the compiler to expect boolean result.
func AsBool() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Bool
	}
}

// AsInt64 tells the compiler to expect int64 result.
func AsInt64() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Int64
	}
}

// AsFloat64 tells the compiler to expect float64 result.
func AsFloat64() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Float64
	}
}

// Optimize turns optimizations on or off.
func Optimize(b bool) conf.Option {
	return func(c *conf.Config) {
		c.Optimize = b
	}
}

// Compile parses and compiles given input expression to bytecode program.
func Compile(input string, ops ...conf.Option) (*vm.Program, error) {
	config := &conf.Config{
		Operators: make(map[string][]string),
		Optimize:  true,
	}

	for _, op := range ops {
		op(config)
	}

	if err := config.Check(); err != nil {
		return nil, err
	}

	tree, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}

	if config.Types != nil {
		_, err = checker.Check(tree, config)
		if err != nil {
			return nil, err
		}
		checker.PatchOperators(tree, config)
	}

	if config.Optimize {
		optimizer.Optimize(&tree.Node)
	}

	program, err := compiler.Compile(tree, config)
	if err != nil {
		return nil, err
	}

	return program, nil
}

// Run evaluates given bytecode program.
func Run(program *vm.Program, env interface{}) (interface{}, error) {
	return vm.Run(program, env)
}
<commit_msg>Add Operator comment<commit_after>package expr

import (
	"github.com/antonmedv/expr/checker"
	"github.com/antonmedv/expr/compiler"
	"github.com/antonmedv/expr/internal/conf"
	"github.com/antonmedv/expr/optimizer"
	"github.com/antonmedv/expr/parser"
	"github.com/antonmedv/expr/vm"
	"reflect"
)

// Eval parses, compiles and runs given input.
func Eval(input string, env interface{}) (interface{}, error) {
	tree, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}

	program, err := compiler.Compile(tree, nil)
	if err != nil {
		return nil, err
	}

	output, err := vm.Run(program, env)
	if err != nil {
		return nil, err
	}

	return output, nil
}

// Env specifies expected input of env for type checks.
// If struct is passed, all fields will be treated as variables,
// as well as all fields of embedded structs and struct itself.
// If map is passed, all items will be treated as variables.
// Methods defined on this type will be available as functions.
func Env(i interface{}) conf.Option {
	return func(c *conf.Config) {
		if _, ok := i.(map[string]interface{}); ok {
			c.MapEnv = true
		}
		c.Types = conf.CreateTypesTable(i)
	}
}

// Operator allows to override binary operator with function.
func Operator(operator string, fn ...string) conf.Option {
	return func(c *conf.Config) {
		c.Operators[operator] = append(c.Operators[operator], fn...)
	}
}

// AsBool tells the compiler to expect boolean result.
func AsBool() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Bool
	}
}

// AsInt64 tells the compiler to expect int64 result.
func AsInt64() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Int64
	}
}

// AsFloat64 tells the compiler to expect float64 result.
func AsFloat64() conf.Option {
	return func(c *conf.Config) {
		c.Expect = reflect.Float64
	}
}

// Optimize turns optimizations on or off.
func Optimize(b bool) conf.Option {
	return func(c *conf.Config) {
		c.Optimize = b
	}
}

// Compile parses and compiles given input expression to bytecode program.
func Compile(input string, ops ...conf.Option) (*vm.Program, error) {
	config := &conf.Config{
		Operators: make(map[string][]string),
		Optimize:  true,
	}

	for _, op := range ops {
		op(config)
	}

	if err := config.Check(); err != nil {
		return nil, err
	}

	tree, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}

	if config.Types != nil {
		_, err = checker.Check(tree, config)
		if err != nil {
			return nil, err
		}
		checker.PatchOperators(tree, config)
	}

	if config.Optimize {
		optimizer.Optimize(&tree.Node)
	}

	program, err := compiler.Compile(tree, config)
	if err != nil {
		return nil, err
	}

	return program, nil
}

// Run evaluates given bytecode program.
func Run(program *vm.Program, env interface{}) (interface{}, error) {
	return vm.Run(program, env)
}
<|endoftext|>


<commit_before>package cli

import (
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	tuf "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-tuf/client"
	"github.com/flynn/flynn/pinkerton"
	"github.com/flynn/flynn/pkg/tufutil"
)

func init() {
	Register("download", runDownload, `
usage: flynn-host download [--driver=<name>] [--root=<path>] [--repository=<uri>] [--tuf-db=<path>] [--config-dir=<dir>] [--bin-dir=<dir>]

Options:
  -d --driver=<name>       image storage driver [default: aufs]
  -r --root=<path>         image storage root [default: /var/lib/docker]
  -u --repository=<uri>    TUF repository URI [default: https://dl.flynn.io/tuf]
  -t --tuf-db=<path>       local TUF file [default: /etc/flynn/tuf.db]
  -c --config-dir=<dir>    config directory [default: /etc/flynn]
  -b --bin-dir=<dir>       binary directory [default: /usr/local/bin]

Download container images and Flynn binaries from a TUF repository`)
}

func runDownload(args *docopt.Args) error {
	if err := os.MkdirAll(args.String["--root"], 0755); err != nil {
		return fmt.Errorf("error creating root dir: %s", err)
	}

	// create a TUF client, initialize it if the DB doesn't exist and update it
	tufDB := args.String["--tuf-db"]
	needsInit := false
	if _, err := os.Stat(tufDB); os.IsNotExist(err) {
		needsInit = true
	}
	local, err := tuf.FileLocalStore(tufDB)
	if err != nil {
		return err
	}
	remote, err := tuf.HTTPRemoteStore(args.String["--repository"], nil)
	if err != nil {
		return err
	}
	client := tuf.NewClient(local, remote)
	if needsInit {
		if err := client.Init(rootKeys, len(rootKeys)); err != nil {
			return err
		}
	}
	if _, err := client.Update(); err != nil && !tuf.IsLatestSnapshot(err) {
		return err
	}

	// pull images from the TUF repo
	if err := pinkerton.PullImagesWithClient(
		client,
		args.String["--repository"],
		args.String["--driver"],
		args.String["--root"],
		pinkerton.InfoPrinter(false),
	); err != nil {
		return err
	}

	// download the upstart config and image manifests
	if err := os.MkdirAll(args.String["--config-dir"], 0755); err != nil {
		return fmt.Errorf("error creating config dir: %s", err)
	}
	for _, path := range []string{"/upstart.conf", "/host-manifest.json", "/bootstrap-manifest.json"} {
		if _, err := downloadGzippedFile(client, path, args.String["--config-dir"]); err != nil {
			return err
		}
	}

	// download the init and cli binaries
	if err := os.MkdirAll(args.String["--bin-dir"], 0755); err != nil {
		return fmt.Errorf("error creating bin dir: %s", err)
	}
	for _, path := range []string{"/flynn-linux-amd64", "/flynn-init"} {
		dst, err := downloadGzippedFile(client, path, args.String["--bin-dir"])
		if err != nil {
			return err
		}
		if err := os.Chmod(dst, 0755); err != nil {
			return err
		}
	}
	return nil
}

func downloadGzippedFile(client *tuf.Client, path, dir string) (string, error) {
	file, err := tufutil.Download(client, path+".gz")
	if err != nil {
		return "", err
	}
	defer file.Close()
	dst := filepath.Join(dir, path)
	out, err := os.Create(dst)
	if err != nil {
		return "", err
	}
	gz, err := gzip.NewReader(file)
	if err != nil {
		return "", err
	}
	defer gz.Close()
	_, err = io.Copy(out, gz)
	return dst, err
}
<commit_msg>host: Fix initializing the TUF client<commit_after>package cli

import (
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	tuf "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-tuf/client"
	"github.com/flynn/flynn/pinkerton"
	"github.com/flynn/flynn/pkg/tufutil"
)

func init() {
	Register("download", runDownload, `
usage: flynn-host download [--driver=<name>] [--root=<path>] [--repository=<uri>] [--tuf-db=<path>] [--config-dir=<dir>] [--bin-dir=<dir>]

Options:
  -d --driver=<name>       image storage driver [default: aufs]
  -r --root=<path>         image storage root [default: /var/lib/docker]
  -u --repository=<uri>    TUF repository URI [default: https://dl.flynn.io/tuf]
  -t --tuf-db=<path>       local TUF file [default: /etc/flynn/tuf.db]
  -c --config-dir=<dir>    config directory [default: /etc/flynn]
  -b --bin-dir=<dir>       binary directory [default: /usr/local/bin]

Download container images and Flynn binaries from a TUF repository`)
}

func runDownload(args *docopt.Args) error {
	if err := os.MkdirAll(args.String["--root"], 0755); err != nil {
		return fmt.Errorf("error creating root dir: %s", err)
	}

	// create a TUF client and update it
	tufDB := args.String["--tuf-db"]
	local, err := tuf.FileLocalStore(tufDB)
	if err != nil {
		return err
	}
	remote, err := tuf.HTTPRemoteStore(args.String["--repository"], nil)
	if err != nil {
		return err
	}
	client := tuf.NewClient(local, remote)
	if err := updateTUFClient(client); err != nil {
		return err
	}

	// pull images from the TUF repo
	if err := pinkerton.PullImagesWithClient(
		client,
		args.String["--repository"],
		args.String["--driver"],
		args.String["--root"],
		pinkerton.InfoPrinter(false),
	); err != nil {
		return err
	}

	// download the upstart config and image manifests
	if err := os.MkdirAll(args.String["--config-dir"], 0755); err != nil {
		return fmt.Errorf("error creating config dir: %s", err)
	}
	for _, path := range []string{"/upstart.conf", "/host-manifest.json", "/bootstrap-manifest.json"} {
		if _, err := downloadGzippedFile(client, path, args.String["--config-dir"]); err != nil {
			return err
		}
	}

	// download the init and cli binaries
	if err := os.MkdirAll(args.String["--bin-dir"], 0755); err != nil {
		return fmt.Errorf("error creating bin dir: %s", err)
	}
	for _, path := range []string{"/flynn-linux-amd64", "/flynn-init"} {
		dst, err := downloadGzippedFile(client, path, args.String["--bin-dir"])
		if err != nil {
			return err
		}
		if err := os.Chmod(dst, 0755); err != nil {
			return err
		}
	}
	return nil
}

func downloadGzippedFile(client *tuf.Client, path, dir string) (string, error) {
	file, err := tufutil.Download(client, path+".gz")
	if err != nil {
		return "", err
	}
	defer file.Close()
	dst := filepath.Join(dir, path)
	out, err := os.Create(dst)
	if err != nil {
		return "", err
	}
	gz, err := gzip.NewReader(file)
	if err != nil {
		return "", err
	}
	defer gz.Close()
	_, err = io.Copy(out, gz)
	return dst, err
}

// updateTUFClient updates the given client, initializing and re-running the
// update if ErrNoRootKeys is returned.
func updateTUFClient(client *tuf.Client) error {
	_, err := client.Update()
	if err == nil || tuf.IsLatestSnapshot(err) {
		return nil
	}
	if err == tuf.ErrNoRootKeys {
		if err := client.Init(rootKeys, len(rootKeys)); err != nil {
			return err
		}
		return updateTUFClient(client)
	}
	return err
}
<|endoftext|>


<commit_before>package apiserver

import (
	"encoding/json"
	"net"
	"net/http"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/Dataman-Cloud/swan/src/manager/apiserver/metrics"

	"github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	API_VERSION = "v_beta"
)

// RootPaths lists the paths available at root.
// For example: "/healthz", "/apis".
type RootPaths struct {
	// paths are the paths available at root.
	Paths []string
}

type ApiRegister interface {
	Register(*restful.Container)
}

type ApiServer struct {
	addr         string
	sock         string
	apiRegisters []ApiRegister
}

func init() {
	metrics.Register()
}

func NewApiServer(addr, sock string) *ApiServer {
	return &ApiServer{
		addr: addr,
		sock: sock,
	}
}

func Install(apiServer *ApiServer, apiRegister ApiRegister) {
	apiServer.apiRegisters = append(apiServer.apiRegisters, apiRegister)
}

func (apiServer *ApiServer) Start() error {
	wsContainer := restful.NewContainer()

	// Register webservices here
	for _, ws := range apiServer.apiRegisters {
		ws.Register(wsContainer)
	}

	// Add container filter to enable CORS
	cors := restful.CrossOriginResourceSharing{
		AllowedMethods: []string{"GET", "POST", "PUT", "PATCH"},
		CookiesAllowed: false,
		Container:      wsContainer}
	wsContainer.Filter(cors.Filter)

	// Add log filter
	wsContainer.Filter(NCSACommonLogFormatLogger())

	// Add prometheus metrics
	wsContainer.Handle("/metrics", promhttp.Handler())

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
	// TODO(xychu): add a config flag for swagger UI, and also for the swagger UI file path.
	swggerUiPath, _ := filepath.Abs("./third_party/swagger-ui-2.2.8")
	logrus.Debugf("xychu:  swaggerUIPath: %s", swggerUiPath)
	config := swagger.Config{
		WebServices: wsContainer.RegisteredWebServices(), // you control what services are visible
		// WebServicesUrl: "",
		ApiVersion: API_VERSION,
		ApiPath:    "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/apidocs/",
		SwaggerFilePath: swggerUiPath,
	}
	swagger.RegisterSwaggerService(config, wsContainer)

	// Add API index handler
	wsContainer.ServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		status := http.StatusOK
		if r.URL.Path != "/" && r.URL.Path != "/index.html" {
			// Since "/" matches all paths, handleIndex is called for all paths for which there is no handler registered.
			// We want to return a 404 status with a list of all valid paths, incase of an invalid URL request.
			status = http.StatusNotFound
			w.WriteHeader(status)
			return
		}
		var handledPaths []string
		// Extract the paths handled using restful.WebService
		for _, ws := range wsContainer.RegisteredWebServices() {
			handledPaths = append(handledPaths, ws.RootPath())
		}
		// Extract the paths handled using mux handler.
		handledPaths = append(handledPaths, "/metrics", "/apidocs/")
		sort.Strings(handledPaths)

		output, err := json.MarshalIndent(RootPaths{Paths: handledPaths}, "", "  ")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		w.Write(output)
	})

	go func() {
		srv := &http.Server{
			Addr:    apiServer.sock,
			Handler: wsContainer,
		}
		ln, err := net.Listen("unix", apiServer.sock)
		if err != nil {
			logrus.Errorf("can't listen on socket %s:%s", apiServer.sock, err.Error())
		}
		logrus.Printf("start listening on %s", apiServer.sock)
		srv.Serve(ln)
	}()

	logrus.Printf("start listening on %s", apiServer.addr)
	server := &http.Server{Addr: apiServer.addr, Handler: wsContainer}
	logrus.Fatal(server.ListenAndServe())

	return nil
}

func NCSACommonLogFormatLogger() restful.FilterFunction {
	return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
		var username = "-"
		if req.Request.URL.User != nil {
			if name := req.Request.URL.User.Username(); name != "" {
				username = name
			}
		}
		chain.ProcessFilter(req, resp)
		logrus.Printf("%s - %s [%s] \"%s %s %s\" %d %d",
			strings.Split(req.Request.RemoteAddr, ":")[0],
			username,
			time.Now().Format("02/Jan/2006:15:04:05 -0700"),
			req.Request.Method,
			req.Request.URL.RequestURI(),
			req.Request.Proto,
			resp.StatusCode(),
			resp.ContentLength(),
		)
	}
}
<commit_msg>remove sock file before listen sock file<commit_after>package apiserver

import (
	"encoding/json"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/Dataman-Cloud/swan/src/manager/apiserver/metrics"

	"github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	API_VERSION = "v_beta"
)

// RootPaths lists the paths available at root.
// For example: "/healthz", "/apis".
type RootPaths struct {
	// paths are the paths available at root.
	Paths []string
}

type ApiRegister interface {
	Register(*restful.Container)
}

type ApiServer struct {
	addr         string
	sock         string
	apiRegisters []ApiRegister
}

func init() {
	metrics.Register()
}

func NewApiServer(addr, sock string) *ApiServer {
	return &ApiServer{
		addr: addr,
		sock: sock,
	}
}

func Install(apiServer *ApiServer, apiRegister ApiRegister) {
	apiServer.apiRegisters = append(apiServer.apiRegisters, apiRegister)
}

func (apiServer *ApiServer) Start() error {
	wsContainer := restful.NewContainer()

	// Register webservices here
	for _, ws := range apiServer.apiRegisters {
		ws.Register(wsContainer)
	}

	// Add container filter to enable CORS
	cors := restful.CrossOriginResourceSharing{
		AllowedMethods: []string{"GET", "POST", "PUT", "PATCH"},
		CookiesAllowed: false,
		Container:      wsContainer}
	wsContainer.Filter(cors.Filter)

	// Add log filter
	wsContainer.Filter(NCSACommonLogFormatLogger())

	// Add prometheus metrics
	wsContainer.Handle("/metrics", promhttp.Handler())

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
	// TODO(xychu): add a config flag for swagger UI, and also for the swagger UI file path.
	swggerUiPath, _ := filepath.Abs("./third_party/swagger-ui-2.2.8")
	logrus.Debugf("xychu:  swaggerUIPath: %s", swggerUiPath)
	config := swagger.Config{
		WebServices: wsContainer.RegisteredWebServices(), // you control what services are visible
		// WebServicesUrl: "",
		ApiVersion: API_VERSION,
		ApiPath:    "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/apidocs/",
		SwaggerFilePath: swggerUiPath,
	}
	swagger.RegisterSwaggerService(config, wsContainer)

	// Add API index handler
	wsContainer.ServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		status := http.StatusOK
		if r.URL.Path != "/" && r.URL.Path != "/index.html" {
			// Since "/" matches all paths, handleIndex is called for all paths for which there is no handler registered.
			// We want to return a 404 status with a list of all valid paths, incase of an invalid URL request.
			status = http.StatusNotFound
			w.WriteHeader(status)
			return
		}
		var handledPaths []string
		// Extract the paths handled using restful.WebService
		for _, ws := range wsContainer.RegisteredWebServices() {
			handledPaths = append(handledPaths, ws.RootPath())
		}
		// Extract the paths handled using mux handler.
		handledPaths = append(handledPaths, "/metrics", "/apidocs/")
		sort.Strings(handledPaths)

		output, err := json.MarshalIndent(RootPaths{Paths: handledPaths}, "", "  ")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		w.Write(output)
	})

	go func() {
		if _, err := os.Stat(apiServer.sock); err == nil {
			// remove the sock file if it's already existed
			if err := os.Remove(apiServer.sock); err != nil {
				logrus.Errorf("can't listen on socket %s remove existed file failed. Error: %s", apiServer.sock, err.Error())
			}
		}
		srv := &http.Server{
			Addr:    apiServer.sock,
			Handler: wsContainer,
		}
		ln, err := net.Listen("unix", apiServer.sock)
		if err != nil {
			logrus.Errorf("can't listen on socket %s:%s", apiServer.sock, err.Error())
		}
		logrus.Printf("start listening on %s", apiServer.sock)
		srv.Serve(ln)
	}()

	logrus.Printf("start listening on %s", apiServer.addr)
	server := &http.Server{Addr: apiServer.addr, Handler: wsContainer}
	logrus.Fatal(server.ListenAndServe())

	return nil
}

func NCSACommonLogFormatLogger() restful.FilterFunction {
	return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
		var username = "-"
		if req.Request.URL.User != nil {
			if name := req.Request.URL.User.Username(); name != "" {
				username = name
			}
		}
		chain.ProcessFilter(req, resp)
		logrus.Printf("%s - %s [%s] \"%s %s %s\" %d %d",
			strings.Split(req.Request.RemoteAddr, ":")[0],
			username,
			time.Now().Format("02/Jan/2006:15:04:05 -0700"),
			req.Request.Method,
			req.Request.URL.RequestURI(),
			req.Request.Proto,
			resp.StatusCode(),
			resp.ContentLength(),
		)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metrics

import (
	"net/http"
	"strconv"
	"sync"

	prom "contrib.go.opencensus.io/exporter/prometheus"
	"go.opencensus.io/resource"
	"go.opencensus.io/stats/view"
	"go.uber.org/zap"
)

var (
	curPromSrv    *http.Server
	curPromSrvMux sync.Mutex
)

type emptyPromExporter struct{}

var _ view.Exporter = (*emptyPromExporter)(nil)

func (emptyPromExporter) ExportView(viewData *view.Data) {
	// Prometheus runs a loop to read stats via ReadAndExport, so this is just
	// a signal to enrich the internal Meters with Resource information.
}

func newPrometheusExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.Exporter, ResourceExporterFactory, error) {
	e, err := prom.NewExporter(prom.Options{Namespace: config.component})
	if err != nil {
		logger.Errorw("Failed to create the Prometheus exporter.", zap.Error(err))
		return nil, nil, err
	}
	logger.Infof("Created Opencensus Prometheus exporter with config: %v. Start the server for Prometheus exporter.", config)
	// Start the server for Prometheus scraping
	go func() {
		srv := startNewPromSrv(e, config.prometheusHost, config.prometheusPort)
		srv.ListenAndServe()
	}()
	return e,
		func(r *resource.Resource) (view.Exporter, error) { return &emptyPromExporter{}, nil },
		nil
}

func getCurPromSrv() *http.Server {
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	return curPromSrv
}

func resetCurPromSrv() {
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	if curPromSrv != nil {
		curPromSrv.Close()
		curPromSrv = nil
	}
}

func startNewPromSrv(e *prom.Exporter, host string, port int) *http.Server {
	sm := http.NewServeMux()
	sm.Handle("/metrics", e)
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	if curPromSrv != nil {
		curPromSrv.Close()
	}
	curPromSrv = &http.Server{
		Addr:    host + ":" + strconv.Itoa(port),
		Handler: sm,
	}
	return curPromSrv
}
<commit_msg>Add nolint to false positive being flagged (#1897)<commit_after>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metrics

import (
	"net/http"
	"strconv"
	"sync"

	prom "contrib.go.opencensus.io/exporter/prometheus"
	"go.opencensus.io/resource"
	"go.opencensus.io/stats/view"
	"go.uber.org/zap"
)

var (
	curPromSrv    *http.Server
	curPromSrvMux sync.Mutex
)

type emptyPromExporter struct{}

var _ view.Exporter = (*emptyPromExporter)(nil)

func (emptyPromExporter) ExportView(viewData *view.Data) {
	// Prometheus runs a loop to read stats via ReadAndExport, so this is just
	// a signal to enrich the internal Meters with Resource information.
}

//nolint: unparam // False positive of flagging the second result of this function unused.
func newPrometheusExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.Exporter, ResourceExporterFactory, error) {
	e, err := prom.NewExporter(prom.Options{Namespace: config.component})
	if err != nil {
		logger.Errorw("Failed to create the Prometheus exporter.", zap.Error(err))
		return nil, nil, err
	}
	logger.Infof("Created Opencensus Prometheus exporter with config: %v. Start the server for Prometheus exporter.", config)
	// Start the server for Prometheus scraping
	go func() {
		srv := startNewPromSrv(e, config.prometheusHost, config.prometheusPort)
		srv.ListenAndServe()
	}()
	return e,
		func(r *resource.Resource) (view.Exporter, error) { return &emptyPromExporter{}, nil },
		nil
}

func getCurPromSrv() *http.Server {
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	return curPromSrv
}

func resetCurPromSrv() {
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	if curPromSrv != nil {
		curPromSrv.Close()
		curPromSrv = nil
	}
}

func startNewPromSrv(e *prom.Exporter, host string, port int) *http.Server {
	sm := http.NewServeMux()
	sm.Handle("/metrics", e)
	curPromSrvMux.Lock()
	defer curPromSrvMux.Unlock()
	if curPromSrv != nil {
		curPromSrv.Close()
	}
	curPromSrv = &http.Server{
		Addr:    host + ":" + strconv.Itoa(port),
		Handler: sm,
	}
	return curPromSrv
}
<|endoftext|>


<commit_before>package publishers

import (
	"database/sql"
	"fmt"
	"os"
	"strings"

	"github.com/fweikert/continuous-integration/metrics/metrics"

	"github.com/fweikert/continuous-integration/metrics/data"
	_ "github.com/go-sql-driver/mysql"
)

const insertTemplate = "INSERT INTO %s (%s) VALUES (%s)"

type CloudSql struct {
	conn       *sql.DB
	statements map[string]*sql.Stmt
}

func (c *CloudSql) Name() string {
	return "Cloud SQL"
}

func (c *CloudSql) RegisterMetric(metric metrics.Metric) error {
	err := c.ensureTableExists(metric)
	if err != nil {
		return err
	}
	return c.prepareInsertStatement(metric)
}

func (c *CloudSql) ensureTableExists(metric metrics.Metric) error {
	// TODO(fweikert): implement
	return nil
}

func (c *CloudSql) prepareInsertStatement(metric metrics.Metric) error {
	name := metric.Name()
	if _, ok := c.statements[name]; ok {
		return fmt.Errorf("Metrics %s has already been registered for publisher %s.", name, c.Name())
	}

	columnNames := metrics.GetColumnNames(metric.Columns())
	placeholder := strings.TrimRight(strings.Repeat("?, ", len(columnNames)), ", ")
	insert := fmt.Sprintf(insertTemplate, name, strings.Join(columnNames, ", "), placeholder)

	nonKeyColumnNames := make([]string, 0)
	for _, c := range metric.Columns() {
		if !c.IsKey {
			nonKeyColumnNames = append(nonKeyColumnNames, c.Name)
		}
	}
	if len(nonKeyColumnNames) != len(columnNames) {
		updates := make([]string, len(nonKeyColumnNames))
		for i, c := range nonKeyColumnNames {
			updates[i] = fmt.Sprintf("%s=VALUES(%s)", c, c)
		}
		insert = fmt.Sprintf("%s ON DUPLICATE KEY UPDATE %s", insert, strings.Join(updates, ", "))
	}

	stmt, err := c.conn.Prepare(insert)
	if err != nil {
		return fmt.Errorf("Failed to prepare insert statement for metric %s: %v", name, err)
	}
	c.statements[name] = stmt
	return nil
}

func (c *CloudSql) Publish(metricName string, newData *data.DataSet) error {
	stmt := c.statements[metricName]
	if stmt == nil {
		return fmt.Errorf("Could not find prepared insert statement for metric %s. Have you called RegisterMetric() first?", metricName)
	}

	for _, row := range newData.Data {
		_, err := stmt.Exec(row...)
		if err != nil {
			return fmt.Errorf("Could not insert new data for metric %s: %v", metricName, err)
		}
	}
	return nil
}

func CreateCloudSqlPublisher(user, password, instance, database string, localPort int) (*CloudSql, error) {
	conn, err := sql.Open("mysql", getConnectionString(user, password, instance, database, localPort))
	if err != nil {
		return nil, fmt.Errorf("Could not establish connection to database: %v", err)
	}
	if err := conn.Ping(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("Connection to database is bad: %v", err)
	}
	return &CloudSql{
		conn:       conn,
		statements: make(map[string]*sql.Stmt),
	}, nil
}

func getConnectionString(user, password, instance, database string, localPort int) string {
	cred := fmt.Sprintf("%s:%s@", user, password)
	if os.Getenv("GAE_INSTANCE") != "" {
		// Running in production.
		return fmt.Sprintf("%sunix(/cloudsql/%s)/%s", cred, instance, database)
	}
	return fmt.Sprintf("%stcp([localhost]:%d)/%s", cred, localPort, database)
}
<commit_msg>Improve SQL logging.<commit_after>package publishers

import (
	"database/sql"
	"fmt"
	"os"
	"strings"

	"github.com/fweikert/continuous-integration/metrics/metrics"

	"github.com/fweikert/continuous-integration/metrics/data"
	_ "github.com/go-sql-driver/mysql"
)

const insertTemplate = "INSERT INTO %s (%s) VALUES (%s)"

type statement struct {
	prepared *sql.Stmt
	text     string
}

type CloudSql struct {
	conn       *sql.DB
	statements map[string]*statement
}

func (c *CloudSql) Name() string {
	return "Cloud SQL"
}

func (c *CloudSql) RegisterMetric(metric metrics.Metric) error {
	err := c.ensureTableExists(metric)
	if err != nil {
		return err
	}
	return c.prepareInsertStatement(metric)
}

func (c *CloudSql) ensureTableExists(metric metrics.Metric) error {
	// TODO(fweikert): implement
	return nil
}

func (c *CloudSql) prepareInsertStatement(metric metrics.Metric) error {
	name := metric.Name()
	if _, ok := c.statements[name]; ok {
		return fmt.Errorf("Metrics %s has already been registered for publisher %s.", name, c.Name())
	}

	columnNames := metrics.GetColumnNames(metric.Columns())
	placeholder := strings.TrimRight(strings.Repeat("?, ", len(columnNames)), ", ")
	insert := fmt.Sprintf(insertTemplate, name, strings.Join(columnNames, ", "), placeholder)

	nonKeyColumnNames := make([]string, 0)
	for _, c := range metric.Columns() {
		if !c.IsKey {
			nonKeyColumnNames = append(nonKeyColumnNames, c.Name)
		}
	}
	if len(nonKeyColumnNames) != len(columnNames) {
		updates := make([]string, len(nonKeyColumnNames))
		for i, c := range nonKeyColumnNames {
			updates[i] = fmt.Sprintf("%s=VALUES(%s)", c, c)
		}
		insert = fmt.Sprintf("%s ON DUPLICATE KEY UPDATE %s", insert, strings.Join(updates, ", "))
	}

	stmt, err := c.createStatement(name, insert)
	if err != nil {
		return err
	}
	c.statements[name] = stmt
	return nil
}

func (c *CloudSql) createStatement(metricName string, text string) (*statement, error) {
	stmt, err := c.conn.Prepare(text)
	if err != nil {
		return nil, fmt.Errorf("Failed to prepare insert statement for metric %s: %v\n\tStatement: %s", metricName, err, text)
	}
	return &statement{prepared: stmt, text: text}, nil
}

func (c *CloudSql) Publish(metricName string, newData *data.DataSet) error {
	stmt := c.statements[metricName]
	if stmt == nil {
		return fmt.Errorf("Could not find prepared insert statement for metric %s. Have you called RegisterMetric() first?", metricName)
	}

	for _, row := range newData.Data {
		_, err := stmt.prepared.Exec(row...)
		if err != nil {
			values := make([]string, len(row))
			for i, v := range row {
				values[i] = fmt.Sprintf("%v", v)
			}
			return fmt.Errorf("Could not insert new data for metric %s: %v\n\tStatement: %s\n\tValues: %s", metricName, err, stmt.text, strings.Join(values, ", "))
		}
	}
	return nil
}

func CreateCloudSqlPublisher(user, password, instance, database string, localPort int) (*CloudSql, error) {
	conn, err := sql.Open("mysql", getConnectionString(user, password, instance, database, localPort))
	if err != nil {
		return nil, fmt.Errorf("Could not establish connection to database: %v", err)
	}
	if err := conn.Ping(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("Connection to database is bad: %v", err)
	}
	return &CloudSql{
		conn:       conn,
		statements: make(map[string]*statement),
	}, nil
}

func getConnectionString(user, password, instance, database string, localPort int) string {
	cred := fmt.Sprintf("%s:%s@", user, password)
	if os.Getenv("GAE_INSTANCE") != "" {
		// Running in production.
		return fmt.Sprintf("%sunix(/cloudsql/%s)/%s", cred, instance, database)
	}
	return fmt.Sprintf("%stcp([localhost]:%d)/%s", cred, localPort, database)
}
<|endoftext|>