<commit_before>package main

import (
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"

	"github.com/agl/ed25519"
	docopt "github.com/docopt/docopt-go"
	"github.com/keybase/client/go/chat/signencrypt"
)

func fail(args ...interface{}) {
	log.Print(fmt.Sprintln(args...))
	os.Exit(1)
}

func decodeHexArg(arg string) []byte {
	decoded, err := hex.DecodeString(arg)
	if err != nil {
		fail("'%s' is not valid hex: %s", arg, err)
	}
	return decoded
}

func zeroSecretboxKey() signencrypt.SecretboxKey {
	var key [signencrypt.SecretboxKeySize]byte // all zeroes
	return &key
}

func zeroNonce() signencrypt.Nonce {
	var nonce [signencrypt.NonceSize]byte // all zeroes
	return &nonce
}

func zeroVerifyKey() signencrypt.VerifyKey {
	var key [ed25519.PublicKeySize]byte
	// Generated from libsodium's crypto_sign_seed_keypair with a zero seed.
	copy(key[:], ";j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)")
	return &key
}

func zeroSignKey() signencrypt.SignKey {
	var key [ed25519.PrivateKeySize]byte
	// Generated from libsodium's crypto_sign_seed_keypair with a zero seed.
	copy(key[:], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)")
	return &key
}

func seal(enckey signencrypt.SecretboxKey, signkey signencrypt.SignKey, nonce signencrypt.Nonce, chunklen int) error {
	encoder := signencrypt.NewEncoder(enckey, signkey, nonce)
	if chunklen != 0 {
		encoder.ChangePlaintextChunkLenForTesting(chunklen)
	}
	var buf [4096]byte
	for {
		num, err := os.Stdin.Read(buf[:])
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		encoded := encoder.Write(buf[0:num])
		_, err = os.Stdout.Write(encoded)
		if err != nil {
			return err
		}
	}
	encoded := encoder.Finish()
	_, err := os.Stdout.Write(encoded)
	if err != nil {
		return err
	}
	return nil
}

func open(enckey signencrypt.SecretboxKey, verifykey signencrypt.VerifyKey, nonce signencrypt.Nonce, chunklen int) error {
	decoder := signencrypt.NewDecoder(enckey, verifykey, nonce)
	if chunklen != 0 {
		decoder.ChangePlaintextChunkLenForTesting(chunklen)
	}
	var buf [4096]byte
	for {
		num, err := os.Stdin.Read(buf[:])
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		decoded, err := decoder.Write(buf[0:num])
		if err != nil {
			return err
		}
		_, err = os.Stdout.Write(decoded)
		if err != nil {
			return err
		}
	}
	decoded, err := decoder.Finish()
	if err != nil {
		return err
	}
	_, err = os.Stdout.Write(decoded)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	usage := `Usage:
    example seal [--enckey=<enckey>] [--signkey=<signkey>] [--nonce=<nonce>] [--chunklen=<chunklen>]
    example open [--enckey=<enckey>] [--verifykey=<signkey>] [--nonce=<nonce>] [--chunklen=<chunklen>]

Options:
    --enckey=<enckey>        the 32-byte encryption key (in hex)
    --signkey=<signkey>      the 64-byte signing private key (in hex)
    --verifykey=<verifykey>  the 32-byte signing public  key (in hex)
    --nonce=<nonce>          the 16-byte nonce
    --chunklen=<chunklen>    the size of plaintext chunks, for testing, default 2^20 bytes
`
	arguments, _ := docopt.Parse(usage, nil, true, "signencrypt crypto example", false)

	enckey := zeroSecretboxKey()
	if arguments["--enckey"] != nil {
		copy(enckey[:], decodeHexArg(arguments["--enckey"].(string)))
	}

	signkey := zeroSignKey()
	if arguments["--signkey"] != nil {
		copy(signkey[:], decodeHexArg(arguments["--signkey"].(string)))
	}

	verifykey := zeroVerifyKey()
	if arguments["--verifykey"] != nil {
		copy(verifykey[:], decodeHexArg(arguments["--verifykey"].(string)))
	}

	nonce := zeroNonce()
	if arguments["--nonce"] != nil {
		copy(nonce[:], decodeHexArg(arguments["--nonce"].(string)))
	}

	chunklen := 0
	if arguments["--chunklen"] != nil {
		parsed, err := strconv.Atoi(arguments["--chunklen"].(string))
		if err != nil {
			fail(err)
		}
		chunklen = parsed
	}

	var err error
	if arguments["seal"].(bool) {
		err = seal(enckey, signkey, nonce, chunklen)
	} else {
		err = open(enckey, verifykey, nonce, chunklen)
	}
	if err != nil {
		fail(err)
	}
}
<commit_msg>fix example<commit_after>package main

import (
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"

	"github.com/agl/ed25519"
	docopt "github.com/docopt/docopt-go"
	"github.com/keybase/client/go/chat/signencrypt"
	"github.com/keybase/client/go/libkb"
)

func fail(args ...interface{}) {
	log.Print(fmt.Sprintln(args...))
	os.Exit(1)
}

func decodeHexArg(arg string) []byte {
	decoded, err := hex.DecodeString(arg)
	if err != nil {
		fail("'%s' is not valid hex: %s", arg, err)
	}
	return decoded
}

func zeroSecretboxKey() signencrypt.SecretboxKey {
	var key [signencrypt.SecretboxKeySize]byte // all zeroes
	return &key
}

func zeroNonce() signencrypt.Nonce {
	var nonce [signencrypt.NonceSize]byte // all zeroes
	return &nonce
}

func zeroVerifyKey() signencrypt.VerifyKey {
	var key [ed25519.PublicKeySize]byte
	// Generated from libsodium's crypto_sign_seed_keypair with a zero seed.
	copy(key[:], ";j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)")
	return &key
}

func zeroSignKey() signencrypt.SignKey {
	var key [ed25519.PrivateKeySize]byte
	// Generated from libsodium's crypto_sign_seed_keypair with a zero seed.
	copy(key[:], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)")
	return &key
}

func seal(enckey signencrypt.SecretboxKey, signkey signencrypt.SignKey, signaturePrefix libkb.SignaturePrefix, nonce signencrypt.Nonce, chunklen int) error {
	encoder := signencrypt.NewEncoder(enckey, signkey, signaturePrefix, nonce)
	if chunklen != 0 {
		encoder.ChangePlaintextChunkLenForTesting(chunklen)
	}
	var buf [4096]byte
	for {
		num, err := os.Stdin.Read(buf[:])
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		encoded := encoder.Write(buf[0:num])
		_, err = os.Stdout.Write(encoded)
		if err != nil {
			return err
		}
	}
	encoded := encoder.Finish()
	_, err := os.Stdout.Write(encoded)
	if err != nil {
		return err
	}
	return nil
}

func open(enckey signencrypt.SecretboxKey, verifykey signencrypt.VerifyKey, signaturePrefix libkb.SignaturePrefix, nonce signencrypt.Nonce, chunklen int) error {
	decoder := signencrypt.NewDecoder(enckey, verifykey, signaturePrefix, nonce)
	if chunklen != 0 {
		decoder.ChangePlaintextChunkLenForTesting(chunklen)
	}
	var buf [4096]byte
	for {
		num, err := os.Stdin.Read(buf[:])
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		decoded, err := decoder.Write(buf[0:num])
		if err != nil {
			return err
		}
		_, err = os.Stdout.Write(decoded)
		if err != nil {
			return err
		}
	}
	decoded, err := decoder.Finish()
	if err != nil {
		return err
	}
	_, err = os.Stdout.Write(decoded)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	usage := `Usage:
    example seal [--enckey=<enckey>] [--signkey=<signkey>] [--sigprefix=<sigprefix>] [--nonce=<nonce>] [--chunklen=<chunklen>]
    example open [--enckey=<enckey>] [--verifykey=<signkey>] [--sigprefix=<sigprefix>] [--nonce=<nonce>] [--chunklen=<chunklen>]

Options:
    --enckey=<enckey>        the 32-byte encryption key (in hex)
    --signkey=<signkey>      the 64-byte signing private key (in hex)
    --verifykey=<verifykey>  the 32-byte signing public  key (in hex)
    --sigprefix=<sigprefix>  the signature prefix (string)
    --nonce=<nonce>          the 16-byte nonce
    --chunklen=<chunklen>    the size of plaintext chunks, for testing, default 2^20 bytes
`
	arguments, _ := docopt.Parse(usage, nil, true, "signencrypt crypto example", false)

	enckey := zeroSecretboxKey()
	if arguments["--enckey"] != nil {
		copy(enckey[:], decodeHexArg(arguments["--enckey"].(string)))
	}

	signkey := zeroSignKey()
	if arguments["--signkey"] != nil {
		copy(signkey[:], decodeHexArg(arguments["--signkey"].(string)))
	}

	verifykey := zeroVerifyKey()
	if arguments["--verifykey"] != nil {
		copy(verifykey[:], decodeHexArg(arguments["--verifykey"].(string)))
	}

	signaturePrefix := libkb.SignaturePrefixTesting
	if arguments["--sigprefix"] != nil {
		signaturePrefixStr := arguments["--sigprefix"].(string)
		signaturePrefix = libkb.SignaturePrefix(signaturePrefixStr)
	}

	nonce := zeroNonce()
	if arguments["--nonce"] != nil {
		copy(nonce[:], decodeHexArg(arguments["--nonce"].(string)))
	}

	chunklen := 0
	if arguments["--chunklen"] != nil {
		parsed, err := strconv.Atoi(arguments["--chunklen"].(string))
		if err != nil {
			fail(err)
		}
		chunklen = parsed
	}

	var err error
	if arguments["seal"].(bool) {
		err = seal(enckey, signkey, signaturePrefix, nonce, chunklen)
	} else {
		err = open(enckey, verifykey, signaturePrefix, nonce, chunklen)
	}
	if err != nil {
		fail(err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

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

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

Unless required by applicable law or agreedto 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 vtgate

import (
	"flag"
	"fmt"
	"net"

	log "github.com/golang/glog"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/mysql"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/callerid"
	"github.com/youtube/vitess/go/vt/servenv"

	querypb "github.com/youtube/vitess/go/vt/proto/query"
	vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
	"github.com/youtube/vitess/go/vt/servenv/grpcutils"
)

var (
	mysqlServerPort               = flag.Int("mysql_server_port", -1, "If set, also listen for MySQL binary protocol connections on this port.")
	mysqlAuthServerImpl           = flag.String("mysql_auth_server_impl", "static", "Which auth server implementation to use.")
	mysqlAllowClearTextWithoutTLS = flag.Bool("mysql_allow_clear_text_without_tls", false, "If set, the server will allow the use of a clear text password over non-SSL connections.")

	mysqlSslCert = flag.String("mysql_server_ssl_cert", "", "Path to the ssl cert for mysql server plugin SSL")
	mysqlSslKey  = flag.String("mysql_server_ssl_key", "", "Path to ssl key for mysql server plugin SSL")
	mysqlSslCa   = flag.String("mysql_server_ssl_ca", "", "Path to ssl CA for mysql server plugin SSL. If specified, server will require and validate client certs.")

	mysqlSlowConnectWarnThreshold = flag.Duration("mysql_slow_connect_warn_threshold", 0, "Warn if it takes more than the given threshold for a mysql connection to establish")
)

// vtgateHandler implements the Listener interface.
// It stores the Session in the ClientData of a Connection, if a transaction
// is in progress.
type vtgateHandler struct {
	vtg *VTGate
}

func newVtgateHandler(vtg *VTGate) *vtgateHandler {
	return &vtgateHandler{
		vtg: vtg,
	}
}

func (vh *vtgateHandler) NewConnection(c *mysql.Conn) {
}

func (vh *vtgateHandler) ConnectionClosed(c *mysql.Conn) {
	// Rollback if there is an ongoing transaction. Ignore error.
	ctx := context.Background()
	session, _ := c.ClientData.(*vtgatepb.Session)
	if session != nil {
		_, _, _ = vh.vtg.Execute(ctx, session, "rollback", make(map[string]interface{}))
	}
}

func (vh *vtgateHandler) ComQuery(c *mysql.Conn, query []byte, callback func(*sqltypes.Result) error) error {
	// FIXME(alainjobart): Add some kind of timeout to the context.
	ctx := context.Background()

	// Fill in the ImmediateCallerID with the UserData returned by
	// the AuthServer plugin for that user. If nothing was
	// returned, use the User. This lets the plugin map a MySQL
	// user used for authentication to a Vitess User used for
	// Table ACLs and Vitess authentication in general.
	im := c.UserData.Get()
	ef := callerid.NewEffectiveCallerID(
		c.User,                  /* principal: who */
		c.RemoteAddr().String(), /* component: running client process */
		"VTGate MySQL Connector" /* subcomponent: part of the client */)
	ctx = callerid.NewContext(ctx, ef, im)

	session, _ := c.ClientData.(*vtgatepb.Session)
	if session == nil {
		session = &vtgatepb.Session{
			Options: &querypb.ExecuteOptions{
				IncludedFields: querypb.ExecuteOptions_ALL,
			},
		}
		if c.Capabilities&mysql.CapabilityClientFoundRows != 0 {
			session.Options.ClientFoundRows = true
		}
	}
	if c.SchemaName != "" {
		session.TargetString = c.SchemaName
	}
	if session.Options.Workload == querypb.ExecuteOptions_OLAP {
		err := vh.vtg.StreamExecute(ctx, session, string(query), make(map[string]interface{}), callback)
		return mysql.NewSQLErrorFromError(err)
	}
	session, result, err := vh.vtg.Execute(ctx, session, string(query), make(map[string]interface{}))
	c.ClientData = session
	err = mysql.NewSQLErrorFromError(err)
	if err != nil {
		return err
	}
	return callback(result)
}

var mysqlListener *mysql.Listener

// initiMySQLProtocol starts the mysql protocol.
// It should be called only once in a process.
func initMySQLProtocol() {
	// Flag is not set, just return.
	if *mysqlServerPort < 0 {
		return
	}

	// If no VTGate was created, just return.
	if rpcVTGate == nil {
		return
	}

	// Initialize registered AuthServer implementations (or other plugins)
	for _, initFn := range pluginInitializers {
		initFn()
	}
	authServer := mysql.GetAuthServer(*mysqlAuthServerImpl)

	// Create a Listener.
	var err error
	vh := newVtgateHandler(rpcVTGate)
	mysqlListener, err = mysql.NewListener("tcp", net.JoinHostPort("", fmt.Sprintf("%v", *mysqlServerPort)), authServer, vh)
	if err != nil {
		log.Fatalf("mysql.NewListener failed: %v", err)
	}
	if *mysqlSslCert != "" && *mysqlSslKey != "" {
		mysqlListener.TLSConfig, err = grpcutils.TLSServerConfig(*mysqlSslCert, *mysqlSslKey, *mysqlSslCa)
		if err != nil {
			log.Fatalf("grpcutils.TLSServerConfig failed: %v", err)
			return
		}
	}
	mysqlListener.AllowClearTextWithoutTLS = *mysqlAllowClearTextWithoutTLS

	// Check for the connection threshold
	if mysqlSlowConnectWarnThreshold.Nanoseconds() != 0 {
		log.Infof("setting mysql slow connection threshold to %v", mysqlSlowConnectWarnThreshold)
		mysqlListener.SlowConnectWarnThreshold = *mysqlSlowConnectWarnThreshold
	}

	// And starts listening.
	go func() {
		mysqlListener.Accept()
	}()
}

func init() {
	servenv.OnRun(initMySQLProtocol)

	servenv.OnTerm(func() {
		if mysqlListener != nil {
			mysqlListener.Close()
			mysqlListener = nil
		}
	})
}

var pluginInitializers []func()

// RegisterPluginInitializer lets plugins register themselves to be init'ed at servenv.OnRun-time
func RegisterPluginInitializer(initializer func()) {
	pluginInitializers = append(pluginInitializers, initializer)
}
<commit_msg>use simpler threshold comparison as per PR feedback<commit_after>/*
Copyright 2017 Google Inc.

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

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

Unless required by applicable law or agreedto 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 vtgate

import (
	"flag"
	"fmt"
	"net"

	log "github.com/golang/glog"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/mysql"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/callerid"
	"github.com/youtube/vitess/go/vt/servenv"

	querypb "github.com/youtube/vitess/go/vt/proto/query"
	vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
	"github.com/youtube/vitess/go/vt/servenv/grpcutils"
)

var (
	mysqlServerPort               = flag.Int("mysql_server_port", -1, "If set, also listen for MySQL binary protocol connections on this port.")
	mysqlAuthServerImpl           = flag.String("mysql_auth_server_impl", "static", "Which auth server implementation to use.")
	mysqlAllowClearTextWithoutTLS = flag.Bool("mysql_allow_clear_text_without_tls", false, "If set, the server will allow the use of a clear text password over non-SSL connections.")

	mysqlSslCert = flag.String("mysql_server_ssl_cert", "", "Path to the ssl cert for mysql server plugin SSL")
	mysqlSslKey  = flag.String("mysql_server_ssl_key", "", "Path to ssl key for mysql server plugin SSL")
	mysqlSslCa   = flag.String("mysql_server_ssl_ca", "", "Path to ssl CA for mysql server plugin SSL. If specified, server will require and validate client certs.")

	mysqlSlowConnectWarnThreshold = flag.Duration("mysql_slow_connect_warn_threshold", 0, "Warn if it takes more than the given threshold for a mysql connection to establish")
)

// vtgateHandler implements the Listener interface.
// It stores the Session in the ClientData of a Connection, if a transaction
// is in progress.
type vtgateHandler struct {
	vtg *VTGate
}

func newVtgateHandler(vtg *VTGate) *vtgateHandler {
	return &vtgateHandler{
		vtg: vtg,
	}
}

func (vh *vtgateHandler) NewConnection(c *mysql.Conn) {
}

func (vh *vtgateHandler) ConnectionClosed(c *mysql.Conn) {
	// Rollback if there is an ongoing transaction. Ignore error.
	ctx := context.Background()
	session, _ := c.ClientData.(*vtgatepb.Session)
	if session != nil {
		_, _, _ = vh.vtg.Execute(ctx, session, "rollback", make(map[string]interface{}))
	}
}

func (vh *vtgateHandler) ComQuery(c *mysql.Conn, query []byte, callback func(*sqltypes.Result) error) error {
	// FIXME(alainjobart): Add some kind of timeout to the context.
	ctx := context.Background()

	// Fill in the ImmediateCallerID with the UserData returned by
	// the AuthServer plugin for that user. If nothing was
	// returned, use the User. This lets the plugin map a MySQL
	// user used for authentication to a Vitess User used for
	// Table ACLs and Vitess authentication in general.
	im := c.UserData.Get()
	ef := callerid.NewEffectiveCallerID(
		c.User,                  /* principal: who */
		c.RemoteAddr().String(), /* component: running client process */
		"VTGate MySQL Connector" /* subcomponent: part of the client */)
	ctx = callerid.NewContext(ctx, ef, im)

	session, _ := c.ClientData.(*vtgatepb.Session)
	if session == nil {
		session = &vtgatepb.Session{
			Options: &querypb.ExecuteOptions{
				IncludedFields: querypb.ExecuteOptions_ALL,
			},
		}
		if c.Capabilities&mysql.CapabilityClientFoundRows != 0 {
			session.Options.ClientFoundRows = true
		}
	}
	if c.SchemaName != "" {
		session.TargetString = c.SchemaName
	}
	if session.Options.Workload == querypb.ExecuteOptions_OLAP {
		err := vh.vtg.StreamExecute(ctx, session, string(query), make(map[string]interface{}), callback)
		return mysql.NewSQLErrorFromError(err)
	}
	session, result, err := vh.vtg.Execute(ctx, session, string(query), make(map[string]interface{}))
	c.ClientData = session
	err = mysql.NewSQLErrorFromError(err)
	if err != nil {
		return err
	}
	return callback(result)
}

var mysqlListener *mysql.Listener

// initiMySQLProtocol starts the mysql protocol.
// It should be called only once in a process.
func initMySQLProtocol() {
	// Flag is not set, just return.
	if *mysqlServerPort < 0 {
		return
	}

	// If no VTGate was created, just return.
	if rpcVTGate == nil {
		return
	}

	// Initialize registered AuthServer implementations (or other plugins)
	for _, initFn := range pluginInitializers {
		initFn()
	}
	authServer := mysql.GetAuthServer(*mysqlAuthServerImpl)

	// Create a Listener.
	var err error
	vh := newVtgateHandler(rpcVTGate)
	mysqlListener, err = mysql.NewListener("tcp", net.JoinHostPort("", fmt.Sprintf("%v", *mysqlServerPort)), authServer, vh)
	if err != nil {
		log.Fatalf("mysql.NewListener failed: %v", err)
	}
	if *mysqlSslCert != "" && *mysqlSslKey != "" {
		mysqlListener.TLSConfig, err = grpcutils.TLSServerConfig(*mysqlSslCert, *mysqlSslKey, *mysqlSslCa)
		if err != nil {
			log.Fatalf("grpcutils.TLSServerConfig failed: %v", err)
			return
		}
	}
	mysqlListener.AllowClearTextWithoutTLS = *mysqlAllowClearTextWithoutTLS

	// Check for the connection threshold
	if mysqlSlowConnectWarnThreshold != 0 {
		log.Infof("setting mysql slow connection threshold to %v", mysqlSlowConnectWarnThreshold)
		mysqlListener.SlowConnectWarnThreshold = *mysqlSlowConnectWarnThreshold
	}

	// And starts listening.
	go func() {
		mysqlListener.Accept()
	}()
}

func init() {
	servenv.OnRun(initMySQLProtocol)

	servenv.OnTerm(func() {
		if mysqlListener != nil {
			mysqlListener.Close()
			mysqlListener = nil
		}
	})
}

var pluginInitializers []func()

// RegisterPluginInitializer lets plugins register themselves to be init'ed at servenv.OnRun-time
func RegisterPluginInitializer(initializer func()) {
	pluginInitializers = append(pluginInitializers, initializer)
}
<|endoftext|>


<commit_before>package common

import(
    dt "github.com/elastest/elastest-monitoring-service/go_EMS/datatypes"
    striverdt "gitlab.software.imdea.org/felipe.gorostiaga/striver-go/datatypes"
    "fmt"
    "errors"
//    "strconv"
)

type StreamExprVisitor interface {
	VisitAggregatorExpr(AggregatorExpr)
	VisitIfThenExpr(IfThenExpr)
	VisitIfThenElseExpr(IfThenElseExpr)
	// VisittringExpr(StringExpr)
	VisitStreamNumExpr(StreamNumExpr)
	VisitPredExpr(PredExpr)
	VisitStringPathExpr(StringPathExpr)
	VisitStreamNameExpr(StreamNameExpr)
}

type StreamExpr interface {
	// add functions here
	Sprint() string
	Accept (StreamExprVisitor)
}

type StreamNumExpr struct {
    Expr NumExpr
}
func (this StreamNumExpr) Accept(visitor StreamExprVisitor) {
	visitor.VisitStreamNumExpr(this)
}
func (this StreamNumExpr) Sprint() string {
	str := this.Expr.Sprint()
	return str
}

type AggregatorExpr struct {
	Operation string
	Stream    striverdt.StreamName //StreamName
	Session   striverdt.StreamName //StreamName
}
func (a AggregatorExpr) Sprint() string {
	return "FIXME"
}

func (this AggregatorExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitAggregatorExpr(this)
}

type IfThenExpr struct {
	If   Predicate
	Then StreamExpr
}
func (this IfThenExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitIfThenExpr(this)
}
func (this IfThenExpr) Sprint() string {
	return fmt.Sprintf("if %s then %s",this.If.Sprint(),this.Then.Sprint())
}

type IfThenElseExpr struct {
	If   Predicate
	Then StreamExpr
	Else StreamExpr
}

func (this IfThenElseExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitIfThenElseExpr(this)
}
func (a IfThenElseExpr) Sprint() string {
	return fmt.Sprintf("if %s then %s else %s",a.If.Sprint(),a.Then.Sprint(),a.Else.Sprint())
}
// Is this ever used?
//type StringExpr struct {
//	Path string// so far only e.get(path) claiming to return a string
//}

type PredExpr struct {
	Pred Predicate
}

func (this PredExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitPredExpr(this)
}
func (this PredExpr) Sprint() string {
	return this.Pred.Sprint()
}

type StreamNameExpr struct {
	Stream string
}
func (this StreamNameExpr) Accept(visitor StreamExprVisitor) {
	visitor.VisitStreamNameExpr(this)
}
func (this StreamNameExpr) Sprint() string {
	return string(this.Stream)
}

type StringPathExpr struct {
	Path []dt.JSONPath
}

func (this StringPathExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitStringPathExpr(this)
}

func (this StringPathExpr) Sprint() string {
	return fmt.Sprintf("e.getstr(%s)",this.Path)
}

//
// Expression Node constructors
//
func NewStreamNumExpr(n interface{}) StreamNumExpr {
	return StreamNumExpr{n.(NumExpr)}
}
func NewAggregatorExpr(op, str, ses interface{}) AggregatorExpr {
	operation := op.(string)
	stream    := str.(Identifier).Val
	session   := ses.(Identifier).Val

	return AggregatorExpr{operation,striverdt.StreamName(stream),striverdt.StreamName(session)}
}

func NewIfThenExpr(p,e interface{}) IfThenExpr {
	if_part   := p.(Predicate)
	then_part := e.(StreamExpr)
	return IfThenExpr{if_part,then_part}
}
func NewIfThenElseExpr(p,a,b interface{}) IfThenElseExpr {
	if_part   := p.(Predicate)
	then_part := a.(StreamExpr)
	else_part := b.(StreamExpr)
	return IfThenElseExpr{if_part, then_part, else_part}
}
func NewPredExpr(p interface{}) PredExpr {
	return PredExpr{p.(Predicate)}
}

func NewStringPathExpr(je, p interface{}) StringPathExpr {
	path := p.(PathName).Val
	return StringPathExpr{append(je.(JSONExpr).Paths, dt.JSONPath(path))}
}

func NewStreamNameExpr(p interface{}) StreamNameExpr {
	return StreamNameExpr{p.(Identifier).Val}
}

func NewJSONExpr(suffixes interface{}) (JSONExpr, error) {
  isuffixes := suffixes.([]interface{})
  var paths []dt.JSONPath
  if len(isuffixes)==1 {
    paths = append(paths, dt.JSONPath(isuffixes[0].(PathName).Val))
  }
  if len(isuffixes)>1 {
	    return JSONExpr{paths},errors.New("Only one level of JSON embedding is supported")
  }
  return JSONExpr{paths}, nil
}
<commit_msg>Embedded getJSON (#181)<commit_after>package common

import(
    dt "github.com/elastest/elastest-monitoring-service/go_EMS/datatypes"
    striverdt "gitlab.software.imdea.org/felipe.gorostiaga/striver-go/datatypes"
    "fmt"
//    "strconv"
)

type StreamExprVisitor interface {
	VisitAggregatorExpr(AggregatorExpr)
	VisitIfThenExpr(IfThenExpr)
	VisitIfThenElseExpr(IfThenElseExpr)
	// VisittringExpr(StringExpr)
	VisitStreamNumExpr(StreamNumExpr)
	VisitPredExpr(PredExpr)
	VisitStringPathExpr(StringPathExpr)
	VisitStreamNameExpr(StreamNameExpr)
}

type StreamExpr interface {
	// add functions here
	Sprint() string
	Accept (StreamExprVisitor)
}

type StreamNumExpr struct {
    Expr NumExpr
}
func (this StreamNumExpr) Accept(visitor StreamExprVisitor) {
	visitor.VisitStreamNumExpr(this)
}
func (this StreamNumExpr) Sprint() string {
	str := this.Expr.Sprint()
	return str
}

type AggregatorExpr struct {
	Operation string
	Stream    striverdt.StreamName //StreamName
	Session   striverdt.StreamName //StreamName
}
func (a AggregatorExpr) Sprint() string {
	return "FIXME"
}

func (this AggregatorExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitAggregatorExpr(this)
}

type IfThenExpr struct {
	If   Predicate
	Then StreamExpr
}
func (this IfThenExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitIfThenExpr(this)
}
func (this IfThenExpr) Sprint() string {
	return fmt.Sprintf("if %s then %s",this.If.Sprint(),this.Then.Sprint())
}

type IfThenElseExpr struct {
	If   Predicate
	Then StreamExpr
	Else StreamExpr
}

func (this IfThenElseExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitIfThenElseExpr(this)
}
func (a IfThenElseExpr) Sprint() string {
	return fmt.Sprintf("if %s then %s else %s",a.If.Sprint(),a.Then.Sprint(),a.Else.Sprint())
}
// Is this ever used?
//type StringExpr struct {
//	Path string// so far only e.get(path) claiming to return a string
//}

type PredExpr struct {
	Pred Predicate
}

func (this PredExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitPredExpr(this)
}
func (this PredExpr) Sprint() string {
	return this.Pred.Sprint()
}

type StreamNameExpr struct {
	Stream string
}
func (this StreamNameExpr) Accept(visitor StreamExprVisitor) {
	visitor.VisitStreamNameExpr(this)
}
func (this StreamNameExpr) Sprint() string {
	return string(this.Stream)
}

type StringPathExpr struct {
	Path []dt.JSONPath
}

func (this StringPathExpr) Accept(visitor StreamExprVisitor) {
    visitor.VisitStringPathExpr(this)
}

func (this StringPathExpr) Sprint() string {
	return fmt.Sprintf("e.getstr(%s)",this.Path)
}

//
// Expression Node constructors
//
func NewStreamNumExpr(n interface{}) StreamNumExpr {
	return StreamNumExpr{n.(NumExpr)}
}
func NewAggregatorExpr(op, str, ses interface{}) AggregatorExpr {
	operation := op.(string)
	stream    := str.(Identifier).Val
	session   := ses.(Identifier).Val

	return AggregatorExpr{operation,striverdt.StreamName(stream),striverdt.StreamName(session)}
}

func NewIfThenExpr(p,e interface{}) IfThenExpr {
	if_part   := p.(Predicate)
	then_part := e.(StreamExpr)
	return IfThenExpr{if_part,then_part}
}
func NewIfThenElseExpr(p,a,b interface{}) IfThenElseExpr {
	if_part   := p.(Predicate)
	then_part := a.(StreamExpr)
	else_part := b.(StreamExpr)
	return IfThenElseExpr{if_part, then_part, else_part}
}
func NewPredExpr(p interface{}) PredExpr {
	return PredExpr{p.(Predicate)}
}

func NewStringPathExpr(je, p interface{}) StringPathExpr {
	path := p.(PathName).Val
	return StringPathExpr{append(je.(JSONExpr).Paths, dt.JSONPath(path))}
}

func NewStreamNameExpr(p interface{}) StreamNameExpr {
	return StreamNameExpr{p.(Identifier).Val}
}

func NewJSONExpr(suffixes interface{}) (JSONExpr, error) {
  isuffixes := suffixes.([]interface{})
  paths := make([]dt.JSONPath, len(isuffixes))
  for i, is := range isuffixes {
    paths[i] =dt.JSONPath(is.(PathName).Val)
  }
  return JSONExpr{paths}, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced 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 web

import (
	"github.com/control-center/serviced/domain"
)

//profile defines meta-data for the host/pool resource's metrics and graphs
var (
	zero       int = 0
	onehundred int = 100

	zeroInt64 int64 = 0

	hostPoolProfile = domain.MonitorProfile{
		MetricConfigs: []domain.MetricConfig{
			//CPU
			domain.MetricConfig{
				ID:          "cpu",
				Name:        "CPU Usage",
				Description: "CPU Statistics",
				Metrics: []domain.Metric{
					domain.Metric{ID: "cpu.user", Name: "CPU User", Unit: "Percent"},
					domain.Metric{ID: "cpu.nice", Name: "CPU Nice", Unit: "Percent"},
					domain.Metric{ID: "cpu.system", Name: "CPU System", Unit: "Percent"},
					domain.Metric{ID: "cpu.idle", Name: "CPU Idle", Unit: "Percent"},
					domain.Metric{ID: "cpu.iowait", Name: "CPU IO Wait", Unit: "Percent"},
					domain.Metric{ID: "cpu.irq", Name: "IRQ", Unit: "Percent"},
					domain.Metric{ID: "cpu.softirq", Name: "Soft IRQ", Unit: "Percent"},
					domain.Metric{ID: "cpu.steal", Name: "CPU Steal", Unit: "Percent"},
				},
			},
			//Memory
			domain.MetricConfig{
				ID:          "memory",
				Name:        "Memory Usage",
				Description: "Memory Usage Statistics -- /proc/meminfo",
				Metrics: []domain.Metric{
					domain.Metric{ID: "memory.buffers", Name: "Memory Buffer", Unit: "Bytes"},
					domain.Metric{ID: "memory.cached", Name: "Memory Cache", Unit: "Bytes"},
					domain.Metric{ID: "memory.free", Name: "Memory Free", Unit: "Bytes"},
					domain.Metric{ID: "memory.total", Name: "Total Memory", Unit: "Bytes"},
					domain.Metric{ID: "memory.actualfree", Name: "Actual Free Memory", Unit: "Bytes"},
					domain.Metric{ID: "memory.actualused", Name: "Actual Used Memory", Unit: "Bytes"},
					domain.Metric{ID: "swap.total", Name: "Total Swap", Unit: "Bytes"},
					domain.Metric{ID: "swap.free", Name: "Free Swap", Unit: "Bytes"},
				},
			},
			//Virtual Memory
			domain.MetricConfig{
				ID:          "virtual.memory",
				Name:        "Virtual Memory Usage",
				Description: "Virtual Memory Usage Statistics -- /proc/vmstat",
				Metrics: []domain.Metric{
					domain.Metric{ID: "vmstat.pgfault", Name: "Minor Page Fault", Unit: "Page Faults"},
					domain.Metric{ID: "vmstat.pgmajfault", Name: "Major Page Fault", Unit: "Page Faults"},
				},
			},
			//Files
			domain.MetricConfig{
				ID:          "files",
				Name:        "File Usage",
				Description: "File Statistics",
				Metrics: []domain.Metric{
					domain.Metric{ID: "Serviced.OpenFileDescriptors", Name: "OpenFileDescriptors", Unit: "Open File Descriptors"},
				},
			},
		},
		ThresholdConfigs: []domain.ThresholdConfig{
			domain.ThresholdConfig{
				ID:           "swap.empty",
				Name:         "Swap empty",
				Description:  "Alert when swap reaches zero",
				MetricSource: "memory",
				DataPoints:   []string{"swap.free", "memory.free"},
				Type:         "MinMax",
				Threshold:    domain.MinMaxThreshold{Min: &zeroInt64, Max: nil},
				EventTags: map[string]interface{}{
					"Severity":    1,
					"Resolution":  "Increase swap or memory",
					"Explanation": "Ran out of all available memory space",
					"EventClass":  "/Perf/Memory",
				},
			},
		},
	}
)

//Open File Descriptors
func newOpenFileDescriptorsGraph(tags map[string][]string) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				ID:           "ofd",
				Aggregator:   "avg",
				Color:        "#aec7e8",
				Fill:         false,
				Format:       "%6.2f",
				Legend:       "Serviced Open File Descriptors",
				Metric:       "Serviced.OpenFileDescriptors",
				MetricSource: "files",
				Name:         "Serviced Open File Descriptors",
				Rate:         false,
				Type:         "line",
			},
		},
		ID:     "serviced.ofd",
		Name:   "Serviced Open File Descriptors",
		Footer: false,
		Format: "%d",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		ReturnSet:   "EXACT",
		Type:        "line",
		Tags:        tags,
		Description: "Graph of serviced's total open file descriptors over time",
	}
}

//Major Page Faults
func newMajorPageFaultGraph(tags map[string][]string) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				ID:           "pgfault",
				Color:        "#aec7e8",
				Fill:         false,
				Format:       "%.2f",
				Legend:       "Major Page Faults",
				Metric:       "vmstat.pgmajfault",
				MetricSource: "virtual.memory",
				Name:         "Major Page Faults",
				Rate:         true,
				Type:         "line",
			},
		},
		ID:     "memory.major.pagefault",
		Name:   "Memory Major Page Faults",
		Footer: false,
		Format: "%.2f",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "Faults / Min",
		ReturnSet:   "EXACT",
		Type:        "line",
		Tags:        tags,
		Description: "Graph of major memory page faults over time",
	}
}

//Cpu Usage
func newCpuConfigGraph(tags map[string][]string, totalCores int) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#729ed7",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "nice",
				Legend:       "Nice",
				Metric:       "cpu.nice",
				MetricSource: "cpu",
				Name:         "Nice",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#aee8cf",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "user",
				Legend:       "User",
				Metric:       "cpu.user",
				MetricSource: "cpu",
				Name:         "User",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#eaf0f9",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "idle",
				Legend:       "Idle",
				Metric:       "cpu.idle",
				MetricSource: "cpu",
				Name:         "Idle",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#d7729e",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "system",
				Legend:       "System",
				Metric:       "cpu.system",
				MetricSource: "cpu",
				Name:         "System",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#e8aec7",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "iowait",
				Legend:       "IOWait",
				Metric:       "cpu.iowait",
				MetricSource: "cpu",
				Name:         "IOWait",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#e8cfae",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "irq",
				Legend:       "IRQ",
				Metric:       "cpu.irq",
				MetricSource: "cpu",
				Name:         "IRQ",
				Rate:         true,
				Type:         "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#ff0000",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "steal",
				Legend:       "Steal",
				Metric:       "cpu.steal",
				MetricSource: "cpu",
				Name:         "Steal",
				Rate:         true,
				Type:         "area",
			},
		},
		ID:     "cpu.usage",
		Name:   "CPU Usage",
		Footer: false,
		Format: "%d",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "% Used",
		ReturnSet:   "EXACT",
		Type:        "area",
		Tags:        tags,
		Description: "Graph of system and user cpu usage over time",
	}
}

func newRSSConfigGraph(tags map[string][]string, totalMemory uint64) domain.GraphConfig {
	MaxY := int(totalMemory / 1024 / 1024 / 1024)
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#e8aec7",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Used",
				Metric:       "memory.actualused",
				MetricSource: "memory",
				Name:         "Used",
				Type:         "area",
				ID:           "used",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#b2aee8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Cached",
				Metric:       "memory.cached",
				MetricSource: "memory",
				Name:         "Cached",
				Type:         "area",
				ID:           "cached",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#aec7e8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Buffers",
				Metric:       "memory.buffers",
				MetricSource: "memory",
				Name:         "Buffers",
				Type:         "area",
				ID:           "buffers",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#aee4e8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Free",
				Metric:       "memory.free",
				MetricSource: "memory",
				Name:         "Free",
				Type:         "area",
				ID:           "free",
			},
		},
		ID:     "memory.usage",
		Name:   "Memory Usage",
		Footer: false,
		Format: "%6.2f",
		MaxY:   &MaxY,
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "GB",
		ReturnSet:   "EXACT",
		Type:        "area",
		Tags:        tags,
		Description: "Graph of memory free (-buffers/+cache) vs used (total - free) over time",
	}
}
<commit_msg>fix rateOptions for various metrics<commit_after>// Copyright 2014 The Serviced 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 web

import (
	"github.com/control-center/serviced/domain"
)

//profile defines meta-data for the host/pool resource's metrics and graphs
var (
	zero       int = 0
	onehundred int = 100

	zeroInt64 int64 = 0

	hostPoolProfile = domain.MonitorProfile{
		MetricConfigs: []domain.MetricConfig{
			//CPU
			domain.MetricConfig{
				ID:          "cpu",
				Name:        "CPU Usage",
				Description: "CPU Statistics",
				Metrics: []domain.Metric{
					domain.Metric{ID: "cpu.user", Name: "CPU User", Unit: "Percent"},
					domain.Metric{ID: "cpu.nice", Name: "CPU Nice", Unit: "Percent"},
					domain.Metric{ID: "cpu.system", Name: "CPU System", Unit: "Percent"},
					domain.Metric{ID: "cpu.idle", Name: "CPU Idle", Unit: "Percent"},
					domain.Metric{ID: "cpu.iowait", Name: "CPU IO Wait", Unit: "Percent"},
					domain.Metric{ID: "cpu.irq", Name: "IRQ", Unit: "Percent"},
					domain.Metric{ID: "cpu.softirq", Name: "Soft IRQ", Unit: "Percent"},
					domain.Metric{ID: "cpu.steal", Name: "CPU Steal", Unit: "Percent"},
				},
			},
			//Memory
			domain.MetricConfig{
				ID:          "memory",
				Name:        "Memory Usage",
				Description: "Memory Usage Statistics -- /proc/meminfo",
				Metrics: []domain.Metric{
					domain.Metric{ID: "memory.buffers", Name: "Memory Buffer", Unit: "Bytes"},
					domain.Metric{ID: "memory.cached", Name: "Memory Cache", Unit: "Bytes"},
					domain.Metric{ID: "memory.free", Name: "Memory Free", Unit: "Bytes"},
					domain.Metric{ID: "memory.total", Name: "Total Memory", Unit: "Bytes"},
					domain.Metric{ID: "memory.actualfree", Name: "Actual Free Memory", Unit: "Bytes"},
					domain.Metric{ID: "memory.actualused", Name: "Actual Used Memory", Unit: "Bytes"},
					domain.Metric{ID: "swap.total", Name: "Total Swap", Unit: "Bytes"},
					domain.Metric{ID: "swap.free", Name: "Free Swap", Unit: "Bytes"},
				},
			},
			//Virtual Memory
			domain.MetricConfig{
				ID:          "virtual.memory",
				Name:        "Virtual Memory Usage",
				Description: "Virtual Memory Usage Statistics -- /proc/vmstat",
				Metrics: []domain.Metric{
					domain.Metric{ID: "vmstat.pgfault", Name: "Minor Page Fault", Unit: "Page Faults"},
					domain.Metric{ID: "vmstat.pgmajfault", Name: "Major Page Fault", Unit: "Page Faults"},
				},
			},
			//Files
			domain.MetricConfig{
				ID:          "files",
				Name:        "File Usage",
				Description: "File Statistics",
				Metrics: []domain.Metric{
					domain.Metric{ID: "Serviced.OpenFileDescriptors", Name: "OpenFileDescriptors", Unit: "Open File Descriptors"},
				},
			},
		},
		ThresholdConfigs: []domain.ThresholdConfig{
			domain.ThresholdConfig{
				ID:           "swap.empty",
				Name:         "Swap empty",
				Description:  "Alert when swap reaches zero",
				MetricSource: "memory",
				DataPoints:   []string{"swap.free", "memory.free"},
				Type:         "MinMax",
				Threshold:    domain.MinMaxThreshold{Min: &zeroInt64, Max: nil},
				EventTags: map[string]interface{}{
					"Severity":    1,
					"Resolution":  "Increase swap or memory",
					"Explanation": "Ran out of all available memory space",
					"EventClass":  "/Perf/Memory",
				},
			},
		},
	}
)

//Open File Descriptors
func newOpenFileDescriptorsGraph(tags map[string][]string) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				ID:           "ofd",
				Aggregator:   "avg",
				Color:        "#aec7e8",
				Fill:         false,
				Format:       "%6.2f",
				Legend:       "Serviced Open File Descriptors",
				Metric:       "Serviced.OpenFileDescriptors",
				MetricSource: "files",
				Name:         "Serviced Open File Descriptors",
				Rate:         false,
				Type:         "line",
			},
		},
		ID:     "serviced.ofd",
		Name:   "Serviced Open File Descriptors",
		Footer: false,
		Format: "%d",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		ReturnSet:   "EXACT",
		Type:        "line",
		Tags:        tags,
		Description: "Graph of serviced's total open file descriptors over time",
	}
}

//Major Page Faults
func newMajorPageFaultGraph(tags map[string][]string) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				ID:           "pgfault",
				Color:        "#aec7e8",
				Fill:         false,
				Format:       "%.2f",
				Legend:       "Major Page Faults",
				Metric:       "vmstat.pgmajfault",
				MetricSource: "virtual.memory",
				Name:         "Major Page Faults",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "line",
			},
		},
		ID:     "memory.major.pagefault",
		Name:   "Memory Major Page Faults",
		Footer: false,
		Format: "%.2f",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "Faults / Min",
		ReturnSet:   "EXACT",
		Type:        "line",
		Tags:        tags,
		Description: "Graph of major memory page faults over time",
	}
}

//Cpu Usage
func newCpuConfigGraph(tags map[string][]string, totalCores int) domain.GraphConfig {
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#729ed7",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "nice",
				Legend:       "Nice",
				Metric:       "cpu.nice",
				MetricSource: "cpu",
				Name:         "Nice",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#aee8cf",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "user",
				Legend:       "User",
				Metric:       "cpu.user",
				MetricSource: "cpu",
				Name:         "User",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#eaf0f9",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "idle",
				Legend:       "Idle",
				Metric:       "cpu.idle",
				MetricSource: "cpu",
				Name:         "Idle",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#d7729e",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "system",
				Legend:       "System",
				Metric:       "cpu.system",
				MetricSource: "cpu",
				Name:         "System",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#e8aec7",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "iowait",
				Legend:       "IOWait",
				Metric:       "cpu.iowait",
				MetricSource: "cpu",
				Name:         "IOWait",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#e8cfae",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "irq",
				Legend:       "IRQ",
				Metric:       "cpu.irq",
				MetricSource: "cpu",
				Name:         "IRQ",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Color:        "#ff0000",
				Fill:         false,
				Format:       "%6.2f",
				ID:           "steal",
				Legend:       "Steal",
				Metric:       "cpu.steal",
				MetricSource: "cpu",
				Name:         "Steal",
				Rate:         true,
				RateOptions: &domain.DataPointRateOptions{
					Counter:        true,
					CounterMax:     9223372036854775000, // Java Long.MaxValue is the default, googled and found this for it.
					ResetThreshold: 0,
				},
				Type: "area",
			},
		},
		ID:     "cpu.usage",
		Name:   "CPU Usage",
		Footer: false,
		Format: "%d",
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "% Used",
		ReturnSet:   "EXACT",
		Type:        "area",
		Tags:        tags,
		Description: "Graph of system and user cpu usage over time",
	}
}

func newRSSConfigGraph(tags map[string][]string, totalMemory uint64) domain.GraphConfig {
	MaxY := int(totalMemory / 1024 / 1024 / 1024)
	return domain.GraphConfig{
		DataPoints: []domain.DataPoint{
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#e8aec7",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Used",
				Metric:       "memory.actualused",
				MetricSource: "memory",
				Name:         "Used",
				Type:         "area",
				ID:           "used",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#b2aee8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Cached",
				Metric:       "memory.cached",
				MetricSource: "memory",
				Name:         "Cached",
				Type:         "area",
				ID:           "cached",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#aec7e8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Buffers",
				Metric:       "memory.buffers",
				MetricSource: "memory",
				Name:         "Buffers",
				Type:         "area",
				ID:           "buffers",
			},
			domain.DataPoint{
				Aggregator:   "avg",
				Expression:   "rpn:1024,/,1024,/,1024,/",
				Color:        "#aee4e8",
				Fill:         true,
				Format:       "%6.2f",
				Legend:       "Free",
				Metric:       "memory.free",
				MetricSource: "memory",
				Name:         "Free",
				Type:         "area",
				ID:           "free",
			},
		},
		ID:     "memory.usage",
		Name:   "Memory Usage",
		Footer: false,
		Format: "%6.2f",
		MaxY:   &MaxY,
		MinY:   &zero,
		Range: &domain.GraphConfigRange{
			End:   "0s-ago",
			Start: "1h-ago",
		},
		YAxisLabel:  "GB",
		ReturnSet:   "EXACT",
		Type:        "area",
		Tags:        tags,
		Description: "Graph of memory free (-buffers/+cache) vs used (total - free) over time",
	}
}
<|endoftext|>


<commit_before>package web

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/registry"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mhttp "github.com/micro/go-micro/v2/util/http"
	mnet "github.com/micro/go-micro/v2/util/net"
	mls "github.com/micro/go-micro/v2/util/tls"
)

type service struct {
	opts Options

	mux *http.ServeMux
	srv *registry.Service

	sync.Mutex
	running bool
	static  bool
	exit    chan chan error
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)
	s := &service{
		opts:   options,
		mux:    http.NewServeMux(),
		static: true,
	}
	s.srv = s.genSrv()
	return s
}

func (s *service) genSrv() *registry.Service {
	var host string
	var port string
	var err error

	// default host:port
	if len(s.opts.Address) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	// check the advertise address first
	// if it exists then use it, otherwise
	// use the address
	if len(s.opts.Advertise) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	addr, err := maddr.Extract(host)
	if err != nil {
		log.Fatal(err)
	}

	if strings.Count(addr, ":") > 0 {
		addr = "[" + addr + "]"
	}

	return &registry.Service{
		Name:    s.opts.Name,
		Version: s.opts.Version,
		Nodes: []*registry.Node{{
			Id:       s.opts.Id,
			Address:  fmt.Sprintf("%s:%s", addr, port),
			Metadata: s.opts.Metadata,
		}},
	}
}

func (s *service) run(exit chan bool) {
	if s.opts.RegisterInterval <= time.Duration(0) {
		return
	}

	t := time.NewTicker(s.opts.RegisterInterval)

	for {
		select {
		case <-t.C:
			s.register()
		case <-exit:
			t.Stop()
			return
		}
	}
}

func (s *service) register() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}

	// service node need modify, node address maybe changed
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	// use RegisterCheck func before register
	if err := s.opts.RegisterCheck(s.opts.Context); err != nil {
		if logger.V(logger.ErrorLevel, log) {
			log.Errorf("Server %s-%s register check error: %s", s.opts.Name, s.opts.Id, err)
		}
		return err
	}

	return r.Register(s.srv, registry.RegisterTTL(s.opts.RegisterTTL))
}

func (s *service) deregister() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}
	return r.Deregister(s.srv)
}

func (s *service) start() error {
	s.Lock()
	defer s.Unlock()

	if s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	l, err := s.listen("tcp", s.opts.Address)
	if err != nil {
		return err
	}

	s.opts.Address = l.Addr().String()
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	var h http.Handler

	if s.opts.Handler != nil {
		h = s.opts.Handler
	} else {
		h = s.mux
		var r sync.Once

		// register the html dir
		r.Do(func() {
			// static dir
			static := s.opts.StaticDir
			if s.opts.StaticDir[0] != '/' {
				dir, _ := os.Getwd()
				static = filepath.Join(dir, static)
			}

			// set static if no / handler is registered
			if s.static {
				_, err := os.Stat(static)
				if err == nil {
					if logger.V(logger.InfoLevel, log) {
						log.Infof("Enabling static file serving from %s", static)
					}
					s.mux.Handle("/", http.FileServer(http.Dir(static)))
				}
			}
		})
	}

	var httpSrv *http.Server
	if s.opts.Server != nil {
		httpSrv = s.opts.Server
	} else {
		httpSrv = &http.Server{}
	}

	httpSrv.Handler = h

	go httpSrv.Serve(l)

	for _, fn := range s.opts.AfterStart {
		if err := fn(); err != nil {
			return err
		}
	}

	s.exit = make(chan chan error, 1)
	s.running = true

	go func() {
		ch := <-s.exit
		ch <- l.Close()
	}()

	if logger.V(logger.DebugLevel, log) {
		log.Debugf("Listening on %v", l.Addr().String())
	}
	return nil
}

func (s *service) stop() error {
	s.Lock()
	defer s.Unlock()

	if !s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStop {
		if err := fn(); err != nil {
			return err
		}
	}

	ch := make(chan error, 1)
	s.exit <- ch
	s.running = false

	if logger.V(logger.InfoLevel, log) {
		log.Info("Stopping")
	}

	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			if chErr := <-ch; chErr != nil {
				return chErr
			}
			return err
		}
	}

	return <-ch
}

func (s *service) Client() *http.Client {
	rt := mhttp.NewRoundTripper(
		mhttp.WithRegistry(s.opts.Registry),
	)
	return &http.Client{
		Transport: rt,
	}
}

func (s *service) Handle(pattern string, handler http.Handler) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}

	// if its unseen then add an endpoint
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	// disable static serving
	if pattern == "/" {
		s.Lock()
		s.static = false
		s.Unlock()
	}

	// register the handler
	s.mux.Handle(pattern, handler)
}

func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	s.mux.HandleFunc(pattern, handler)
}

func (s *service) Init(opts ...Option) error {
	for _, o := range opts {
		o(&s.opts)
	}

	serviceOpts := []micro.Option{}

	if len(s.opts.Flags) > 0 {
		serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...))
	}

	if s.opts.Registry != nil {
		serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry))
	}

	serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error {
		if ttl := ctx.Int("register_ttl"); ttl > 0 {
			s.opts.RegisterTTL = time.Duration(ttl) * time.Second
		}

		if interval := ctx.Int("register_interval"); interval > 0 {
			s.opts.RegisterInterval = time.Duration(interval) * time.Second
		}

		if name := ctx.String("server_name"); len(name) > 0 {
			s.opts.Name = name
		}

		if ver := ctx.String("server_version"); len(ver) > 0 {
			s.opts.Version = ver
		}

		if id := ctx.String("server_id"); len(id) > 0 {
			s.opts.Id = id
		}

		if addr := ctx.String("server_address"); len(addr) > 0 {
			s.opts.Address = addr
		}

		if adv := ctx.String("server_advertise"); len(adv) > 0 {
			s.opts.Advertise = adv
		}

		if s.opts.Action != nil {
			s.opts.Action(ctx)
		}

		return nil
	}))

	// pass in own name and version
	serviceOpts = append(serviceOpts, micro.Name(s.opts.Name))
	serviceOpts = append(serviceOpts, micro.Version(s.opts.Version))

	s.opts.Service.Init(serviceOpts...)
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	return nil
}

func (s *service) Run() error {
	if err := s.start(); err != nil {
		return err
	}

	if err := s.register(); err != nil {
		return err
	}

	// start reg loop
	ex := make(chan bool)
	go s.run(ex)

	ch := make(chan os.Signal, 1)
	if s.opts.Signal {
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	}

	select {
	// wait on kill signal
	case sig := <-ch:
		if logger.V(logger.InfoLevel, log) {
			log.Infof("Received signal %s", sig)
		}
	// wait on context cancel
	case <-s.opts.Context.Done():
		if logger.V(logger.InfoLevel, log) {
			log.Info("Received context shutdown")
		}
	}

	// exit reg loop
	close(ex)

	if err := s.deregister(); err != nil {
		return err
	}

	return s.stop()
}

// Options returns the options for the given service
func (s *service) Options() Options {
	return s.opts
}

func (s *service) listen(network, addr string) (net.Listener, error) {
	var l net.Listener
	var err error

	// TODO: support use of listen options
	if s.opts.Secure || s.opts.TLSConfig != nil {
		config := s.opts.TLSConfig

		fn := func(addr string) (net.Listener, error) {
			if config == nil {
				hosts := []string{addr}

				// check if its a valid host:port
				if host, _, err := net.SplitHostPort(addr); err == nil {
					if len(host) == 0 {
						hosts = maddr.IPs()
					} else {
						hosts = []string{host}
					}
				}

				// generate a certificate
				cert, err := mls.Certificate(hosts...)
				if err != nil {
					return nil, err
				}
				config = &tls.Config{Certificates: []tls.Certificate{cert}}
			}
			return tls.Listen(network, addr, config)
		}

		l, err = mnet.Listen(addr, fn)
	} else {
		fn := func(addr string) (net.Listener, error) {
			return net.Listen(network, addr)
		}

		l, err = mnet.Listen(addr, fn)
	}

	if err != nil {
		return nil, err
	}

	return l, nil
}
<commit_msg>Log listening port<commit_after>package web

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/registry"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mhttp "github.com/micro/go-micro/v2/util/http"
	mnet "github.com/micro/go-micro/v2/util/net"
	mls "github.com/micro/go-micro/v2/util/tls"
)

type service struct {
	opts Options

	mux *http.ServeMux
	srv *registry.Service

	sync.Mutex
	running bool
	static  bool
	exit    chan chan error
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)
	s := &service{
		opts:   options,
		mux:    http.NewServeMux(),
		static: true,
	}
	s.srv = s.genSrv()
	return s
}

func (s *service) genSrv() *registry.Service {
	var host string
	var port string
	var err error

	// default host:port
	if len(s.opts.Address) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	// check the advertise address first
	// if it exists then use it, otherwise
	// use the address
	if len(s.opts.Advertise) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	addr, err := maddr.Extract(host)
	if err != nil {
		log.Fatal(err)
	}

	if strings.Count(addr, ":") > 0 {
		addr = "[" + addr + "]"
	}

	return &registry.Service{
		Name:    s.opts.Name,
		Version: s.opts.Version,
		Nodes: []*registry.Node{{
			Id:       s.opts.Id,
			Address:  fmt.Sprintf("%s:%s", addr, port),
			Metadata: s.opts.Metadata,
		}},
	}
}

func (s *service) run(exit chan bool) {
	if s.opts.RegisterInterval <= time.Duration(0) {
		return
	}

	t := time.NewTicker(s.opts.RegisterInterval)

	for {
		select {
		case <-t.C:
			s.register()
		case <-exit:
			t.Stop()
			return
		}
	}
}

func (s *service) register() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}

	// service node need modify, node address maybe changed
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	// use RegisterCheck func before register
	if err := s.opts.RegisterCheck(s.opts.Context); err != nil {
		if logger.V(logger.ErrorLevel, log) {
			log.Errorf("Server %s-%s register check error: %s", s.opts.Name, s.opts.Id, err)
		}
		return err
	}

	return r.Register(s.srv, registry.RegisterTTL(s.opts.RegisterTTL))
}

func (s *service) deregister() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}
	return r.Deregister(s.srv)
}

func (s *service) start() error {
	s.Lock()
	defer s.Unlock()

	if s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	l, err := s.listen("tcp", s.opts.Address)
	if err != nil {
		return err
	}

	s.opts.Address = l.Addr().String()
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	var h http.Handler

	if s.opts.Handler != nil {
		h = s.opts.Handler
	} else {
		h = s.mux
		var r sync.Once

		// register the html dir
		r.Do(func() {
			// static dir
			static := s.opts.StaticDir
			if s.opts.StaticDir[0] != '/' {
				dir, _ := os.Getwd()
				static = filepath.Join(dir, static)
			}

			// set static if no / handler is registered
			if s.static {
				_, err := os.Stat(static)
				if err == nil {
					if logger.V(logger.InfoLevel, log) {
						log.Infof("Enabling static file serving from %s", static)
					}
					s.mux.Handle("/", http.FileServer(http.Dir(static)))
				}
			}
		})
	}

	var httpSrv *http.Server
	if s.opts.Server != nil {
		httpSrv = s.opts.Server
	} else {
		httpSrv = &http.Server{}
	}

	httpSrv.Handler = h

	go httpSrv.Serve(l)

	for _, fn := range s.opts.AfterStart {
		if err := fn(); err != nil {
			return err
		}
	}

	s.exit = make(chan chan error, 1)
	s.running = true

	go func() {
		ch := <-s.exit
		ch <- l.Close()
	}()

	if logger.V(logger.InfoLevel, log) {
		log.Debugf("Listening on %v", l.Addr().String())
	}
	return nil
}

func (s *service) stop() error {
	s.Lock()
	defer s.Unlock()

	if !s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStop {
		if err := fn(); err != nil {
			return err
		}
	}

	ch := make(chan error, 1)
	s.exit <- ch
	s.running = false

	if logger.V(logger.InfoLevel, log) {
		log.Info("Stopping")
	}

	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			if chErr := <-ch; chErr != nil {
				return chErr
			}
			return err
		}
	}

	return <-ch
}

func (s *service) Client() *http.Client {
	rt := mhttp.NewRoundTripper(
		mhttp.WithRegistry(s.opts.Registry),
	)
	return &http.Client{
		Transport: rt,
	}
}

func (s *service) Handle(pattern string, handler http.Handler) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}

	// if its unseen then add an endpoint
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	// disable static serving
	if pattern == "/" {
		s.Lock()
		s.static = false
		s.Unlock()
	}

	// register the handler
	s.mux.Handle(pattern, handler)
}

func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	s.mux.HandleFunc(pattern, handler)
}

func (s *service) Init(opts ...Option) error {
	for _, o := range opts {
		o(&s.opts)
	}

	serviceOpts := []micro.Option{}

	if len(s.opts.Flags) > 0 {
		serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...))
	}

	if s.opts.Registry != nil {
		serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry))
	}

	serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error {
		if ttl := ctx.Int("register_ttl"); ttl > 0 {
			s.opts.RegisterTTL = time.Duration(ttl) * time.Second
		}

		if interval := ctx.Int("register_interval"); interval > 0 {
			s.opts.RegisterInterval = time.Duration(interval) * time.Second
		}

		if name := ctx.String("server_name"); len(name) > 0 {
			s.opts.Name = name
		}

		if ver := ctx.String("server_version"); len(ver) > 0 {
			s.opts.Version = ver
		}

		if id := ctx.String("server_id"); len(id) > 0 {
			s.opts.Id = id
		}

		if addr := ctx.String("server_address"); len(addr) > 0 {
			s.opts.Address = addr
		}

		if adv := ctx.String("server_advertise"); len(adv) > 0 {
			s.opts.Advertise = adv
		}

		if s.opts.Action != nil {
			s.opts.Action(ctx)
		}

		return nil
	}))

	// pass in own name and version
	serviceOpts = append(serviceOpts, micro.Name(s.opts.Name))
	serviceOpts = append(serviceOpts, micro.Version(s.opts.Version))

	s.opts.Service.Init(serviceOpts...)
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	return nil
}

func (s *service) Run() error {
	if err := s.start(); err != nil {
		return err
	}

	if err := s.register(); err != nil {
		return err
	}

	// start reg loop
	ex := make(chan bool)
	go s.run(ex)

	ch := make(chan os.Signal, 1)
	if s.opts.Signal {
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	}

	select {
	// wait on kill signal
	case sig := <-ch:
		if logger.V(logger.InfoLevel, log) {
			log.Infof("Received signal %s", sig)
		}
	// wait on context cancel
	case <-s.opts.Context.Done():
		if logger.V(logger.InfoLevel, log) {
			log.Info("Received context shutdown")
		}
	}

	// exit reg loop
	close(ex)

	if err := s.deregister(); err != nil {
		return err
	}

	return s.stop()
}

// Options returns the options for the given service
func (s *service) Options() Options {
	return s.opts
}

func (s *service) listen(network, addr string) (net.Listener, error) {
	var l net.Listener
	var err error

	// TODO: support use of listen options
	if s.opts.Secure || s.opts.TLSConfig != nil {
		config := s.opts.TLSConfig

		fn := func(addr string) (net.Listener, error) {
			if config == nil {
				hosts := []string{addr}

				// check if its a valid host:port
				if host, _, err := net.SplitHostPort(addr); err == nil {
					if len(host) == 0 {
						hosts = maddr.IPs()
					} else {
						hosts = []string{host}
					}
				}

				// generate a certificate
				cert, err := mls.Certificate(hosts...)
				if err != nil {
					return nil, err
				}
				config = &tls.Config{Certificates: []tls.Certificate{cert}}
			}
			return tls.Listen(network, addr, config)
		}

		l, err = mnet.Listen(addr, fn)
	} else {
		fn := func(addr string) (net.Listener, error) {
			return net.Listen(network, addr)
		}

		l, err = mnet.Listen(addr, fn)
	}

	if err != nil {
		return nil, err
	}

	return l, nil
}
<|endoftext|>


<commit_before>package web

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/registry"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mhttp "github.com/micro/go-micro/v2/util/http"
	mnet "github.com/micro/go-micro/v2/util/net"
	mls "github.com/micro/go-micro/v2/util/tls"
)

type service struct {
	opts Options

	mux *http.ServeMux
	srv *registry.Service

	sync.Mutex
	running bool
	static  bool
	exit    chan chan error
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)
	s := &service{
		opts:   options,
		mux:    http.NewServeMux(),
		static: true,
	}
	s.srv = s.genSrv()
	return s
}

func (s *service) genSrv() *registry.Service {
	var host string
	var port string
	var err error

	// default host:port
	if len(s.opts.Address) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	// check the advertise address first
	// if it exists then use it, otherwise
	// use the address
	if len(s.opts.Advertise) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	addr, err := maddr.Extract(host)
	if err != nil {
		log.Fatal(err)
	}

	if strings.Count(addr, ":") > 0 {
		addr = "[" + addr + "]"
	}

	return &registry.Service{
		Name:    s.opts.Name,
		Version: s.opts.Version,
		Nodes: []*registry.Node{{
			Id:       s.opts.Id,
			Address:  fmt.Sprintf("%s:%s", addr, port),
			Metadata: s.opts.Metadata,
		}},
	}
}

func (s *service) run(exit chan bool) {
	if s.opts.RegisterInterval <= time.Duration(0) {
		return
	}

	t := time.NewTicker(s.opts.RegisterInterval)

	for {
		select {
		case <-t.C:
			s.register()
		case <-exit:
			t.Stop()
			return
		}
	}
}

func (s *service) register() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}

	// service node need modify, node address maybe changed
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	// use RegisterCheck func before register
	if err := s.opts.RegisterCheck(s.opts.Context); err != nil {
		if logger.V(logger.ErrorLevel, log) {
			log.Errorf("Server %s-%s register check error: %s", s.opts.Name, s.opts.Id, err)
		}
		return err
	}

	return r.Register(s.srv, registry.RegisterTTL(s.opts.RegisterTTL))
}

func (s *service) deregister() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}
	return r.Deregister(s.srv)
}

func (s *service) start() error {
	s.Lock()
	defer s.Unlock()

	if s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	l, err := s.listen("tcp", s.opts.Address)
	if err != nil {
		return err
	}

	s.opts.Address = l.Addr().String()
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	var h http.Handler

	if s.opts.Handler != nil {
		h = s.opts.Handler
	} else {
		h = s.mux
		var r sync.Once

		// register the html dir
		r.Do(func() {
			// static dir
			static := s.opts.StaticDir
			if s.opts.StaticDir[0] != '/' {
				dir, _ := os.Getwd()
				static = filepath.Join(dir, static)
			}

			// set static if no / handler is registered
			if s.static {
				_, err := os.Stat(static)
				if err == nil {
					if logger.V(logger.InfoLevel, log) {
						log.Infof("Enabling static file serving from %s", static)
					}
					s.mux.Handle("/", http.FileServer(http.Dir(static)))
				}
			}
		})
	}

	var httpSrv *http.Server
	if s.opts.Server != nil {
		httpSrv = s.opts.Server
	} else {
		httpSrv = &http.Server{}
	}

	httpSrv.Handler = h

	go httpSrv.Serve(l)

	for _, fn := range s.opts.AfterStart {
		if err := fn(); err != nil {
			return err
		}
	}

	s.exit = make(chan chan error, 1)
	s.running = true

	go func() {
		ch := <-s.exit
		ch <- l.Close()
	}()

	if logger.V(logger.InfoLevel, log) {
		log.Infof("Listening on %v", l.Addr().String())
	}
	return nil
}

func (s *service) stop() error {
	s.Lock()
	defer s.Unlock()

	if !s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStop {
		if err := fn(); err != nil {
			return err
		}
	}

	ch := make(chan error, 1)
	s.exit <- ch
	s.running = false

	if logger.V(logger.InfoLevel, log) {
		log.Info("Stopping")
	}

	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			if chErr := <-ch; chErr != nil {
				return chErr
			}
			return err
		}
	}

	return <-ch
}

func (s *service) Client() *http.Client {
	rt := mhttp.NewRoundTripper(
		mhttp.WithRegistry(s.opts.Registry),
	)
	return &http.Client{
		Transport: rt,
	}
}

func (s *service) Handle(pattern string, handler http.Handler) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}

	// if its unseen then add an endpoint
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	// disable static serving
	if pattern == "/" {
		s.Lock()
		s.static = false
		s.Unlock()
	}

	// register the handler
	s.mux.Handle(pattern, handler)
}

func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	s.mux.HandleFunc(pattern, handler)
}

func (s *service) Init(opts ...Option) error {
	for _, o := range opts {
		o(&s.opts)
	}

	serviceOpts := []micro.Option{}

	if len(s.opts.Flags) > 0 {
		serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...))
	}

	if s.opts.Registry != nil {
		serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry))
	}

	serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error {
		if ttl := ctx.Int("register_ttl"); ttl > 0 {
			s.opts.RegisterTTL = time.Duration(ttl) * time.Second
		}

		if interval := ctx.Int("register_interval"); interval > 0 {
			s.opts.RegisterInterval = time.Duration(interval) * time.Second
		}

		if name := ctx.String("server_name"); len(name) > 0 {
			s.opts.Name = name
		}

		if ver := ctx.String("server_version"); len(ver) > 0 {
			s.opts.Version = ver
		}

		if id := ctx.String("server_id"); len(id) > 0 {
			s.opts.Id = id
		}

		if addr := ctx.String("server_address"); len(addr) > 0 {
			s.opts.Address = addr
		}

		if adv := ctx.String("server_advertise"); len(adv) > 0 {
			s.opts.Advertise = adv
		}

		if s.opts.Action != nil {
			s.opts.Action(ctx)
		}

		return nil
	}))

	// pass in own name and version
	serviceOpts = append(serviceOpts, micro.Name(s.opts.Name))
	serviceOpts = append(serviceOpts, micro.Version(s.opts.Version))

	s.opts.Service.Init(serviceOpts...)
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	return nil
}

func (s *service) Run() error {
	if err := s.start(); err != nil {
		return err
	}

	if err := s.register(); err != nil {
		return err
	}

	// start reg loop
	ex := make(chan bool)
	go s.run(ex)

	ch := make(chan os.Signal, 1)
	if s.opts.Signal {
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	}

	select {
	// wait on kill signal
	case sig := <-ch:
		if logger.V(logger.InfoLevel, log) {
			log.Infof("Received signal %s", sig)
		}
	// wait on context cancel
	case <-s.opts.Context.Done():
		if logger.V(logger.InfoLevel, log) {
			log.Info("Received context shutdown")
		}
	}

	// exit reg loop
	close(ex)

	if err := s.deregister(); err != nil {
		return err
	}

	return s.stop()
}

// Options returns the options for the given service
func (s *service) Options() Options {
	return s.opts
}

func (s *service) listen(network, addr string) (net.Listener, error) {
	var l net.Listener
	var err error

	// TODO: support use of listen options
	if s.opts.Secure || s.opts.TLSConfig != nil {
		config := s.opts.TLSConfig

		fn := func(addr string) (net.Listener, error) {
			if config == nil {
				hosts := []string{addr}

				// check if its a valid host:port
				if host, _, err := net.SplitHostPort(addr); err == nil {
					if len(host) == 0 {
						hosts = maddr.IPs()
					} else {
						hosts = []string{host}
					}
				}

				// generate a certificate
				cert, err := mls.Certificate(hosts...)
				if err != nil {
					return nil, err
				}
				config = &tls.Config{Certificates: []tls.Certificate{cert}}
			}
			return tls.Listen(network, addr, config)
		}

		l, err = mnet.Listen(addr, fn)
	} else {
		fn := func(addr string) (net.Listener, error) {
			return net.Listen(network, addr)
		}

		l, err = mnet.Listen(addr, fn)
	}

	if err != nil {
		return nil, err
	}

	return l, nil
}
<commit_msg>Static serving disabled<commit_after>package web

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/registry"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mhttp "github.com/micro/go-micro/v2/util/http"
	mnet "github.com/micro/go-micro/v2/util/net"
	mls "github.com/micro/go-micro/v2/util/tls"
)

type service struct {
	opts Options

	mux *http.ServeMux
	srv *registry.Service

	sync.Mutex
	running bool
	static  bool
	exit    chan chan error
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)
	s := &service{
		opts:   options,
		mux:    http.NewServeMux(),
		static: true,
	}
	s.srv = s.genSrv()
	return s
}

func (s *service) genSrv() *registry.Service {
	var host string
	var port string
	var err error

	// default host:port
	if len(s.opts.Address) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	// check the advertise address first
	// if it exists then use it, otherwise
	// use the address
	if len(s.opts.Advertise) > 0 {
		host, port, err = net.SplitHostPort(s.opts.Address)
		if err != nil {
			log.Fatal(err)
		}
	}

	addr, err := maddr.Extract(host)
	if err != nil {
		log.Fatal(err)
	}

	if strings.Count(addr, ":") > 0 {
		addr = "[" + addr + "]"
	}

	return &registry.Service{
		Name:    s.opts.Name,
		Version: s.opts.Version,
		Nodes: []*registry.Node{{
			Id:       s.opts.Id,
			Address:  fmt.Sprintf("%s:%s", addr, port),
			Metadata: s.opts.Metadata,
		}},
	}
}

func (s *service) run(exit chan bool) {
	if s.opts.RegisterInterval <= time.Duration(0) {
		return
	}

	t := time.NewTicker(s.opts.RegisterInterval)

	for {
		select {
		case <-t.C:
			s.register()
		case <-exit:
			t.Stop()
			return
		}
	}
}

func (s *service) register() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}

	// service node need modify, node address maybe changed
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	// use RegisterCheck func before register
	if err := s.opts.RegisterCheck(s.opts.Context); err != nil {
		if logger.V(logger.ErrorLevel, log) {
			log.Errorf("Server %s-%s register check error: %s", s.opts.Name, s.opts.Id, err)
		}
		return err
	}

	return r.Register(s.srv, registry.RegisterTTL(s.opts.RegisterTTL))
}

func (s *service) deregister() error {
	if s.srv == nil {
		return nil
	}
	// default to service registry
	r := s.opts.Service.Client().Options().Registry
	// switch to option if specified
	if s.opts.Registry != nil {
		r = s.opts.Registry
	}
	return r.Deregister(s.srv)
}

func (s *service) start() error {
	s.Lock()
	defer s.Unlock()

	if s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	l, err := s.listen("tcp", s.opts.Address)
	if err != nil {
		return err
	}

	s.opts.Address = l.Addr().String()
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	var h http.Handler

	if s.opts.Handler != nil {
		h = s.opts.Handler
	} else {
		h = s.mux
		var r sync.Once

		// register the html dir
		r.Do(func() {
			// static dir
			static := s.opts.StaticDir
			if s.opts.StaticDir[0] != '/' {
				dir, _ := os.Getwd()
				static = filepath.Join(dir, static)
			}

			// set static if no / handler is registered
			if s.static {
				_, err := os.Stat(static)
				if err == nil {
					if logger.V(logger.InfoLevel, log) {
						log.Infof("Enabling static file serving from %s", static)
					}
					s.mux.Handle("/", http.FileServer(http.Dir(static)))
				}
			}
		})
	}

	var httpSrv *http.Server
	if s.opts.Server != nil {
		httpSrv = s.opts.Server
	} else {
		httpSrv = &http.Server{}
	}

	httpSrv.Handler = h

	go httpSrv.Serve(l)

	for _, fn := range s.opts.AfterStart {
		if err := fn(); err != nil {
			return err
		}
	}

	s.exit = make(chan chan error, 1)
	s.running = true

	go func() {
		ch := <-s.exit
		ch <- l.Close()
	}()

	if logger.V(logger.InfoLevel, log) {
		log.Infof("Listening on %v", l.Addr().String())
	}
	return nil
}

func (s *service) stop() error {
	s.Lock()
	defer s.Unlock()

	if !s.running {
		return nil
	}

	for _, fn := range s.opts.BeforeStop {
		if err := fn(); err != nil {
			return err
		}
	}

	ch := make(chan error, 1)
	s.exit <- ch
	s.running = false

	if logger.V(logger.InfoLevel, log) {
		log.Info("Stopping")
	}

	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			if chErr := <-ch; chErr != nil {
				return chErr
			}
			return err
		}
	}

	return <-ch
}

func (s *service) Client() *http.Client {
	rt := mhttp.NewRoundTripper(
		mhttp.WithRegistry(s.opts.Registry),
	)
	return &http.Client{
		Transport: rt,
	}
}

func (s *service) Handle(pattern string, handler http.Handler) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}

	// if its unseen then add an endpoint
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	// disable static serving
	if pattern == "/" {
		s.Lock()
		s.static = false
		s.Unlock()
	}

	// register the handler
	s.mux.Handle(pattern, handler)
}

func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var seen bool
	for _, ep := range s.srv.Endpoints {
		if ep.Name == pattern {
			seen = true
			break
		}
	}
	if !seen {
		s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
			Name: pattern,
		})
	}

	// disable static serving
	if pattern == "/" {
		s.Lock()
		s.static = false
		s.Unlock()
	}

	s.mux.HandleFunc(pattern, handler)
}

func (s *service) Init(opts ...Option) error {
	for _, o := range opts {
		o(&s.opts)
	}

	serviceOpts := []micro.Option{}

	if len(s.opts.Flags) > 0 {
		serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...))
	}

	if s.opts.Registry != nil {
		serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry))
	}

	serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error {
		if ttl := ctx.Int("register_ttl"); ttl > 0 {
			s.opts.RegisterTTL = time.Duration(ttl) * time.Second
		}

		if interval := ctx.Int("register_interval"); interval > 0 {
			s.opts.RegisterInterval = time.Duration(interval) * time.Second
		}

		if name := ctx.String("server_name"); len(name) > 0 {
			s.opts.Name = name
		}

		if ver := ctx.String("server_version"); len(ver) > 0 {
			s.opts.Version = ver
		}

		if id := ctx.String("server_id"); len(id) > 0 {
			s.opts.Id = id
		}

		if addr := ctx.String("server_address"); len(addr) > 0 {
			s.opts.Address = addr
		}

		if adv := ctx.String("server_advertise"); len(adv) > 0 {
			s.opts.Advertise = adv
		}

		if s.opts.Action != nil {
			s.opts.Action(ctx)
		}

		return nil
	}))

	// pass in own name and version
	serviceOpts = append(serviceOpts, micro.Name(s.opts.Name))
	serviceOpts = append(serviceOpts, micro.Version(s.opts.Version))

	s.opts.Service.Init(serviceOpts...)
	srv := s.genSrv()
	srv.Endpoints = s.srv.Endpoints
	s.srv = srv

	return nil
}

func (s *service) Run() error {
	if err := s.start(); err != nil {
		return err
	}

	if err := s.register(); err != nil {
		return err
	}

	// start reg loop
	ex := make(chan bool)
	go s.run(ex)

	ch := make(chan os.Signal, 1)
	if s.opts.Signal {
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	}

	select {
	// wait on kill signal
	case sig := <-ch:
		if logger.V(logger.InfoLevel, log) {
			log.Infof("Received signal %s", sig)
		}
	// wait on context cancel
	case <-s.opts.Context.Done():
		if logger.V(logger.InfoLevel, log) {
			log.Info("Received context shutdown")
		}
	}

	// exit reg loop
	close(ex)

	if err := s.deregister(); err != nil {
		return err
	}

	return s.stop()
}

// Options returns the options for the given service
func (s *service) Options() Options {
	return s.opts
}

func (s *service) listen(network, addr string) (net.Listener, error) {
	var l net.Listener
	var err error

	// TODO: support use of listen options
	if s.opts.Secure || s.opts.TLSConfig != nil {
		config := s.opts.TLSConfig

		fn := func(addr string) (net.Listener, error) {
			if config == nil {
				hosts := []string{addr}

				// check if its a valid host:port
				if host, _, err := net.SplitHostPort(addr); err == nil {
					if len(host) == 0 {
						hosts = maddr.IPs()
					} else {
						hosts = []string{host}
					}
				}

				// generate a certificate
				cert, err := mls.Certificate(hosts...)
				if err != nil {
					return nil, err
				}
				config = &tls.Config{Certificates: []tls.Certificate{cert}}
			}
			return tls.Listen(network, addr, config)
		}

		l, err = mnet.Listen(addr, fn)
	} else {
		fn := func(addr string) (net.Listener, error) {
			return net.Listen(network, addr)
		}

		l, err = mnet.Listen(addr, fn)
	}

	if err != nil {
		return nil, err
	}

	return l, nil
}
<|endoftext|>


<commit_before>package indexer

import (
	"bytes"
	"io"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

type htmlParser struct {
	title   string
	b       bytes.Buffer
	noIndex bool // set to true if has meta name="robots" or name="kkr-search" and content="noindex">
}

func (p *htmlParser) parseMeta(n *html.Node) {
	consume := false
	robots := false
	for _, a := range n.Attr {
		if a.Key == "name" {
			v := strings.ToLower(a.Val)
			if v == "keywords" || v == "description" {
				consume = true
			} else if v == "robots" || v == "kkr-search" {
				robots = true
			}
		}
	}
	if !consume && !robots {
		return
	}
	for _, a := range n.Attr {
		if a.Key == "content" {
			if consume {
				p.consumeString(a.Val)
			} else if robots {
				p.noIndex = false
			}
		}
	}
}

func (p *htmlParser) skipNoIndexElement(n *html.Node) bool {
	for _, a := range n.Attr {
		if a.Key == "data-kkr-search" {
			v := strings.ToLower(a.Val)
			if v == "noindex" {
				return true
			}
		}
	}
	return false
}

func (p *htmlParser) parseImg(n *html.Node) {
	for _, a := range n.Attr {
		if a.Key == "alt" {
			p.consumeString(a.Val)
			return
		}
	}
}

func (p *htmlParser) parseTitle(n *html.Node) {
	if c := n.FirstChild; c != nil && c.Type == html.TextNode {
		p.title = c.Data
	}
}

func (p *htmlParser) consumeString(s string) {
	p.b.WriteString(s)
	p.b.WriteByte('\n')
}

func (p *htmlParser) parseNoscript(n *html.Node) {
	c := n.FirstChild
	if c == nil {
		return
	}
	nodes, err := html.ParseFragment(strings.NewReader(c.Data), nil)
	if err != nil {
		return // ignore error
	}
	for _, v := range nodes {
		p.parseNode(v)
	}
}

func (p *htmlParser) parseNode(n *html.Node) {
	if p.noIndex {
		return
	}
	switch n.Type {
	case html.DocumentNode:
		// Parse children
		if c := n.FirstChild; c != nil {
			p.parseNode(c)
		}
	case html.ElementNode:
		switch n.DataAtom {
		case atom.Title:
			p.parseTitle(n)
		case atom.Meta:
			p.parseMeta(n)
		case atom.Img:
			p.parseImg(n)
		case atom.Noscript:
			// Parse insides of noscript as HTML.
			p.parseNoscript(n)
		case atom.Script, atom.Style:
			// skip children
		default:
			if !p.skipNoIndexElement(n) {
				// Parse children
				if c := n.FirstChild; c != nil {
					p.parseNode(c)
				}
			}
		}
	case html.TextNode:
		p.consumeString(n.Data)
	}
	// Parse sibling.
	if c := n.NextSibling; c != nil {
		p.parseNode(c)
	}
}

func (p *htmlParser) Parse(r io.Reader) error {
	doc, err := html.Parse(r)
	if err != nil {
		return err
	}
	p.parseNode(doc)
	return nil
}

func (p *htmlParser) Content() string {
	return p.b.String()
}

func (p *htmlParser) Title() string {
	return p.title
}

func (p *htmlParser) IsIndexable() bool {
	return !p.noIndex
}

func parseHTML(r io.Reader) (title, content string, indexable bool, err error) {
	var p htmlParser
	err = p.Parse(r)
	if err != nil {
		return
	}
	return p.Title(), p.Content(), p.IsIndexable(), nil
}
<commit_msg>search: index image titles<commit_after>package indexer

import (
	"bytes"
	"io"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

type htmlParser struct {
	title   string
	b       bytes.Buffer
	noIndex bool // set to true if has meta name="robots" or name="kkr-search" and content="noindex">
}

func (p *htmlParser) parseMeta(n *html.Node) {
	consume := false
	robots := false
	for _, a := range n.Attr {
		if a.Key == "name" {
			v := strings.ToLower(a.Val)
			if v == "keywords" || v == "description" {
				consume = true
			} else if v == "robots" || v == "kkr-search" {
				robots = true
			}
		}
	}
	if !consume && !robots {
		return
	}
	for _, a := range n.Attr {
		if a.Key == "content" {
			if consume {
				p.consumeString(a.Val)
			} else if robots {
				p.noIndex = false
			}
		}
	}
}

func (p *htmlParser) skipNoIndexElement(n *html.Node) bool {
	for _, a := range n.Attr {
		if a.Key == "data-kkr-search" {
			v := strings.ToLower(a.Val)
			if v == "noindex" {
				return true
			}
		}
	}
	return false
}

func (p *htmlParser) parseImg(n *html.Node) {
	for _, a := range n.Attr {
		if a.Key == "alt" || a.Key == "title" {
			p.consumeString(a.Val)
			return
		}
	}
}

func (p *htmlParser) parseTitle(n *html.Node) {
	if c := n.FirstChild; c != nil && c.Type == html.TextNode {
		p.title = c.Data
	}
}

func (p *htmlParser) consumeString(s string) {
	p.b.WriteString(s)
	p.b.WriteByte('\n')
}

func (p *htmlParser) parseNoscript(n *html.Node) {
	c := n.FirstChild
	if c == nil {
		return
	}
	nodes, err := html.ParseFragment(strings.NewReader(c.Data), nil)
	if err != nil {
		return // ignore error
	}
	for _, v := range nodes {
		p.parseNode(v)
	}
}

func (p *htmlParser) parseNode(n *html.Node) {
	if p.noIndex {
		return
	}
	switch n.Type {
	case html.DocumentNode:
		// Parse children
		if c := n.FirstChild; c != nil {
			p.parseNode(c)
		}
	case html.ElementNode:
		switch n.DataAtom {
		case atom.Title:
			p.parseTitle(n)
		case atom.Meta:
			p.parseMeta(n)
		case atom.Img:
			p.parseImg(n)
		case atom.Noscript:
			// Parse insides of noscript as HTML.
			p.parseNoscript(n)
		case atom.Script, atom.Style:
			// skip children
		default:
			if !p.skipNoIndexElement(n) {
				// Parse children
				if c := n.FirstChild; c != nil {
					p.parseNode(c)
				}
			}
		}
	case html.TextNode:
		p.consumeString(n.Data)
	}
	// Parse sibling.
	if c := n.NextSibling; c != nil {
		p.parseNode(c)
	}
}

func (p *htmlParser) Parse(r io.Reader) error {
	doc, err := html.Parse(r)
	if err != nil {
		return err
	}
	p.parseNode(doc)
	return nil
}

func (p *htmlParser) Content() string {
	return p.b.String()
}

func (p *htmlParser) Title() string {
	return p.title
}

func (p *htmlParser) IsIndexable() bool {
	return !p.noIndex
}

func parseHTML(r io.Reader) (title, content string, indexable bool, err error) {
	var p htmlParser
	err = p.Parse(r)
	if err != nil {
		return
	}
	return p.Title(), p.Content(), p.IsIndexable(), nil
}
<|endoftext|>