<commit_before>// +build !windows

/*
   Copyright The containerd 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 client

import (
	"context"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/sys/unix"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/stevvooe/ttrpc"

	"github.com/containerd/containerd/events"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/runtime/shim"
	shimapi "github.com/containerd/containerd/runtime/shim/v1"
	"github.com/containerd/containerd/sys"
	ptypes "github.com/gogo/protobuf/types"
)

var empty = &ptypes.Empty{}

// Opt is an option for a shim client configuration
type Opt func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error)

// WithStart executes a new shim process
func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) Opt {
	return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) {
		socket, err := newSocket(address)
		if err != nil {
			return nil, nil, err
		}
		defer socket.Close()
		f, err := socket.File()
		if err != nil {
			return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address)
		}
		defer f.Close()

		cmd, err := newCommand(binary, daemonAddress, debug, config, f)
		if err != nil {
			return nil, nil, err
		}
		if err := cmd.Start(); err != nil {
			return nil, nil, errors.Wrapf(err, "failed to start shim")
		}
		defer func() {
			if err != nil {
				cmd.Process.Kill()
			}
		}()
		go func() {
			cmd.Wait()
			exitHandler()
		}()
		log.G(ctx).WithFields(logrus.Fields{
			"pid":     cmd.Process.Pid,
			"address": address,
			"debug":   debug,
		}).Infof("shim %s started", binary)
		// set shim in cgroup if it is provided
		if cgroup != "" {
			if err := setCgroup(cgroup, cmd); err != nil {
				return nil, nil, err
			}
			log.G(ctx).WithFields(logrus.Fields{
				"pid":     cmd.Process.Pid,
				"address": address,
			}).Infof("shim placed in cgroup %s", cgroup)
		}
		if err = sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil {
			return nil, nil, errors.Wrap(err, "failed to set OOM Score on shim")
		}
		c, clo, err := WithConnect(address, func() {})(ctx, config)
		if err != nil {
			return nil, nil, errors.Wrap(err, "failed to connect")
		}
		return c, clo, nil
	}
}

func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) (*exec.Cmd, error) {
	selfExe, err := os.Executable()
	if err != nil {
		return nil, err
	}
	args := []string{
		"-namespace", config.Namespace,
		"-workdir", config.WorkDir,
		"-address", daemonAddress,
		"-containerd-binary", selfExe,
	}

	if config.Criu != "" {
		args = append(args, "-criu-path", config.Criu)
	}
	if config.RuntimeRoot != "" {
		args = append(args, "-runtime-root", config.RuntimeRoot)
	}
	if config.SystemdCgroup {
		args = append(args, "-systemd-cgroup")
	}
	if debug {
		args = append(args, "-debug")
	}

	cmd := exec.Command(binary, args...)
	cmd.Dir = config.Path
	// make sure the shim can be re-parented to system init
	// and is cloned in a new mount namespace because the overlay/filesystems
	// will be mounted by the shim
	cmd.SysProcAttr = getSysProcAttr()
	cmd.ExtraFiles = append(cmd.ExtraFiles, socket)
	if debug {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	return cmd, nil
}

func newSocket(address string) (*net.UnixListener, error) {
	if len(address) > 106 {
		return nil, errors.Errorf("%q: unix socket path too long (> 106)", address)
	}
	l, err := net.Listen("unix", "\x00"+address)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address)
	}

	return l.(*net.UnixListener), nil
}

func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) {
	return d(address, 100*time.Second)
}

func annonDialer(address string, timeout time.Duration) (net.Conn, error) {
	address = strings.TrimPrefix(address, "unix://")
	return net.DialTimeout("unix", "\x00"+address, timeout)
}

// WithConnect connects to an existing shim
func WithConnect(address string, onClose func()) Opt {
	return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
		conn, err := connect(address, annonDialer)
		if err != nil {
			return nil, nil, err
		}
		client := ttrpc.NewClient(conn)
		client.OnClose(onClose)
		return shimapi.NewShimClient(client), conn, nil
	}
}

// WithLocal uses an in process shim
func WithLocal(publisher events.Publisher) func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error) {
	return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
		service, err := shim.NewService(config, publisher)
		if err != nil {
			return nil, nil, err
		}
		return shim.NewLocal(service), nil, nil
	}
}

// New returns a new shim client
func New(ctx context.Context, config shim.Config, opt Opt) (*Client, error) {
	s, c, err := opt(ctx, config)
	if err != nil {
		return nil, err
	}
	return &Client{
		ShimService: s,
		c:           c,
		exitCh:      make(chan struct{}),
	}, nil
}

// Client is a shim client containing the connection to a shim
type Client struct {
	shimapi.ShimService

	c        io.Closer
	exitCh   chan struct{}
	exitOnce sync.Once
}

// IsAlive returns true if the shim can be contacted.
// NOTE: a negative answer doesn't mean that the process is gone.
func (c *Client) IsAlive(ctx context.Context) (bool, error) {
	_, err := c.ShimInfo(ctx, empty)
	if err != nil {
		// TODO(stevvooe): There are some error conditions that need to be
		// handle with unix sockets existence to give the right answer here.
		return false, err
	}
	return true, nil
}

// StopShim signals the shim to exit and wait for the process to disappear
func (c *Client) StopShim(ctx context.Context) error {
	return c.signalShim(ctx, unix.SIGTERM)
}

// KillShim kills the shim forcefully and wait for the process to disappear
func (c *Client) KillShim(ctx context.Context) error {
	return c.signalShim(ctx, unix.SIGKILL)
}

// Close the cient connection
func (c *Client) Close() error {
	if c.c == nil {
		return nil
	}
	return c.c.Close()
}

func (c *Client) signalShim(ctx context.Context, sig syscall.Signal) error {
	info, err := c.ShimInfo(ctx, empty)
	if err != nil {
		return err
	}
	pid := int(info.ShimPid)
	// make sure we don't kill ourselves if we are running a local shim
	if os.Getpid() == pid {
		return nil
	}
	if err := unix.Kill(pid, sig); err != nil && err != unix.ESRCH {
		return err
	}
	// wait for shim to die after being signaled
	select {
	case <-ctx.Done():
		return ctx.Err()
	case <-c.waitForExit(pid):
		return nil
	}
}

func (c *Client) waitForExit(pid int) <-chan struct{} {
	c.exitOnce.Do(func() {
		for {
			// use kill(pid, 0) here because the shim could have been reparented
			// and we are no longer able to waitpid(pid, ...) on the shim
			if err := unix.Kill(pid, 0); err == unix.ESRCH {
				close(c.exitCh)
				return
			}
			time.Sleep(10 * time.Millisecond)
		}
	})
	return c.exitCh
}
<commit_msg>Set shim max procs via env var<commit_after>// +build !windows

/*
   Copyright The containerd 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 client

import (
	"context"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/sys/unix"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/stevvooe/ttrpc"

	"github.com/containerd/containerd/events"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/runtime/shim"
	shimapi "github.com/containerd/containerd/runtime/shim/v1"
	"github.com/containerd/containerd/sys"
	ptypes "github.com/gogo/protobuf/types"
)

var empty = &ptypes.Empty{}

// Opt is an option for a shim client configuration
type Opt func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error)

// WithStart executes a new shim process
func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) Opt {
	return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) {
		socket, err := newSocket(address)
		if err != nil {
			return nil, nil, err
		}
		defer socket.Close()
		f, err := socket.File()
		if err != nil {
			return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address)
		}
		defer f.Close()

		cmd, err := newCommand(binary, daemonAddress, debug, config, f)
		if err != nil {
			return nil, nil, err
		}
		if err := cmd.Start(); err != nil {
			return nil, nil, errors.Wrapf(err, "failed to start shim")
		}
		defer func() {
			if err != nil {
				cmd.Process.Kill()
			}
		}()
		go func() {
			cmd.Wait()
			exitHandler()
		}()
		log.G(ctx).WithFields(logrus.Fields{
			"pid":     cmd.Process.Pid,
			"address": address,
			"debug":   debug,
		}).Infof("shim %s started", binary)
		// set shim in cgroup if it is provided
		if cgroup != "" {
			if err := setCgroup(cgroup, cmd); err != nil {
				return nil, nil, err
			}
			log.G(ctx).WithFields(logrus.Fields{
				"pid":     cmd.Process.Pid,
				"address": address,
			}).Infof("shim placed in cgroup %s", cgroup)
		}
		if err = sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil {
			return nil, nil, errors.Wrap(err, "failed to set OOM Score on shim")
		}
		c, clo, err := WithConnect(address, func() {})(ctx, config)
		if err != nil {
			return nil, nil, errors.Wrap(err, "failed to connect")
		}
		return c, clo, nil
	}
}

func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) (*exec.Cmd, error) {
	selfExe, err := os.Executable()
	if err != nil {
		return nil, err
	}
	args := []string{
		"-namespace", config.Namespace,
		"-workdir", config.WorkDir,
		"-address", daemonAddress,
		"-containerd-binary", selfExe,
	}

	if config.Criu != "" {
		args = append(args, "-criu-path", config.Criu)
	}
	if config.RuntimeRoot != "" {
		args = append(args, "-runtime-root", config.RuntimeRoot)
	}
	if config.SystemdCgroup {
		args = append(args, "-systemd-cgroup")
	}
	if debug {
		args = append(args, "-debug")
	}

	cmd := exec.Command(binary, args...)
	cmd.Dir = config.Path
	// make sure the shim can be re-parented to system init
	// and is cloned in a new mount namespace because the overlay/filesystems
	// will be mounted by the shim
	cmd.SysProcAttr = getSysProcAttr()
	cmd.ExtraFiles = append(cmd.ExtraFiles, socket)
	cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
	if debug {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	return cmd, nil
}

func newSocket(address string) (*net.UnixListener, error) {
	if len(address) > 106 {
		return nil, errors.Errorf("%q: unix socket path too long (> 106)", address)
	}
	l, err := net.Listen("unix", "\x00"+address)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address)
	}

	return l.(*net.UnixListener), nil
}

func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) {
	return d(address, 100*time.Second)
}

func annonDialer(address string, timeout time.Duration) (net.Conn, error) {
	address = strings.TrimPrefix(address, "unix://")
	return net.DialTimeout("unix", "\x00"+address, timeout)
}

// WithConnect connects to an existing shim
func WithConnect(address string, onClose func()) Opt {
	return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
		conn, err := connect(address, annonDialer)
		if err != nil {
			return nil, nil, err
		}
		client := ttrpc.NewClient(conn)
		client.OnClose(onClose)
		return shimapi.NewShimClient(client), conn, nil
	}
}

// WithLocal uses an in process shim
func WithLocal(publisher events.Publisher) func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error) {
	return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
		service, err := shim.NewService(config, publisher)
		if err != nil {
			return nil, nil, err
		}
		return shim.NewLocal(service), nil, nil
	}
}

// New returns a new shim client
func New(ctx context.Context, config shim.Config, opt Opt) (*Client, error) {
	s, c, err := opt(ctx, config)
	if err != nil {
		return nil, err
	}
	return &Client{
		ShimService: s,
		c:           c,
		exitCh:      make(chan struct{}),
	}, nil
}

// Client is a shim client containing the connection to a shim
type Client struct {
	shimapi.ShimService

	c        io.Closer
	exitCh   chan struct{}
	exitOnce sync.Once
}

// IsAlive returns true if the shim can be contacted.
// NOTE: a negative answer doesn't mean that the process is gone.
func (c *Client) IsAlive(ctx context.Context) (bool, error) {
	_, err := c.ShimInfo(ctx, empty)
	if err != nil {
		// TODO(stevvooe): There are some error conditions that need to be
		// handle with unix sockets existence to give the right answer here.
		return false, err
	}
	return true, nil
}

// StopShim signals the shim to exit and wait for the process to disappear
func (c *Client) StopShim(ctx context.Context) error {
	return c.signalShim(ctx, unix.SIGTERM)
}

// KillShim kills the shim forcefully and wait for the process to disappear
func (c *Client) KillShim(ctx context.Context) error {
	return c.signalShim(ctx, unix.SIGKILL)
}

// Close the cient connection
func (c *Client) Close() error {
	if c.c == nil {
		return nil
	}
	return c.c.Close()
}

func (c *Client) signalShim(ctx context.Context, sig syscall.Signal) error {
	info, err := c.ShimInfo(ctx, empty)
	if err != nil {
		return err
	}
	pid := int(info.ShimPid)
	// make sure we don't kill ourselves if we are running a local shim
	if os.Getpid() == pid {
		return nil
	}
	if err := unix.Kill(pid, sig); err != nil && err != unix.ESRCH {
		return err
	}
	// wait for shim to die after being signaled
	select {
	case <-ctx.Done():
		return ctx.Err()
	case <-c.waitForExit(pid):
		return nil
	}
}

func (c *Client) waitForExit(pid int) <-chan struct{} {
	c.exitOnce.Do(func() {
		for {
			// use kill(pid, 0) here because the shim could have been reparented
			// and we are no longer able to waitpid(pid, ...) on the shim
			if err := unix.Kill(pid, 0); err == unix.ESRCH {
				close(c.exitCh)
				return
			}
			time.Sleep(10 * time.Millisecond)
		}
	})
	return c.exitCh
}
<|endoftext|>


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

package xsrf

import (
	"fmt"
	"github.com/google/go-safeweb/safehttp"

	// TODO(@empijei, @kele, @mattiasgrenfeldt, @mihalimara22): decide whether
	// we want to depend on this package or reimplement thefunctionality
	"golang.org/x/net/xsrftoken"
)

const (
	// TokenKey is the form key used when sending the token as part of POST
	// request.
	TokenKey = "xsrf-token"
)

// UserIDStorage stores the web application users' IDs,
// needed in generating the XSRF token.
type UserIDStorage interface {
	// GetUserID returns the ID of the user making the request.
	// TODO(@mihalimara22): add a *safehttp.IncomingRequest as a parameter to
	// this function once the method for this is exported.
	GetUserID() (string, error)
}

// Plugin implements XSRF protection.
// TODO(@mihalimara22): Add Fetch Metadata support
type Plugin struct {
	appKey  string
	storage UserIDStorage
}

// NewPlugin creates a new XSRF plugin. It requires an application key and a
// storage service. The appKey uniquely identifies each registered service and
// should have high entropy. The storage service supports retrieving ID's of the
// application's users. Both the appKey and user ID are used in the XSRF
// token generation algorithm.
func NewPlugin(appKey string, s UserIDStorage) *Plugin {
	return &Plugin{
		appKey:  appKey,
		storage: s,
	}
}

// GenerateToken generates a cryptographically safe XSRF token per user, using
// their ID and the request host and path.
func (p *Plugin) GenerateToken(host string, path string) (string, error) {
	userID, err := p.storage.GetUserID()
	if err != nil {
		return "", fmt.Errorf("couldn't retrive the user ID: %v", err)
	}
	return xsrftoken.Generate(p.appKey, userID, host+path), nil
}

// validateToken validates the XSRF token. This should be present in all
// requests as the value of form parameter xsrf-token.
func (p *Plugin) validateToken(r *safehttp.IncomingRequest) safehttp.StatusCode {
	userID, err := p.storage.GetUserID()
	if err != nil {
		return safehttp.StatusUnauthorized
	}
	f, err := r.PostForm()
	if err != nil {
		mf, err := r.MultipartForm(32 << 20)
		if err != nil {
			return safehttp.StatusBadRequest
		}
		f = &mf.Form
	}
	tok := f.String(TokenKey, "")
	if f.Err() != nil || tok == "" {
		return safehttp.StatusUnauthorized
	}
	if ok := xsrftoken.Valid(tok, p.appKey, userID, r.Host()+r.Path()); !ok {
		return safehttp.StatusForbidden
	}
	return 0
}

// Before should be executed before directing the request to the handler. The
// function applies checks to the Incoming Request to ensure this is not part
// of a Cross-Site Request Forgery.
func (p *Plugin) Before(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
	if status := p.validateToken(r); status != 0 {
		return w.ClientError(status)
	}
	return safehttp.Result{}
}
<commit_msg>Inline the validateToken function to be part of Before<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xsrf

import (
	"fmt"
	"github.com/google/go-safeweb/safehttp"

	// TODO(@empijei, @kele, @mattiasgrenfeldt, @mihalimara22): decide whether
	// we want to depend on this package or reimplement thefunctionality
	"golang.org/x/net/xsrftoken"
)

const (
	// TokenKey is the form key used when sending the token as part of POST
	// request.
	TokenKey = "xsrf-token"
)

// UserIDStorage stores the web application users' IDs,
// needed in generating the XSRF token.
type UserIDStorage interface {
	// GetUserID returns the ID of the user making the request.
	// TODO(@mihalimara22): add a *safehttp.IncomingRequest as a parameter to
	// this function once the method for this is exported.
	GetUserID() (string, error)
}

// Plugin implements XSRF protection.
// TODO(@mihalimara22): Add Fetch Metadata support
type Plugin struct {
	appKey  string
	storage UserIDStorage
}

// NewPlugin creates a new XSRF plugin. It requires an application key and a
// storage service. The appKey uniquely identifies each registered service and
// should have high entropy. The storage service supports retrieving ID's of the
// application's users. Both the appKey and user ID are used in the XSRF
// token generation algorithm.
func NewPlugin(appKey string, s UserIDStorage) *Plugin {
	return &Plugin{
		appKey:  appKey,
		storage: s,
	}
}

// GenerateToken generates a cryptographically safe XSRF token per user, using
// their ID and the request host and path.
func (p *Plugin) GenerateToken(host string, path string) (string, error) {
	userID, err := p.storage.GetUserID()
	if err != nil {
		return "", fmt.Errorf("couldn't retrive the user ID: %v", err)
	}
	return xsrftoken.Generate(p.appKey, userID, host+path), nil
}

// Before should be executed before directing the safehttp.IncomingRequest to
// the handler to ensure it is not part of the Cross Site Request
// Forgery. It checks for the presence of an xsrf-token in the request body and
// validates it based on the userID associated with the request.
func (p *Plugin) Before(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
	userID, err := p.storage.GetUserID()
	if err != nil {
		return w.ClientError(safehttp.StatusUnauthorized)
	}
	f, err := r.PostForm()
	if err != nil {
		mf, err := r.MultipartForm(32 << 20)
		if err != nil {
			return w.ClientError(safehttp.StatusBadRequest)
		}
		f = &mf.Form
	}
	tok := f.String(TokenKey, "")
	if f.Err() != nil || tok == "" {
		return w.ClientError(safehttp.StatusUnauthorized)
	}
	if ok := xsrftoken.Valid(tok, p.appKey, userID, r.Host()+r.Path()); !ok {
		return w.ClientError(safehttp.StatusForbidden)
	}
	return safehttp.Result{}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A simple tool for mounting sample file systems, used by the tests in
// samples/.
package main

import (
	"flag"
	"os"
)

var fType = flag.String(
	"type",
	"",
	"The name of the samples/ sub-dir to be mounted.")

var fFlushesFile = flag.String(
	"flushfs.flushes_file",
	"",
	"Path to a file to which flushes should be reported, \\n-separated.")

var fFsyncsFile = flag.String(
	"flushfs.fsyncs_file",
	"",
	"Path to a file to which fsyncs should be reported, \\n-separated.")

func main() {
	os.Exit(1)
}
<commit_msg>Sketched main.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A simple tool for mounting sample file systems, used by the tests in
// samples/.
package main

import (
	"flag"
	"log"

	"github.com/jacobsa/fuse"
	"golang.org/x/net/context"
)

var fType = flag.String("type", "", "The name of the samples/ sub-dir.")
var fMountPoint = flag.String("mount_point", "", "Path to mount point.")

var fFlushesFile = flag.String(
	"flushfs.flushes_file",
	"",
	"Path to a file to which flushes should be reported, \\n-separated.")

var fFsyncsFile = flag.String(
	"flushfs.fsyncs_file",
	"",
	"Path to a file to which fsyncs should be reported, \\n-separated.")

func makeFS() (fs fuse.FileSystem, err error)

func main() {
	flag.Parse()

	// Create an appropriate file system.
	fs, err := makeFS()
	if err != nil {
		log.Fatalf("makeFS: %v", err)
	}

	// Mount the file system.
	if *fMountPoint == "" {
		log.Fatalf("You must set --mount_point.")
	}

	mfs, err := fuse.Mount(*fMountPoint, fs, &fuse.MountConfig{})
	if err != nil {
		log.Fatalf("Mount: %v", err)
	}

	// Wait for it to be ready.
	if err = mfs.WaitForReady(context.Background()); err != nil {
		log.Fatalf("WaitForReady: %v", err)
	}

	// Wait for it to be unmounted.
	if err = mfs.Join(context.Background()); err != nil {
		log.Fatalf("Join: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright Datajin Technologies, Inc. 2015,2016. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"path"
	"time"

	"github.com/codegangsta/cli"
	"github.com/mktmpio/cli/commands"
)

// overriden at compile time (-ldflags "-X main.version=V main.commit=C")
var (
	version   = "0.0.0"
	commit    = "HEAD"
	buildtime = "0000-00-00T00:00:00Z"
	t, terr   = time.Parse("2006-01-02T15:04:05Z", buildtime)
)

const appHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.HelpName}}
   {{- if .Flags }} [global options] {{- end -}}
   {{- if .Commands}} command [command options] {{- end -}}
   {{- if .ArgsUsage}} {{.ArgsUsage}} {{- else }} [arguments...] {{- end}}

GLOBAL OPTIONS:
   {{range .Flags}}
     {{- .}}
   {{end}}
COMMANDS:
   {{range .Commands}}
     {{- join .Names ", "}}{{ "\t" }}{{.Usage}}
   {{end}}
BUGS:
   Report to https://github.com/mktmpio/cli/issues

VERSION:
   Version: {{.Version}}
   Compiled: {{.Compiled}}

COPYRIGHT:
   {{.Copyright}}
`

func mktmpioApp() *cli.App {
	// overrides for some variables exposed by codegangsta/cli
	cli.AppHelpTemplate = appHelpTemplate
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	return &cli.App{
		Name:         "mktmpio",
		HelpName:     path.Base(os.Args[0]),
		Usage:        "create, destroy, and manage mktmpio database servers",
		Version:      version,
		Compiled:     t,
		Copyright:    "Copyright Datajin Technologies, Inc. 2015,2016. All rights reserved.",
		BashComplete: cli.DefaultAppComplete,
		Action:       commands.ShellCommand.Action,
		Before:       commands.PopulateConfig,
		Writer:       os.Stdout,
		Commands: []cli.Command{
			commands.ConfigCommand,
			commands.ListCommand,
			commands.RemoveCommand,
			commands.ShellCommand,
			commands.LegalCommand,
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:   "debug, d",
				Usage:  "Enable extra verbose logging",
				EnvVar: "MKTMPIO_DEBUG",
			},
			cli.StringFlag{
				Name:   "token",
				Usage:  "API token for making requests to mktmpio service",
				EnvVar: "MKTMPIO_TOKEN",
				Value:  "TOKEN",
			},
			cli.StringFlag{
				Name:   "url",
				Usage:  "override the URL for the mktmpio service",
				EnvVar: "MKTMPIO_URL",
				Value:  "URL",
			},
		},
		Authors: []cli.Author{
			{Name: "Ryan Graham", Email: "mktmpio@datajin.com"},
		},
	}
}

func main() {
	mktmpioApp().RunAndExitOnError()
}
<commit_msg>include compilation details in version string<commit_after>// Copyright Datajin Technologies, Inc. 2015,2016. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"path"
	"runtime"
	"time"

	"github.com/codegangsta/cli"
	"github.com/mktmpio/cli/commands"
)

// overriden at compile time (-ldflags "-X main.version=V main.commit=C")
var (
	version   = "0.0.0"
	commit    = "HEAD"
	buildtime = "0000-00-00T00:00:00Z"
	t, terr   = time.Parse("2006-01-02T15:04:05Z", buildtime)
)

const appHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.HelpName}}
   {{- if .Flags }} [global options] {{- end -}}
   {{- if .Commands}} command [command options] {{- end -}}
   {{- if .ArgsUsage}} {{.ArgsUsage}} {{- else }} [arguments...] {{- end}}

GLOBAL OPTIONS:
   {{range .Flags}}
     {{- .}}
   {{end}}
COMMANDS:
   {{range .Commands}}
     {{- join .Names ", "}}{{ "\t" }}{{.Usage}}
   {{end}}
BUGS:
   Report to https://github.com/mktmpio/cli/issues

VERSION:
   Version: {{.Version}}
   Compiled: {{.Compiled}}

COPYRIGHT:
   {{.Copyright}}
`

func mktmpioApp() *cli.App {
	// overrides for some variables exposed by codegangsta/cli
	cli.AppHelpTemplate = appHelpTemplate
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	return &cli.App{
		Name:         "mktmpio",
		HelpName:     path.Base(os.Args[0]),
		Usage:        "create, destroy, and manage mktmpio database servers",
		Version:      version + " (built with " + runtime.Compiler + ", " + runtime.Version() + ")",
		Compiled:     t,
		Copyright:    "Copyright Datajin Technologies, Inc. 2015,2016. All rights reserved.",
		BashComplete: cli.DefaultAppComplete,
		Action:       commands.ShellCommand.Action,
		Before:       commands.PopulateConfig,
		Writer:       os.Stdout,
		Commands: []cli.Command{
			commands.ConfigCommand,
			commands.ListCommand,
			commands.RemoveCommand,
			commands.ShellCommand,
			commands.LegalCommand,
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:   "debug, d",
				Usage:  "Enable extra verbose logging",
				EnvVar: "MKTMPIO_DEBUG",
			},
			cli.StringFlag{
				Name:   "token",
				Usage:  "API token for making requests to mktmpio service",
				EnvVar: "MKTMPIO_TOKEN",
				Value:  "TOKEN",
			},
			cli.StringFlag{
				Name:   "url",
				Usage:  "override the URL for the mktmpio service",
				EnvVar: "MKTMPIO_URL",
				Value:  "URL",
			},
		},
		Authors: []cli.Author{
			{Name: "Ryan Graham", Email: "mktmpio@datajin.com"},
		},
	}
}

func main() {
	mktmpioApp().RunAndExitOnError()
}
<|endoftext|>


<commit_before>package pixel

import (
	"github.com/faiface/pixel/pixelgl"
	"github.com/go-gl/glfw/v3.2/glfw"
)

// Monitor represents a physical display attached to your computer.
type Monitor struct {
	monitor *glfw.Monitor
}

// PrimaryMonitor returns the main monitor (usually the one with the taskbar and stuff).
func PrimaryMonitor() *Monitor {
	m := &Monitor{}
	pixelgl.Do(func() {
		m.monitor = glfw.GetPrimaryMonitor()
	})
	return m
}

// Monitors returns a slice of all currently attached monitors.
func Monitors() []*Monitor {
	var monitors []*Monitor
	pixelgl.Do(func() {
		for _, monitor := range glfw.GetMonitors() {
			monitors = append(monitors, &Monitor{monitor: monitor})
		}
	})
	return monitors
}

// Name returns a human-readable name of a monitor.
func (m *Monitor) Name() string {
	return pixelgl.DoVal(func() interface{} {
		return m.monitor.GetName()
	}).(string)
}

// PhysicalSize returns the size of the display are of a monitor in millimeters.
func (m *Monitor) PhysicalSize() (width, height float64) {
	var w, h float64
	pixelgl.Do(func() {
		wi, hi := m.monitor.GetPhysicalSize()
		w = float64(wi)
		h = float64(hi)
	})
	return w, h
}

// Position returns the position of the upper-left corner of a monitor in screen coordinates.
func (m *Monitor) Position() (x, y float64) {
	pixelgl.Do(func() {
		xi, yi := m.monitor.GetPos()
		x = float64(xi)
		y = float64(yi)
	})
	return x, y
}

// Size returns the resolution of a monitor in pixels.
func (m *Monitor) Size() (width, height float64) {
	var w, h float64
	pixelgl.Do(func() {
		mode := m.monitor.GetVideoMode()
		w = float64(mode.Width)
		h = float64(mode.Height)
	})
	return w, h
}

// BitDepth returns the number of bits per color of a monitor.
func (m *Monitor) BitDepth() (red, green, blue int) {
	var r, g, b int
	pixelgl.Do(func() {
		mode := m.monitor.GetVideoMode()
		r = mode.RedBits
		g = mode.GreenBits
		b = mode.BlueBits
	})
	return r, g, b
}

// RefreshRate returns the refresh frequency of a monitor in Hz (refreshes/second).
func (m *Monitor) RefreshRate() float64 {
	var rate float64
	pixelgl.Do(func() {
		mode := m.monitor.GetVideoMode()
		rate = float64(mode.RefreshRate)
	})
	return rate
}
<commit_msg>remove pixegl.Do from monitor stuff (unnecessary)<commit_after>package pixel

import "github.com/go-gl/glfw/v3.2/glfw"

// Monitor represents a physical display attached to your computer.
type Monitor struct {
	monitor *glfw.Monitor
}

// PrimaryMonitor returns the main monitor (usually the one with the taskbar and stuff).
func PrimaryMonitor() *Monitor {
	return &Monitor{
		monitor: glfw.GetPrimaryMonitor(),
	}
}

// Monitors returns a slice of all currently attached monitors.
func Monitors() []*Monitor {
	var monitors []*Monitor
	for _, monitor := range glfw.GetMonitors() {
		monitors = append(monitors, &Monitor{monitor: monitor})
	}
	return monitors
}

// Name returns a human-readable name of a monitor.
func (m *Monitor) Name() string {
	return m.monitor.GetName()
}

// PhysicalSize returns the size of the display are of a monitor in millimeters.
func (m *Monitor) PhysicalSize() (width, height float64) {
	wi, hi := m.monitor.GetPhysicalSize()
	width = float64(wi)
	height = float64(hi)
	return
}

// Position returns the position of the upper-left corner of a monitor in screen coordinates.
func (m *Monitor) Position() (x, y float64) {
	xi, yi := m.monitor.GetPos()
	x = float64(xi)
	y = float64(yi)
	return
}

// Size returns the resolution of a monitor in pixels.
func (m *Monitor) Size() (width, height float64) {
	mode := m.monitor.GetVideoMode()
	width = float64(mode.Width)
	height = float64(mode.Height)
	return
}

// BitDepth returns the number of bits per color of a monitor.
func (m *Monitor) BitDepth() (red, green, blue int) {
	mode := m.monitor.GetVideoMode()
	red = mode.RedBits
	green = mode.GreenBits
	blue = mode.BlueBits
	return
}

// RefreshRate returns the refresh frequency of a monitor in Hz (refreshes/second).
func (m *Monitor) RefreshRate() (rate float64) {
	mode := m.monitor.GetVideoMode()
	rate = float64(mode.RefreshRate)
	return
}
<|endoftext|>


<commit_before>// Copyright 2016 laosj Author @songtianyi. All Rights Reserved.
//
// 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 main

import (
	"github.com/songtianyi/laosj/downloader"
	"github.com/songtianyi/laosj/spider"
	"github.com/songtianyi/laosj/storage"
	"github.com/songtianyi/rrframework/connector/redis"
	"github.com/songtianyi/rrframework/logs"
	"regexp"
	"strconv"
	"sync"
)

func main() {
	d := &downloader.Downloader{
		ConcurrencyLimit: 10,
		UrlChannelFactor: 10,
		RedisConnStr:     "127.0.0.1:6379",
		SourceQueue:      "DATA:IMAGE:MZITU:XINGGAN",
		Store:            storage.NewLocalDiskStorage("/data/sexx/mzituzp/"),
	}
	go func() {
		d.Start()
	}()

	// step1: find total index pages
	s := &spider.Spider{
		IndexUrl: "http://www.mzitu.com/share",
		Rules: []string{
			"div.main>div.main-content>div.postlist>div>div.pagenavi-cm>a",
		},
		LeafType: spider.TEXT_LEAF,
	}
	rs, err := s.Run()
	if err != nil {
		logs.Error(err)
		return
	}
	max := spider.FindMaxFromSliceString(1, rs)

	// step2: for every index page, find every post entrance
	var wg sync.WaitGroup
	var mu sync.Mutex
	step2 := make([]string, 0)
	for i := 1; i <= max; i++ {
		wg.Add(1)
		go func(ix int) {
			defer wg.Done()
			ns := &spider.Spider{
				IndexUrl: s.IndexUrl + "/comment-page-" + strconv.Itoa(ix) + "#comments/",
				Rules: []string{
					"div.main>div.main-content>div.postlist>div>ul>li>div>p",
				},
				LeafType: spider.HTML_LEAF,
			}
			t, err := ns.Run()
			if err != nil {
				logs.Error(err)
				return
			}
			mu.Lock()
			step2 = append(step2, t...)
			mu.Unlock()
		}(i)
	}
	wg.Wait()
	err, rc := rrredis.GetRedisClient(d.RedisConnStr)
	if err != nil {
		logs.Error(err)
		return
	}
	// parse url
	for _, v := range step2 {
		re := regexp.MustCompile("src=\"(\\S+)\"")
		url := re.FindStringSubmatch(v)[1]
		key := d.SourceQueue
		if _, err := rc.RPush(key, url); err != nil {
			logs.Error(err)
			return
		}
	}
	d.WaitCloser()
}
<commit_msg>fix some comments<commit_after>// Copyright 2016 laosj Author @songtianyi. All Rights Reserved.
//
// 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 main

import (
	"github.com/songtianyi/laosj/downloader"
	"github.com/songtianyi/laosj/spider"
	"github.com/songtianyi/laosj/storage"
	"github.com/songtianyi/rrframework/connector/redis"
	"github.com/songtianyi/rrframework/logs"
	"regexp"
	"strconv"
	"sync"
)

func main() {
	d := &downloader.Downloader{
		ConcurrencyLimit: 10,
		UrlChannelFactor: 10,
		RedisConnStr:     "127.0.0.1:6379",
		SourceQueue:      "DATA:IMAGE:MZITU:XINGGAN",
		Store:            storage.NewLocalDiskStorage("/data/sexx/mzituzp/"),
	}
	go func() {
		d.Start()
	}()

	// step1: find total pages
	s := &spider.Spider{
		IndexUrl: "http://www.mzitu.com/share",
		Rules: []string{
			"div.main>div.main-content>div.postlist>div>div.pagenavi-cm>a",
		},
		LeafType: spider.TEXT_LEAF,
	}
	rs, err := s.Run()
	if err != nil {
		logs.Error(err)
		return
	}
	max := spider.FindMaxFromSliceString(1, rs)

	// step2: for every page, find all img tags
	var wg sync.WaitGroup
	var mu sync.Mutex
	step2 := make([]string, 0)
	for i := 1; i <= max; i++ {
		wg.Add(1)
		go func(ix int) {
			defer wg.Done()
			ns := &spider.Spider{
				IndexUrl: s.IndexUrl + "/comment-page-" + strconv.Itoa(ix) + "#comments/",
				Rules: []string{
					"div.main>div.main-content>div.postlist>div>ul>li>div>p",
				},
				LeafType: spider.HTML_LEAF,
			}
			t, err := ns.Run()
			if err != nil {
				logs.Error(err)
				return
			}
			mu.Lock()
			step2 = append(step2, t...)
			mu.Unlock()
		}(i)
	}
	wg.Wait()
	err, rc := rrredis.GetRedisClient(d.RedisConnStr)
	if err != nil {
		logs.Error(err)
		return
	}
	// parse url
	for _, v := range step2 {
		re := regexp.MustCompile("src=\"(\\S+)\"")
		url := re.FindStringSubmatch(v)[1]
		key := d.SourceQueue
		if _, err := rc.RPush(key, url); err != nil {
			logs.Error(err)
			return
		}
	}
	d.WaitCloser()
}
<|endoftext|>


<commit_before>// Copyright 2013 The Prometheus 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 rules

import (
	"fmt"
	"html/template"
	"sync"
	"time"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/util/strutil"
)

const (
	// AlertMetricName is the metric name for synthetic alert timeseries.
	alertMetricName model.LabelValue = "ALERTS"

	// AlertNameLabel is the label name indicating the name of an alert.
	alertNameLabel model.LabelName = "alertname"
	// AlertStateLabel is the label name indicating the state of an alert.
	alertStateLabel model.LabelName = "alertstate"
)

// AlertState denotes the state of an active alert.
type AlertState int

const (
	// StateInactive is the state of an alert that is neither firing nor pending.
	StateInactive AlertState = iota
	// StatePending is the state of an alert that has been active for less than
	// the configured threshold duration.
	StatePending
	// StateFiring is the state of an alert that has been active for longer than
	// the configured threshold duration.
	StateFiring
)

func (s AlertState) String() string {
	switch s {
	case StateInactive:
		return "inactive"
	case StatePending:
		return "pending"
	case StateFiring:
		return "firing"
	}
	panic(fmt.Errorf("unknown alert state: %v", s.String()))
}

// Alert is the user-level representation of a single instance of an alerting rule.
type Alert struct {
	State  AlertState
	Labels model.LabelSet
	// The value at the last evaluation of the alerting expression.
	Value model.SampleValue
	// The interval during which the condition of this alert held true.
	// ResolvedAt will be 0 to indicate a still active alert.
	ActiveAt, ResolvedAt model.Time
}

// An AlertingRule generates alerts from its vector expression.
type AlertingRule struct {
	// The name of the alert.
	name string
	// The vector expression from which to generate alerts.
	vector promql.Expr
	// The duration for which a labelset needs to persist in the expression
	// output vector before an alert transitions from Pending to Firing state.
	holdDuration time.Duration
	// Extra labels to attach to the resulting alert sample vectors.
	labels model.LabelSet
	// Non-identifying key/value pairs.
	annotations model.LabelSet

	// Protects the below.
	mtx sync.Mutex
	// A map of alerts which are currently active (Pending or Firing), keyed by
	// the fingerprint of the labelset they correspond to.
	active map[model.Fingerprint]*Alert
}

// NewAlertingRule constructs a new AlertingRule.
func NewAlertingRule(name string, vec promql.Expr, hold time.Duration, lbls, anns model.LabelSet) *AlertingRule {
	return &AlertingRule{
		name:         name,
		vector:       vec,
		holdDuration: hold,
		labels:       lbls,
		annotations:  anns,
		active:       map[model.Fingerprint]*Alert{},
	}
}

// Name returns the name of the alert.
func (rule *AlertingRule) Name() string {
	return rule.name
}

func (r *AlertingRule) equal(o *AlertingRule) bool {
	return r.name == o.name && r.labels.Equal(o.labels)
}

func (r *AlertingRule) sample(alert *Alert, ts model.Time, set bool) *model.Sample {
	metric := model.Metric(r.labels.Clone())

	for ln, lv := range alert.Labels {
		metric[ln] = lv
	}

	metric[model.MetricNameLabel] = alertMetricName
	metric[model.AlertNameLabel] = model.LabelValue(r.name)
	metric[alertStateLabel] = model.LabelValue(alert.State.String())

	s := &model.Sample{
		Metric:    metric,
		Timestamp: ts,
		Value:     0,
	}
	if set {
		s.Value = 1
	}
	return s
}

// resolvedRetention is the duration for which a resolved alert instance
// is kept in memory state and consequentally repeatedly sent to the AlertManager.
const resolvedRetention = 15 * time.Minute

// eval evaluates the rule expression and then creates pending alerts and fires
// or removes previously pending alerts accordingly.
func (r *AlertingRule) eval(ts model.Time, engine *promql.Engine) (model.Vector, error) {
	query, err := engine.NewInstantQuery(r.vector.String(), ts)
	if err != nil {
		return nil, err
	}
	res, err := query.Exec().Vector()
	if err != nil {
		return nil, err
	}

	r.mtx.Lock()
	defer r.mtx.Unlock()

	// Create pending alerts for any new vector elements in the alert expression
	// or update the expression value for existing elements.
	resultFPs := map[model.Fingerprint]struct{}{}

	for _, smpl := range res {
		fp := smpl.Metric.Fingerprint()
		resultFPs[fp] = struct{}{}

		if alert, ok := r.active[fp]; ok && alert.State != StateInactive {
			alert.Value = smpl.Value
			continue
		}

		delete(smpl.Metric, model.MetricNameLabel)

		r.active[fp] = &Alert{
			Labels:   model.LabelSet(smpl.Metric),
			ActiveAt: ts,
			State:    StatePending,
			Value:    smpl.Value,
		}
	}

	var vec model.Vector
	// Check if any pending alerts should be removed or fire now. Write out alert timeseries.
	for fp, a := range r.active {
		if _, ok := resultFPs[fp]; !ok {
			if a.State != StateInactive {
				vec = append(vec, r.sample(a, ts, false))
			}
			// If the alert was previously firing, keep it around for a given
			// retention time so it is reported as resolved to the AlertManager.
			if a.State == StatePending || (a.ResolvedAt != 0 && ts.Sub(a.ResolvedAt) > resolvedRetention) {
				delete(r.active, fp)
			}
			if a.State != StateInactive {
				a.State = StateInactive
				a.ResolvedAt = ts
			}
			continue
		}

		if a.State == StatePending && ts.Sub(a.ActiveAt) >= r.holdDuration {
			vec = append(vec, r.sample(a, ts, false))
			a.State = StateFiring
		}

		vec = append(vec, r.sample(a, ts, true))
	}

	return vec, nil
}

// State returns the maximum state of alert instances for this rule.
// StateFiring > StatePending > StateInactive
func (r *AlertingRule) State() AlertState {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	maxState := StateInactive
	for _, a := range r.active {
		if a.State > maxState {
			maxState = a.State
		}
	}
	return maxState
}

// ActiveAlerts returns a slice of active alerts.
func (r *AlertingRule) ActiveAlerts() []*Alert {
	var res []*Alert
	for _, a := range r.currentAlerts() {
		if a.ResolvedAt == 0 {
			res = append(res, a)
		}
	}
	return res
}

// currentAlerts returns all instances of alerts for this rule. This may include
// inactive alerts that were previously firing.
func (r *AlertingRule) currentAlerts() []*Alert {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	alerts := make([]*Alert, 0, len(r.active))

	for _, a := range r.active {
		labels := r.labels.Clone()
		for ln, lv := range a.Labels {
			labels[ln] = lv
		}
		anew := *a
		anew.Labels = labels

		alerts = append(alerts, &anew)
	}
	return alerts
}

func (rule *AlertingRule) String() string {
	s := fmt.Sprintf("ALERT %s", rule.name)
	s += fmt.Sprintf("\n\tIF %s", rule.vector)
	if rule.holdDuration > 0 {
		s += fmt.Sprintf("\n\tFOR %s", model.Duration(rule.holdDuration))
	}
	if len(rule.labels) > 0 {
		s += fmt.Sprintf("\n\tLABELS %s", rule.labels)
	}
	if len(rule.annotations) > 0 {
		s += fmt.Sprintf("\n\tANNOTATIONS %s", rule.annotations)
	}
	return s
}

// HTMLSnippet returns an HTML snippet representing this alerting rule. The
// resulting snippet is expected to be presented in a
 element, so that
// line breaks and other returned whitespace is respected.
func (rule *AlertingRule) HTMLSnippet(pathPrefix string) template.HTML {
	alertMetric := model.Metric{
		model.MetricNameLabel: alertMetricName,
		alertNameLabel:        model.LabelValue(rule.name),
	}
	s := fmt.Sprintf("ALERT %s", pathPrefix+strutil.GraphLinkForExpression(alertMetric.String()), rule.name)
	s += fmt.Sprintf("\n  IF %s", pathPrefix+strutil.GraphLinkForExpression(rule.vector.String()), rule.vector)
	if rule.holdDuration > 0 {
		s += fmt.Sprintf("\n  FOR %s", model.Duration(rule.holdDuration))
	}
	if len(rule.labels) > 0 {
		s += fmt.Sprintf("\n  LABELS %s", rule.labels)
	}
	if len(rule.annotations) > 0 {
		s += fmt.Sprintf("\n  ANNOTATIONS %s", rule.annotations)
	}
	return template.HTML(s)
}
<commit_msg>Fix style issues in rules/...<commit_after>// Copyright 2013 The Prometheus 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 rules

import (
	"fmt"
	"html/template"
	"sync"
	"time"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/util/strutil"
)

const (
	// AlertMetricName is the metric name for synthetic alert timeseries.
	alertMetricName model.LabelValue = "ALERTS"

	// AlertNameLabel is the label name indicating the name of an alert.
	alertNameLabel model.LabelName = "alertname"
	// AlertStateLabel is the label name indicating the state of an alert.
	alertStateLabel model.LabelName = "alertstate"
)

// AlertState denotes the state of an active alert.
type AlertState int

const (
	// StateInactive is the state of an alert that is neither firing nor pending.
	StateInactive AlertState = iota
	// StatePending is the state of an alert that has been active for less than
	// the configured threshold duration.
	StatePending
	// StateFiring is the state of an alert that has been active for longer than
	// the configured threshold duration.
	StateFiring
)

func (s AlertState) String() string {
	switch s {
	case StateInactive:
		return "inactive"
	case StatePending:
		return "pending"
	case StateFiring:
		return "firing"
	}
	panic(fmt.Errorf("unknown alert state: %v", s.String()))
}

// Alert is the user-level representation of a single instance of an alerting rule.
type Alert struct {
	State  AlertState
	Labels model.LabelSet
	// The value at the last evaluation of the alerting expression.
	Value model.SampleValue
	// The interval during which the condition of this alert held true.
	// ResolvedAt will be 0 to indicate a still active alert.
	ActiveAt, ResolvedAt model.Time
}

// An AlertingRule generates alerts from its vector expression.
type AlertingRule struct {
	// The name of the alert.
	name string
	// The vector expression from which to generate alerts.
	vector promql.Expr
	// The duration for which a labelset needs to persist in the expression
	// output vector before an alert transitions from Pending to Firing state.
	holdDuration time.Duration
	// Extra labels to attach to the resulting alert sample vectors.
	labels model.LabelSet
	// Non-identifying key/value pairs.
	annotations model.LabelSet

	// Protects the below.
	mtx sync.Mutex
	// A map of alerts which are currently active (Pending or Firing), keyed by
	// the fingerprint of the labelset they correspond to.
	active map[model.Fingerprint]*Alert
}

// NewAlertingRule constructs a new AlertingRule.
func NewAlertingRule(name string, vec promql.Expr, hold time.Duration, lbls, anns model.LabelSet) *AlertingRule {
	return &AlertingRule{
		name:         name,
		vector:       vec,
		holdDuration: hold,
		labels:       lbls,
		annotations:  anns,
		active:       map[model.Fingerprint]*Alert{},
	}
}

// Name returns the name of the alert.
func (r *AlertingRule) Name() string {
	return r.name
}

func (r *AlertingRule) equal(o *AlertingRule) bool {
	return r.name == o.name && r.labels.Equal(o.labels)
}

func (r *AlertingRule) sample(alert *Alert, ts model.Time, set bool) *model.Sample {
	metric := model.Metric(r.labels.Clone())

	for ln, lv := range alert.Labels {
		metric[ln] = lv
	}

	metric[model.MetricNameLabel] = alertMetricName
	metric[model.AlertNameLabel] = model.LabelValue(r.name)
	metric[alertStateLabel] = model.LabelValue(alert.State.String())

	s := &model.Sample{
		Metric:    metric,
		Timestamp: ts,
		Value:     0,
	}
	if set {
		s.Value = 1
	}
	return s
}

// resolvedRetention is the duration for which a resolved alert instance
// is kept in memory state and consequentally repeatedly sent to the AlertManager.
const resolvedRetention = 15 * time.Minute

// eval evaluates the rule expression and then creates pending alerts and fires
// or removes previously pending alerts accordingly.
func (r *AlertingRule) eval(ts model.Time, engine *promql.Engine) (model.Vector, error) {
	query, err := engine.NewInstantQuery(r.vector.String(), ts)
	if err != nil {
		return nil, err
	}
	res, err := query.Exec().Vector()
	if err != nil {
		return nil, err
	}

	r.mtx.Lock()
	defer r.mtx.Unlock()

	// Create pending alerts for any new vector elements in the alert expression
	// or update the expression value for existing elements.
	resultFPs := map[model.Fingerprint]struct{}{}

	for _, smpl := range res {
		fp := smpl.Metric.Fingerprint()
		resultFPs[fp] = struct{}{}

		if alert, ok := r.active[fp]; ok && alert.State != StateInactive {
			alert.Value = smpl.Value
			continue
		}

		delete(smpl.Metric, model.MetricNameLabel)

		r.active[fp] = &Alert{
			Labels:   model.LabelSet(smpl.Metric),
			ActiveAt: ts,
			State:    StatePending,
			Value:    smpl.Value,
		}
	}

	var vec model.Vector
	// Check if any pending alerts should be removed or fire now. Write out alert timeseries.
	for fp, a := range r.active {
		if _, ok := resultFPs[fp]; !ok {
			if a.State != StateInactive {
				vec = append(vec, r.sample(a, ts, false))
			}
			// If the alert was previously firing, keep it around for a given
			// retention time so it is reported as resolved to the AlertManager.
			if a.State == StatePending || (a.ResolvedAt != 0 && ts.Sub(a.ResolvedAt) > resolvedRetention) {
				delete(r.active, fp)
			}
			if a.State != StateInactive {
				a.State = StateInactive
				a.ResolvedAt = ts
			}
			continue
		}

		if a.State == StatePending && ts.Sub(a.ActiveAt) >= r.holdDuration {
			vec = append(vec, r.sample(a, ts, false))
			a.State = StateFiring
		}

		vec = append(vec, r.sample(a, ts, true))
	}

	return vec, nil
}

// State returns the maximum state of alert instances for this rule.
// StateFiring > StatePending > StateInactive
func (r *AlertingRule) State() AlertState {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	maxState := StateInactive
	for _, a := range r.active {
		if a.State > maxState {
			maxState = a.State
		}
	}
	return maxState
}

// ActiveAlerts returns a slice of active alerts.
func (r *AlertingRule) ActiveAlerts() []*Alert {
	var res []*Alert
	for _, a := range r.currentAlerts() {
		if a.ResolvedAt == 0 {
			res = append(res, a)
		}
	}
	return res
}

// currentAlerts returns all instances of alerts for this rule. This may include
// inactive alerts that were previously firing.
func (r *AlertingRule) currentAlerts() []*Alert {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	alerts := make([]*Alert, 0, len(r.active))

	for _, a := range r.active {
		labels := r.labels.Clone()
		for ln, lv := range a.Labels {
			labels[ln] = lv
		}
		anew := *a
		anew.Labels = labels

		alerts = append(alerts, &anew)
	}
	return alerts
}

func (r *AlertingRule) String() string {
	s := fmt.Sprintf("ALERT %s", r.name)
	s += fmt.Sprintf("\n\tIF %s", r.vector)
	if r.holdDuration > 0 {
		s += fmt.Sprintf("\n\tFOR %s", model.Duration(r.holdDuration))
	}
	if len(r.labels) > 0 {
		s += fmt.Sprintf("\n\tLABELS %s", r.labels)
	}
	if len(r.annotations) > 0 {
		s += fmt.Sprintf("\n\tANNOTATIONS %s", r.annotations)
	}
	return s
}

// HTMLSnippet returns an HTML snippet representing this alerting rule. The
// resulting snippet is expected to be presented in a
 element, so that
// line breaks and other returned whitespace is respected.
func (r *AlertingRule) HTMLSnippet(pathPrefix string) template.HTML {
	alertMetric := model.Metric{
		model.MetricNameLabel: alertMetricName,
		alertNameLabel:        model.LabelValue(r.name),
	}
	s := fmt.Sprintf("ALERT %s", pathPrefix+strutil.GraphLinkForExpression(alertMetric.String()), r.name)
	s += fmt.Sprintf("\n  IF %s", pathPrefix+strutil.GraphLinkForExpression(r.vector.String()), r.vector)
	if r.holdDuration > 0 {
		s += fmt.Sprintf("\n  FOR %s", model.Duration(r.holdDuration))
	}
	if len(r.labels) > 0 {
		s += fmt.Sprintf("\n  LABELS %s", r.labels)
	}
	if len(r.annotations) > 0 {
		s += fmt.Sprintf("\n  ANNOTATIONS %s", r.annotations)
	}
	return template.HTML(s)
}
<|endoftext|>


<commit_before>package envconfig_test

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/vrischmann/envconfig"
)

func TestParseSimpleConfig(t *testing.T) {
	var conf struct {
		Name string
		Log  struct {
			Path string
		}
	}

	// Go 1.2 and 1.3 don't have os.Unsetenv
	os.Setenv("NAME", "")
	os.Setenv("LOG_PATH", "")

	err := envconfig.Init(&conf)
	equals(t, "envconfig: key NAME not found", err.Error())

	os.Setenv("NAME", "foobar")
	err = envconfig.Init(&conf)
	equals(t, "envconfig: key LOG_PATH not found", err.Error())

	os.Setenv("LOG_PATH", "/var/log/foobar")
	err = envconfig.Init(&conf)
	ok(t, err)

	equals(t, "foobar", conf.Name)
	equals(t, "/var/log/foobar", conf.Log.Path)
}

func TestParseIntegerConfig(t *testing.T) {
	var conf struct {
		Port    int
		Long    uint64
		Version uint8
	}

	timestamp := time.Now().UnixNano()

	os.Setenv("PORT", "80")
	os.Setenv("LONG", fmt.Sprintf("%d", timestamp))
	os.Setenv("VERSION", "2")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, 80, conf.Port)
	equals(t, uint64(timestamp), conf.Long)
	equals(t, uint8(2), conf.Version)
}

func TestParseBoolConfig(t *testing.T) {
	var conf struct {
		DoIt bool
	}

	os.Setenv("DOIT", "true")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, true, conf.DoIt)
}

func TestParseFloatConfig(t *testing.T) {
	var conf struct {
		Delta  float32
		DeltaV float64
	}

	os.Setenv("DELTA", "0.02")
	os.Setenv("DELTAV", "400.20000000001")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, float32(0.02), conf.Delta)
	equals(t, float64(400.20000000001), conf.DeltaV)
}

func TestParseSliceConfig(t *testing.T) {
	var conf struct {
		Names  []string
		Ports  []int
		Shards []struct {
			Name string
			Addr string
		}
	}

	os.Setenv("NAMES", "foobar,barbaz")
	os.Setenv("PORTS", "900,100")
	os.Setenv("SHARDS", "{foobar,localhost:2929},{barbaz,localhost:2828}")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, 2, len(conf.Names))
	equals(t, "foobar", conf.Names[0])
	equals(t, "barbaz", conf.Names[1])
	equals(t, 2, len(conf.Ports))
	equals(t, 900, conf.Ports[0])
	equals(t, 100, conf.Ports[1])
	equals(t, 2, len(conf.Shards))
	equals(t, "foobar", conf.Shards[0].Name)
	equals(t, "localhost:2929", conf.Shards[0].Addr)
	equals(t, "barbaz", conf.Shards[1].Name)
	equals(t, "localhost:2828", conf.Shards[1].Addr)
}

func TestDurationConfig(t *testing.T) {
	var conf struct {
		Timeout time.Duration
	}

	os.Setenv("TIMEOUT", "1m")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, time.Minute*1, conf.Timeout)
}

func TestInvalidDurationConfig(t *testing.T) {
	var conf struct {
		Timeout time.Duration
	}

	os.Setenv("TIMEOUT", "foo")

	err := envconfig.Init(&conf)
	assert(t, err != nil, "err should not be nil")
}

func TestAllPointerConfig(t *testing.T) {
	var conf struct {
		Name   *string
		Port   *int
		Delta  *float32
		DeltaV *float64
		Hosts  *[]string
		Shards *[]*struct {
			Name *string
			Addr *string
		}
		Master *struct {
			Name *string
			Addr *string
		}
		Timeout *time.Duration
	}

	os.Setenv("NAME", "foobar")
	os.Setenv("PORT", "9000")
	os.Setenv("DELTA", "40.01")
	os.Setenv("DELTAV", "200.00001")
	os.Setenv("HOSTS", "localhost,free.fr")
	os.Setenv("SHARDS", "{foobar,localhost:2828},{barbaz,localhost:2929}")
	os.Setenv("MASTER_NAME", "master")
	os.Setenv("MASTER_ADDR", "localhost:2727")
	os.Setenv("TIMEOUT", "1m")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, "foobar", *conf.Name)
	equals(t, 9000, *conf.Port)
	equals(t, float32(40.01), *conf.Delta)
	equals(t, 200.00001, *conf.DeltaV)
	equals(t, 2, len(*conf.Hosts))
	equals(t, "localhost", (*conf.Hosts)[0])
	equals(t, "free.fr", (*conf.Hosts)[1])
	equals(t, 2, len(*conf.Shards))
	equals(t, "foobar", *(*conf.Shards)[0].Name)
	equals(t, "localhost:2828", *(*conf.Shards)[0].Addr)
	equals(t, "barbaz", *(*conf.Shards)[1].Name)
	equals(t, "localhost:2929", *(*conf.Shards)[1].Addr)
	equals(t, "master", *conf.Master.Name)
	equals(t, "localhost:2727", *conf.Master.Addr)
	equals(t, time.Minute*1, *conf.Timeout)
}

type logMode uint

const (
	logFile logMode = iota + 1
	logStdout
)

func (m *logMode) Unmarshal(s string) error {
	switch strings.ToLower(s) {
	case "file":
		*m = logFile
	case "stdout":
		*m = logStdout
	default:
		return fmt.Errorf("unable to unmarshal %s", s)
	}

	return nil
}

func TestUnmarshaler(t *testing.T) {
	var conf struct {
		LogMode logMode
	}

	os.Setenv("LOGMODE", "file")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, logFile, conf.LogMode)
}

func TestParseOptionalConfig(t *testing.T) {
	var conf struct {
		Name    string        `envconfig:"optional"`
		Flag    bool          `envconfig:"optional"`
		Timeout time.Duration `envconfig:"optional"`
		Port    int           `envconfig:"optional"`
		Port2   uint          `envconfig:"optional"`
		Delta   float32       `envconfig:"optional"`
		DeltaV  float64       `envconfig:"optional"`
		Slice   []string      `envconfig:"optional"`
		Struct  struct {
			A string
			B int
		} `envconfig:"optional"`
	}

	os.Setenv("NAME", "")
	os.Setenv("FLAG", "")
	os.Setenv("TIMEOUT", "")
	os.Setenv("PORT", "")
	os.Setenv("PORT2", "")
	os.Setenv("DELTA", "")
	os.Setenv("DELTAV", "")
	os.Setenv("SLICE", "")
	os.Setenv("STRUCT", "")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "", conf.Name)
}

func TestParseCustomNameConfig(t *testing.T) {
	var conf struct {
		Name string `envconfig:"customName"`
	}

	os.Setenv("customName", "foobar")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "foobar", conf.Name)
}

func TestParseOptionalStruct(t *testing.T) {
	var conf struct {
		Master struct {
			Name string
		} `envconfig:"optional"`
	}

	os.Setenv("MASTER_NAME", "")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "", conf.Master.Name)
}

func TestParsePrefixedStruct(t *testing.T) {
	var conf struct {
		Name string
	}

	os.Setenv("NAME", "")
	os.Setenv("FOO_NAME", "")

	os.Setenv("NAME", "bad")
	err := envconfig.InitWithPrefix(&conf, "FOO")
	assert(t, err != nil, "err should not be nil")

	os.Setenv("FOO_NAME", "good")
	err = envconfig.InitWithPrefix(&conf, "FOO")
	ok(t, err)
	equals(t, "good", conf.Name)
}

func TestUnexportedField(t *testing.T) {
	var conf struct {
		name string
	}

	os.Setenv("NAME", "foobar")

	err := envconfig.Init(&conf)
	equals(t, envconfig.ErrUnexportedField, err)
}

// assert fails the test if the condition is false.
func assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
	if !condition {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
		tb.FailNow()
	}
}

// ok fails the test if an err is not nil.
func ok(tb testing.TB, err error) {
	if err != nil {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
		tb.FailNow()
	}
}

// equals fails the test if exp is not equal to act.
func equals(tb testing.TB, exp, act interface{}) {
	if !reflect.DeepEqual(exp, act) {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
		tb.FailNow()
	}
}
<commit_msg>Add a test for skippable fields<commit_after>package envconfig_test

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/vrischmann/envconfig"
)

func TestParseSimpleConfig(t *testing.T) {
	var conf struct {
		Name string
		Log  struct {
			Path string
		}
	}

	// Go 1.2 and 1.3 don't have os.Unsetenv
	os.Setenv("NAME", "")
	os.Setenv("LOG_PATH", "")

	err := envconfig.Init(&conf)
	equals(t, "envconfig: key NAME not found", err.Error())

	os.Setenv("NAME", "foobar")
	err = envconfig.Init(&conf)
	equals(t, "envconfig: key LOG_PATH not found", err.Error())

	os.Setenv("LOG_PATH", "/var/log/foobar")
	err = envconfig.Init(&conf)
	ok(t, err)

	equals(t, "foobar", conf.Name)
	equals(t, "/var/log/foobar", conf.Log.Path)
}

func TestParseIntegerConfig(t *testing.T) {
	var conf struct {
		Port    int
		Long    uint64
		Version uint8
	}

	timestamp := time.Now().UnixNano()

	os.Setenv("PORT", "80")
	os.Setenv("LONG", fmt.Sprintf("%d", timestamp))
	os.Setenv("VERSION", "2")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, 80, conf.Port)
	equals(t, uint64(timestamp), conf.Long)
	equals(t, uint8(2), conf.Version)
}

func TestParseBoolConfig(t *testing.T) {
	var conf struct {
		DoIt bool
	}

	os.Setenv("DOIT", "true")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, true, conf.DoIt)
}

func TestParseFloatConfig(t *testing.T) {
	var conf struct {
		Delta  float32
		DeltaV float64
	}

	os.Setenv("DELTA", "0.02")
	os.Setenv("DELTAV", "400.20000000001")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, float32(0.02), conf.Delta)
	equals(t, float64(400.20000000001), conf.DeltaV)
}

func TestParseSliceConfig(t *testing.T) {
	var conf struct {
		Names  []string
		Ports  []int
		Shards []struct {
			Name string
			Addr string
		}
	}

	os.Setenv("NAMES", "foobar,barbaz")
	os.Setenv("PORTS", "900,100")
	os.Setenv("SHARDS", "{foobar,localhost:2929},{barbaz,localhost:2828}")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, 2, len(conf.Names))
	equals(t, "foobar", conf.Names[0])
	equals(t, "barbaz", conf.Names[1])
	equals(t, 2, len(conf.Ports))
	equals(t, 900, conf.Ports[0])
	equals(t, 100, conf.Ports[1])
	equals(t, 2, len(conf.Shards))
	equals(t, "foobar", conf.Shards[0].Name)
	equals(t, "localhost:2929", conf.Shards[0].Addr)
	equals(t, "barbaz", conf.Shards[1].Name)
	equals(t, "localhost:2828", conf.Shards[1].Addr)
}

func TestDurationConfig(t *testing.T) {
	var conf struct {
		Timeout time.Duration
	}

	os.Setenv("TIMEOUT", "1m")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, time.Minute*1, conf.Timeout)
}

func TestInvalidDurationConfig(t *testing.T) {
	var conf struct {
		Timeout time.Duration
	}

	os.Setenv("TIMEOUT", "foo")

	err := envconfig.Init(&conf)
	assert(t, err != nil, "err should not be nil")
}

func TestAllPointerConfig(t *testing.T) {
	var conf struct {
		Name   *string
		Port   *int
		Delta  *float32
		DeltaV *float64
		Hosts  *[]string
		Shards *[]*struct {
			Name *string
			Addr *string
		}
		Master *struct {
			Name *string
			Addr *string
		}
		Timeout *time.Duration
	}

	os.Setenv("NAME", "foobar")
	os.Setenv("PORT", "9000")
	os.Setenv("DELTA", "40.01")
	os.Setenv("DELTAV", "200.00001")
	os.Setenv("HOSTS", "localhost,free.fr")
	os.Setenv("SHARDS", "{foobar,localhost:2828},{barbaz,localhost:2929}")
	os.Setenv("MASTER_NAME", "master")
	os.Setenv("MASTER_ADDR", "localhost:2727")
	os.Setenv("TIMEOUT", "1m")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, "foobar", *conf.Name)
	equals(t, 9000, *conf.Port)
	equals(t, float32(40.01), *conf.Delta)
	equals(t, 200.00001, *conf.DeltaV)
	equals(t, 2, len(*conf.Hosts))
	equals(t, "localhost", (*conf.Hosts)[0])
	equals(t, "free.fr", (*conf.Hosts)[1])
	equals(t, 2, len(*conf.Shards))
	equals(t, "foobar", *(*conf.Shards)[0].Name)
	equals(t, "localhost:2828", *(*conf.Shards)[0].Addr)
	equals(t, "barbaz", *(*conf.Shards)[1].Name)
	equals(t, "localhost:2929", *(*conf.Shards)[1].Addr)
	equals(t, "master", *conf.Master.Name)
	equals(t, "localhost:2727", *conf.Master.Addr)
	equals(t, time.Minute*1, *conf.Timeout)
}

type logMode uint

const (
	logFile logMode = iota + 1
	logStdout
)

func (m *logMode) Unmarshal(s string) error {
	switch strings.ToLower(s) {
	case "file":
		*m = logFile
	case "stdout":
		*m = logStdout
	default:
		return fmt.Errorf("unable to unmarshal %s", s)
	}

	return nil
}

func TestUnmarshaler(t *testing.T) {
	var conf struct {
		LogMode logMode
	}

	os.Setenv("LOGMODE", "file")

	err := envconfig.Init(&conf)
	ok(t, err)

	equals(t, logFile, conf.LogMode)
}

func TestParseOptionalConfig(t *testing.T) {
	var conf struct {
		Name    string        `envconfig:"optional"`
		Flag    bool          `envconfig:"optional"`
		Timeout time.Duration `envconfig:"optional"`
		Port    int           `envconfig:"optional"`
		Port2   uint          `envconfig:"optional"`
		Delta   float32       `envconfig:"optional"`
		DeltaV  float64       `envconfig:"optional"`
		Slice   []string      `envconfig:"optional"`
		Struct  struct {
			A string
			B int
		} `envconfig:"optional"`
	}

	os.Setenv("NAME", "")
	os.Setenv("FLAG", "")
	os.Setenv("TIMEOUT", "")
	os.Setenv("PORT", "")
	os.Setenv("PORT2", "")
	os.Setenv("DELTA", "")
	os.Setenv("DELTAV", "")
	os.Setenv("SLICE", "")
	os.Setenv("STRUCT", "")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "", conf.Name)
}

func TestParseSkippableConfig(t *testing.T) {
	var conf struct {
		Flag bool `envconfig:"-"`
	}

	os.Setenv("FLAG", "true")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, false, conf.Flag)
}

func TestParseCustomNameConfig(t *testing.T) {
	var conf struct {
		Name string `envconfig:"customName"`
	}

	os.Setenv("customName", "foobar")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "foobar", conf.Name)
}

func TestParseOptionalStruct(t *testing.T) {
	var conf struct {
		Master struct {
			Name string
		} `envconfig:"optional"`
	}

	os.Setenv("MASTER_NAME", "")

	err := envconfig.Init(&conf)
	ok(t, err)
	equals(t, "", conf.Master.Name)
}

func TestParsePrefixedStruct(t *testing.T) {
	var conf struct {
		Name string
	}

	os.Setenv("NAME", "")
	os.Setenv("FOO_NAME", "")

	os.Setenv("NAME", "bad")
	err := envconfig.InitWithPrefix(&conf, "FOO")
	assert(t, err != nil, "err should not be nil")

	os.Setenv("FOO_NAME", "good")
	err = envconfig.InitWithPrefix(&conf, "FOO")
	ok(t, err)
	equals(t, "good", conf.Name)
}

func TestUnexportedField(t *testing.T) {
	var conf struct {
		name string
	}

	os.Setenv("NAME", "foobar")

	err := envconfig.Init(&conf)
	equals(t, envconfig.ErrUnexportedField, err)
}

// assert fails the test if the condition is false.
func assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
	if !condition {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
		tb.FailNow()
	}
}

// ok fails the test if an err is not nil.
func ok(tb testing.TB, err error) {
	if err != nil {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
		tb.FailNow()
	}
}

// equals fails the test if exp is not equal to act.
func equals(tb testing.TB, exp, act interface{}) {
	if !reflect.DeepEqual(exp, act) {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
		tb.FailNow()
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// @author john, zseeker

// 日志模块.
// 直接文件/输出操作，没有异步逻辑，没有使用缓存或者通道
package glog

import (
    "gitee.com/johng/gf/g/container/gtype"
)

const (
    LEVEL_ALL  = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
    LEVEL_DEBU = 1 << iota
    LEVEL_INFO
    LEVEL_NOTI
    LEVEL_WARN
    LEVEL_ERRO
    LEVEL_CRIT
)

var (
    // glog默认的日志等级，影响全局
    defaultLevel = gtype.NewInt(LEVEL_ALL)

    // 默认的日志对象
    logger = New()
)

// 日志日志目录绝对路径
func SetPath(path string) {
    logger.SetPath(path)
}

// 日志文件名称
func SetFile(file string) {
    logger.SetFile(file)
}

// 设置全局的日志记录等级
func SetLevel(level int) {
    logger.SetLevel(level)
    defaultLevel.Set(level)
}

// 获取全局的日志记录等级
func GetLevel() int {
    return defaultLevel.Val()
}

// 设置是否允许输出DEBUG信息
func SetDebug(debug bool) {
    logger.SetDebug(debug)
}

// 设置写日志的同时开启or关闭控制台打印，默认是关闭的
func SetStdPrint(open bool) {
    logger.SetStdPrint(open)
}

// 获取日志目录绝对路径
func GetPath() string {
    return logger.GetPath()
}

// 打印文件调用回溯信息
func PrintBacktrace(skip...int) {
    logger.PrintBacktrace(skip...)
}

// 获取文件调用回溯信息
func GetBacktrace(skip...int) string {
    return logger.GetBacktrace(skip...)
}

// 设置下一次输出的分类，支持多级分类设置
func Cat(category string) *Logger {
    return logger.Cat(category)
}

// 设置日志输出文件名称格式
func File(file string) *Logger {
    return logger.File(file)
}

// 设置日志打印等级
func Level(level int) *Logger {
    return logger.Level(level)
}

// 设置文件调用回溯信息
func Backtrace(enabled bool, skip...int) *Logger {
    return logger.Backtrace(enabled, skip...)
}

// 是否允许在设置输出文件时同时也输出到终端
func StdPrint(enabled bool) *Logger {
    return logger.StdPrint(enabled)
}

// 是否打印每行日志头信息(默认开启)
func Header(enabled bool) *Logger {
    return logger.Header(enabled)
}
func Print(v ...interface{}) {
    logger.Print(v ...)
}

func Printf(format string, v ...interface{}) {
    logger.Printf(format, v ...)
}

func Println(v ...interface{}) {
    logger.Println(v ...)
}

func Printfln(format string, v ...interface{}) {
    logger.Printfln(format, v ...)
}

func Fatal(v ...interface{}) {
    logger.Fatal(v ...)
}

func Fatalf(format string, v ...interface{}) {
    logger.Fatalf(format, v ...)
}

func Fatalln(v ...interface{}) {
    logger.Fatalln(v ...)
}

func Fatalfln(format string, v ...interface{}) {
    logger.Fatalfln(format, v ...)
}

func Panic(v ...interface{}) {
    logger.Panic(v ...)
}

func Panicf(format string, v ...interface{}) {
    logger.Panicf(format, v ...)
}

func Panicln(v ...interface{}) {
    logger.Panicln(v ...)
}

func Panicfln(format string, v ...interface{}) {
    logger.Panicfln(format, v ...)
}

func Info(v ...interface{}) {
    logger.Info(v...)
}

func Debug(v ...interface{}) {
    logger.Debug(v...)
}

func Notice(v ...interface{}) {
    logger.Notice(v...)
}

func Warning(v ...interface{}) {
    logger.Warning(v...)
}

func Error(v ...interface{}) {
    logger.Error(v...)
}

func Critical(v ...interface{}) {
    logger.Critical(v...)
}

func Infof(format string, v ...interface{}) {
    logger.Infof(format, v...)
}

func Debugf(format string, v ...interface{}) {
    logger.Debugf(format, v...)
}

func Noticef(format string, v ...interface{}) {
    logger.Noticef(format, v...)
}

func Warningf(format string, v ...interface{}) {
    logger.Warningf(format, v...)
}

func Errorf(format string, v ...interface{}) {
    logger.Errorf(format, v...)
}

func Criticalf(format string, v ...interface{}) {
    logger.Criticalf(format, v...)
}

func Infofln(format string, v ...interface{}) {
    logger.Infofln(format, v...)
}

func Debugfln(format string, v ...interface{}) {
    logger.Debugfln(format, v...)
}

func Noticefln(format string, v ...interface{}) {
    logger.Noticefln(format, v...)
}

func Warningfln(format string, v ...interface{}) {
    logger.Warningfln(format, v...)
}

func Errorfln(format string, v ...interface{}) {
    logger.Errorfln(format, v...)
}

func Criticalfln(format string, v ...interface{}) {
    logger.Criticalfln(format, v...)
}
<commit_msg>glog增加全局的BackTrace关闭功能<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// @author john, zseeker

// 日志模块.
// 直接文件/输出操作，没有异步逻辑，没有使用缓存或者通道
package glog

import (
    "gitee.com/johng/gf/g/container/gtype"
)

const (
    LEVEL_ALL  = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
    LEVEL_DEBU = 1 << iota
    LEVEL_INFO
    LEVEL_NOTI
    LEVEL_WARN
    LEVEL_ERRO
    LEVEL_CRIT
)

var (
    // glog默认的日志等级，影响全局
    defaultLevel = gtype.NewInt(LEVEL_ALL)

    // 默认的日志对象
    logger = New()
)

// 日志日志目录绝对路径
func SetPath(path string) {
    logger.SetPath(path)
}

// 日志文件名称
func SetFile(file string) {
    logger.SetFile(file)
}

// 设置全局的日志记录等级
func SetLevel(level int) {
    logger.SetLevel(level)
    defaultLevel.Set(level)
}

// 获取全局的日志记录等级
func GetLevel() int {
    return defaultLevel.Val()
}

// 设置是否允许输出DEBUG信息
func SetDebug(debug bool) {
    logger.SetDebug(debug)
}

// 设置写日志的同时开启or关闭控制台打印，默认是关闭的
func SetStdPrint(open bool) {
    logger.SetStdPrint(open)
}

// 获取日志目录绝对路径
func GetPath() string {
    return logger.GetPath()
}

// 打印文件调用回溯信息
func PrintBacktrace(skip...int) {
    logger.PrintBacktrace(skip...)
}

// 获取文件调用回溯信息
func GetBacktrace(skip...int) string {
    return logger.GetBacktrace(skip...)
}

// 是否关闭全局的backtrace信息
func SetBacktrace(enabled bool) {
    logger.SetBacktrace(enabled)
}

// 设置下一次输出的分类，支持多级分类设置
func Cat(category string) *Logger {
    return logger.Cat(category)
}

// 设置日志输出文件名称格式
func File(file string) *Logger {
    return logger.File(file)
}

// 设置日志打印等级
func Level(level int) *Logger {
    return logger.Level(level)
}

// 设置文件调用回溯信息
func Backtrace(enabled bool, skip...int) *Logger {
    return logger.Backtrace(enabled, skip...)
}

// 是否允许在设置输出文件时同时也输出到终端
func StdPrint(enabled bool) *Logger {
    return logger.StdPrint(enabled)
}

// 是否打印每行日志头信息(默认开启)
func Header(enabled bool) *Logger {
    return logger.Header(enabled)
}
func Print(v ...interface{}) {
    logger.Print(v ...)
}

func Printf(format string, v ...interface{}) {
    logger.Printf(format, v ...)
}

func Println(v ...interface{}) {
    logger.Println(v ...)
}

func Printfln(format string, v ...interface{}) {
    logger.Printfln(format, v ...)
}

func Fatal(v ...interface{}) {
    logger.Fatal(v ...)
}

func Fatalf(format string, v ...interface{}) {
    logger.Fatalf(format, v ...)
}

func Fatalln(v ...interface{}) {
    logger.Fatalln(v ...)
}

func Fatalfln(format string, v ...interface{}) {
    logger.Fatalfln(format, v ...)
}

func Panic(v ...interface{}) {
    logger.Panic(v ...)
}

func Panicf(format string, v ...interface{}) {
    logger.Panicf(format, v ...)
}

func Panicln(v ...interface{}) {
    logger.Panicln(v ...)
}

func Panicfln(format string, v ...interface{}) {
    logger.Panicfln(format, v ...)
}

func Info(v ...interface{}) {
    logger.Info(v...)
}

func Debug(v ...interface{}) {
    logger.Debug(v...)
}

func Notice(v ...interface{}) {
    logger.Notice(v...)
}

func Warning(v ...interface{}) {
    logger.Warning(v...)
}

func Error(v ...interface{}) {
    logger.Error(v...)
}

func Critical(v ...interface{}) {
    logger.Critical(v...)
}

func Infof(format string, v ...interface{}) {
    logger.Infof(format, v...)
}

func Debugf(format string, v ...interface{}) {
    logger.Debugf(format, v...)
}

func Noticef(format string, v ...interface{}) {
    logger.Noticef(format, v...)
}

func Warningf(format string, v ...interface{}) {
    logger.Warningf(format, v...)
}

func Errorf(format string, v ...interface{}) {
    logger.Errorf(format, v...)
}

func Criticalf(format string, v ...interface{}) {
    logger.Criticalf(format, v...)
}

func Infofln(format string, v ...interface{}) {
    logger.Infofln(format, v...)
}

func Debugfln(format string, v ...interface{}) {
    logger.Debugfln(format, v...)
}

func Noticefln(format string, v ...interface{}) {
    logger.Noticefln(format, v...)
}

func Warningfln(format string, v ...interface{}) {
    logger.Warningfln(format, v...)
}

func Errorfln(format string, v ...interface{}) {
    logger.Errorfln(format, v...)
}

func Criticalfln(format string, v ...interface{}) {
    logger.Criticalfln(format, v...)
}
<|endoftext|>