<commit_before>package api

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/mdlayher/goat/goat/common"
	"github.com/mdlayher/goat/goat/data"
)

// TestAuthenticator verifies that the Basic and HMAC authenticators work properly
func TestAuthenticator(t *testing.T) {
	log.Println("TestBasicAuthenticator()")

	// Load config
	config, err := common.LoadConfig()
	if err != nil {
		t.Fatalf("Could not load configuration: %s", err.Error())
	}
	common.Static.Config = config

	// Generate mock user
	user := new(data.UserRecord)
	if err := user.Create("test", "test", 10); err != nil {
		t.Fatalf("Failed to create mock user: %s", err.Error())
	}

	// Save mock user
	if err := user.Save(); err != nil {
		t.Fatalf("Failed to save mock user: %s", err.Error())
	}

	// Generate mock HTTP request
	r, err := http.NewRequest("POST", "http://localhost:8080/api/login", nil)
	if err != nil {
		t.Fatalf("Failed to generate HTTP request: %s", err.Error())
	}

	headers := map[string][]string{
		"Authorization": {"Basic " + base64.URLEncoding.EncodeToString([]byte("test:test"))},
	}
	r.Header = headers

	// Capture HTTP response with recorder
	w := httptest.NewRecorder()

	// Invoke API router
	Router(w, r, *user)

	// Read HTTP response body
	body, err := ioutil.ReadAll(w.Body)
	if err != nil {
		t.Fatalf("Failed to read HTTP response body: %s", err.Error())
	}

	// Unmarshal result JSON
	var login data.JSONAPIKey
	if err := json.Unmarshal(body, &login); err != nil {
		t.Fatalf("Failed to unmarshal login JSON: %s", err.Error())
	}

	// Generate API signature
	nonce := "abcdef"
	method := "GET"
	resource := "/api/status"

	signature, err := apiSignature(login.UserID, nonce, method, resource, login.Secret)
	if err != nil {
		t.Fatalf("Failed to generate API signature: %s", err.Error())
	}
	log.Println("signature:", signature)

	// Generate mock HTTP request
	r, err = http.NewRequest(method, "http://localhost:8080"+resource, nil)
	if err != nil {
		t.Fatalf("Failed to generate HTTP request: %s", err.Error())
	}

	headers = map[string][]string{
		"Authorization": {"Basic " + base64.URLEncoding.EncodeToString([]byte(login.Pubkey+":"+nonce+"/"+signature))},
	}
	r.Header = headers

	// Capture HTTP response with recorder
	w = httptest.NewRecorder()

	// Invoke API router
	Router(w, r, *user)

	// Read HTTP response body
	body, err = ioutil.ReadAll(w.Body)
	if err != nil {
		t.Fatalf("Failed to read HTTP response body: %s", err.Error())
	}
	log.Println(string(body))

	// Delete mock user
	if err := user.Delete(); err != nil {
		t.Fatalf("Failed to delete mock user: %s", err.Error())
	}
}
<commit_msg>Fix API authenticator tests<commit_after>package api

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/mdlayher/goat/goat/common"
	"github.com/mdlayher/goat/goat/data"
)

// TestAuthenticator verifies that the Basic and HMAC authenticators work properly
func TestAuthenticator(t *testing.T) {
	log.Println("TestBasicAuthenticator()")

	// Load config
	config, err := common.LoadConfig()
	if err != nil {
		t.Fatalf("Could not load configuration: %s", err.Error())
	}
	common.Static.Config = config

	// Generate mock user
	user := new(data.UserRecord)
	if err := user.Create("test", "test", 10); err != nil {
		t.Fatalf("Failed to create mock user: %s", err.Error())
	}

	// Save mock user
	if err := user.Save(); err != nil {
		t.Fatalf("Failed to save mock user: %s", err.Error())
	}

	// Load user to get ID
	user2, err := user.Load("test", "username")
	if err != nil || (user2 == data.UserRecord{}) {
		t.Fatalf("Failed to load mock user: %s", err.Error())
	}

	// Generate mock HTTP request
	r, err := http.NewRequest("POST", "http://localhost:8080/api/login", nil)
	if err != nil {
		t.Fatalf("Failed to generate HTTP request: %s", err.Error())
	}

	headers := map[string][]string{
		"Authorization": {"Basic " + base64.URLEncoding.EncodeToString([]byte("test:test"))},
	}
	r.Header = headers

	// Capture HTTP response with recorder
	w := httptest.NewRecorder()

	// Perform HTTP Basic authentication
	var apiAuth APIAuthenticator
	apiAuth = new(BasicAuthenticator)

	// Attempt Basic authentication
	clientErr, serverErr := apiAuth.Auth(r)
	if clientErr != nil {
		t.Fatalf("Failed to authenticate: client: %s", clientErr.Error())
	}
	if serverErr != nil {
		t.Fatalf("Failed to authenticate: server: %s", serverErr.Error())
	}

	// Invoke API router
	Router(w, r, user2)

	// Read HTTP response body
	body, err := ioutil.ReadAll(w.Body)
	if err != nil {
		t.Fatalf("Failed to read HTTP response body: %s", err.Error())
	}

	// Unmarshal result JSON
	var login data.JSONAPIKey
	if err := json.Unmarshal(body, &login); err != nil {
		t.Fatalf("Failed to unmarshal login JSON: %s", err.Error())
	}

	// Generate API signature
	nonce := "abcdef"
	method := "GET"
	resource := "/api/status"

	signature, err := apiSignature(login.UserID, nonce, method, resource, login.Secret)
	if err != nil {
		t.Fatalf("Failed to generate API signature: %s", err.Error())
	}
	log.Println("signature:", signature)

	// Generate mock HTTP request
	r, err = http.NewRequest(method, "http://localhost:8080"+resource, nil)
	if err != nil {
		t.Fatalf("Failed to generate HTTP request: %s", err.Error())
	}

	headers = map[string][]string{
		"Authorization": {"Basic " + base64.URLEncoding.EncodeToString([]byte(login.Pubkey+":"+nonce+"/"+signature))},
	}
	r.Header = headers

	// Capture HTTP response with recorder
	w = httptest.NewRecorder()

	// Attempt HMAC authentication
	apiAuth = new(HMACAuthenticator)
	clientErr, serverErr = apiAuth.Auth(r)
	if clientErr != nil {
		t.Fatalf("Failed to authenticate: client: %s", clientErr.Error())
	}
	if serverErr != nil {
		t.Fatalf("Failed to authenticate: server: %s", serverErr.Error())
	}

	// Invoke API router
	Router(w, r, user2)

	// Read HTTP response body
	body, err = ioutil.ReadAll(w.Body)
	if err != nil {
		t.Fatalf("Failed to read HTTP response body: %s", err.Error())
	}
	log.Println(string(body))

	// Delete mock user
	if err := user2.Delete(); err != nil {
		t.Fatalf("Failed to delete mock user: %s", err.Error())
	}
}
<|endoftext|>


<commit_before>// Package sentinel provides a convenient interface with a redis sentinel which
// will automatically handle pooling connections and automatic failover.
//
// Here's an example of creating a sentinel client and then using it to perform
// some commands
//
//	func example() error {
//		// If there exists sentinel masters "bucket0" and "bucket1", and we want
//		// out client to create pools for both:
//		client, err := sentinel.NewClient("tcp", "localhost:6379", 100, "bucket0", "bucket1")
//		if err != nil {
//			return err
//		}
//
//		if err := exampleCmd(client); err != nil {
//			return err
//		}
//
//		return nil
//	}
//
//	func exampleCmd(client *sentinel.Client) error {
//		conn, err := client.GetMaster("bucket0")
//		if err != nil {
//			return redisErr
//		}
//		defer client.PutMaster("bucket0", conn)
//
//		i, err := conn.Cmd("GET", "foo").Int()
//		if err != nil {
//			return err
//		}
//
//		if err := conn.Cmd("SET", "foo", i+1); err != nil {
//			return err
//		}
//
//		return nil
//	}
//
// This package only guarantees that when GetMaster is called the returned
// connection will be a connection to the master as of the moment that method is
// called. It is still possible that there is a failover as that connection is
// being used by the application.
//
// As a final note, a Client can be interacted with from multiple routines at
// once safely, except for the Close method. To safely Close, ensure that only
// one routine ever makes the call and that once the call is made no other
// methods are ever called by any routines.
package sentinel

import (
	"errors"
	"strings"

	"github.com/mediocregopher/radix.v2/pool"
	"github.com/mediocregopher/radix.v2/pubsub"
	"github.com/mediocregopher/radix.v2/redis"
)

// ClientError is an error wrapper returned by operations in this package. It
// implements the error interface and can therefore be passed around as a normal
// error.
type ClientError struct {
	err error

	// If this is true the error is due to a problem with the sentinel
	// connection, either it being closed or otherwise unavailable. If false the
	// error is due to some other circumstances. This is useful if you want to
	// implement some kind of reconnecting to sentinel on an error.
	SentinelErr bool
}

// Error implements the error protocol
func (ce *ClientError) Error() string {
	return ce.err.Error()
}

type getReqRet struct {
	conn *redis.Client
	err  *ClientError
}

type getReq struct {
	name  string
	retCh chan *getReqRet
}

type putReq struct {
	name string
	conn *redis.Client
}

type switchMaster struct {
	name string
	addr string
}

// Client communicates with a sentinel instance and manages connection pools of
// active masters
type Client struct {
	poolSize    int
	masterPools map[string]*pool.Pool
	subClient   *pubsub.SubClient

	// This is pool.DialFunc instead of the package's DialFunc
	// as it's only used when calling pool.NewCustom. Otherwise it
	// will have to be cast on each invocation.
	dialFunc pool.DialFunc

	getCh   chan *getReq
	putCh   chan *putReq
	closeCh chan struct{}

	alwaysErr      *ClientError
	alwaysErrCh    chan *ClientError
	switchMasterCh chan *switchMaster
}

// DialFunc is a function which can be passed into NewClientCustom
type DialFunc func(network, addr string) (*redis.Client, error)

// NewClient creates a sentinel client. Connects to the given sentinel instance,
// pulls the information for the masters of the given names, and creates an
// initial pool of connections for each master. The client will automatically
// replace the pool for any master should sentinel decide to fail the master
// over. The returned error is a *ClientError.
func NewClient(
	network, address string, poolSize int, names ...string,
) (
	*Client, error,
) {
	return NewClientCustom(network, address, poolSize, redis.Dial, names...)
}

// NewClientCustom is the same as NewClient, except it takes in a DialFunc which
// will be used to create all new connections to the master instances. This can
// be used to implement authentication, custom timeouts, etc...
func NewClientCustom(
	network, address string, poolSize int, df DialFunc, names ...string,
) (
	*Client, error,
) {

	// We use this to fetch initial details about masters before we upgrade it
	// to a pubsub client
	client, err := redis.Dial(network, address)
	if err != nil {
		return nil, &ClientError{err: err}
	}

	masterPools := map[string]*pool.Pool{}
	for _, name := range names {
		r := client.Cmd("SENTINEL", "MASTER", name)
		l, err := r.List()
		if err != nil {
			return nil, &ClientError{err: err, SentinelErr: true}
		}
		addr := l[3] + ":" + l[5]
		pool, err := pool.NewCustom("tcp", addr, poolSize, (pool.DialFunc)(df))
		if err != nil {
			return nil, &ClientError{err: err}
		}
		masterPools[name] = pool
	}

	subClient := pubsub.NewSubClient(client)
	r := subClient.Subscribe("+switch-master")
	if r.Err != nil {
		return nil, &ClientError{err: r.Err, SentinelErr: true}
	}

	c := &Client{
		poolSize:       poolSize,
		masterPools:    masterPools,
		subClient:      subClient,
		dialFunc:       (pool.DialFunc)(df),
		getCh:          make(chan *getReq),
		putCh:          make(chan *putReq),
		closeCh:        make(chan struct{}),
		alwaysErrCh:    make(chan *ClientError),
		switchMasterCh: make(chan *switchMaster),
	}

	go c.subSpin()
	go c.spin()
	return c, nil
}

func (c *Client) subSpin() {
	for {
		r := c.subClient.Receive()
		if r.Timeout() {
			continue
		}
		if r.Err != nil {
			select {
			case c.alwaysErrCh <- &ClientError{err: r.Err, SentinelErr: true}:
			case <-c.closeCh:
			}
			return
		}
		sMsg := strings.Split(r.Message, " ")
		name := sMsg[0]
		newAddr := sMsg[3] + ":" + sMsg[4]
		select {
		case c.switchMasterCh <- &switchMaster{name, newAddr}:
		case <-c.closeCh:
			return
		}
	}
}

func (c *Client) spin() {
	for {
		select {
		case req := <-c.getCh:
			if c.alwaysErr != nil {
				req.retCh <- &getReqRet{nil, c.alwaysErr}
				continue
			}
			pool, ok := c.masterPools[req.name]
			if !ok {
				err := errors.New("unknown name: " + req.name)
				req.retCh <- &getReqRet{nil, &ClientError{err: err}}
				continue
			}
			conn, err := pool.Get()
			if err != nil {
				req.retCh <- &getReqRet{nil, &ClientError{err: err}}
				continue
			}
			req.retCh <- &getReqRet{conn, nil}

		case req := <-c.putCh:
			if pool, ok := c.masterPools[req.name]; ok {
				pool.Put(req.conn)
			}

		case err := <-c.alwaysErrCh:
			c.alwaysErr = err

		case sm := <-c.switchMasterCh:
			if p, ok := c.masterPools[sm.name]; ok {
				p.Empty()
				p, _ = pool.NewCustom("tcp", sm.addr, c.poolSize, c.dialFunc)
				c.masterPools[sm.name] = p
			}

		case <-c.closeCh:
			for name := range c.masterPools {
				c.masterPools[name].Empty()
			}
			c.subClient.Client.Close()
			close(c.getCh)
			close(c.putCh)
			return
		}
	}
}

// GetMaster retrieves a connection for the master of the given name. If
// sentinel has become unreachable this will always return an error. Close
// should be called in that case. The returned error is a *ClientError.
func (c *Client) GetMaster(name string) (*redis.Client, error) {
	req := getReq{name, make(chan *getReqRet)}
	c.getCh <- &req
	ret := <-req.retCh
	if ret.err != nil {
		return nil, ret.err
	}
	return ret.conn, nil
}

// PutMaster return a connection for a master of a given name
func (c *Client) PutMaster(name string, client *redis.Client) {
	c.putCh <- &putReq{name, client}
}
<commit_msg>have sentinel periodically Ping its master sub connection (might fix #41)<commit_after>// Package sentinel provides a convenient interface with a redis sentinel which
// will automatically handle pooling connections and automatic failover.
//
// Here's an example of creating a sentinel client and then using it to perform
// some commands
//
//	func example() error {
//		// If there exists sentinel masters "bucket0" and "bucket1", and we want
//		// out client to create pools for both:
//		client, err := sentinel.NewClient("tcp", "localhost:6379", 100, "bucket0", "bucket1")
//		if err != nil {
//			return err
//		}
//
//		if err := exampleCmd(client); err != nil {
//			return err
//		}
//
//		return nil
//	}
//
//	func exampleCmd(client *sentinel.Client) error {
//		conn, err := client.GetMaster("bucket0")
//		if err != nil {
//			return redisErr
//		}
//		defer client.PutMaster("bucket0", conn)
//
//		i, err := conn.Cmd("GET", "foo").Int()
//		if err != nil {
//			return err
//		}
//
//		if err := conn.Cmd("SET", "foo", i+1); err != nil {
//			return err
//		}
//
//		return nil
//	}
//
// This package only guarantees that when GetMaster is called the returned
// connection will be a connection to the master as of the moment that method is
// called. It is still possible that there is a failover as that connection is
// being used by the application.
//
// As a final note, a Client can be interacted with from multiple routines at
// once safely, except for the Close method. To safely Close, ensure that only
// one routine ever makes the call and that once the call is made no other
// methods are ever called by any routines.
package sentinel

import (
	"errors"
	"strings"

	"github.com/mediocregopher/radix.v2/pool"
	"github.com/mediocregopher/radix.v2/pubsub"
	"github.com/mediocregopher/radix.v2/redis"
)

// ClientError is an error wrapper returned by operations in this package. It
// implements the error interface and can therefore be passed around as a normal
// error.
type ClientError struct {
	err error

	// If this is true the error is due to a problem with the sentinel
	// connection, either it being closed or otherwise unavailable. If false the
	// error is due to some other circumstances. This is useful if you want to
	// implement some kind of reconnecting to sentinel on an error.
	SentinelErr bool
}

// Error implements the error protocol
func (ce *ClientError) Error() string {
	return ce.err.Error()
}

type getReqRet struct {
	conn *redis.Client
	err  *ClientError
}

type getReq struct {
	name  string
	retCh chan *getReqRet
}

type putReq struct {
	name string
	conn *redis.Client
}

type switchMaster struct {
	name string
	addr string
}

// Client communicates with a sentinel instance and manages connection pools of
// active masters
type Client struct {
	poolSize    int
	masterPools map[string]*pool.Pool
	subClient   *pubsub.SubClient

	// This is pool.DialFunc instead of the package's DialFunc
	// as it's only used when calling pool.NewCustom. Otherwise it
	// will have to be cast on each invocation.
	dialFunc pool.DialFunc

	getCh   chan *getReq
	putCh   chan *putReq
	closeCh chan struct{}

	alwaysErr      *ClientError
	alwaysErrCh    chan *ClientError
	switchMasterCh chan *switchMaster
}

// DialFunc is a function which can be passed into NewClientCustom
type DialFunc func(network, addr string) (*redis.Client, error)

// NewClient creates a sentinel client. Connects to the given sentinel instance,
// pulls the information for the masters of the given names, and creates an
// initial pool of connections for each master. The client will automatically
// replace the pool for any master should sentinel decide to fail the master
// over. The returned error is a *ClientError.
func NewClient(
	network, address string, poolSize int, names ...string,
) (
	*Client, error,
) {
	return NewClientCustom(network, address, poolSize, redis.Dial, names...)
}

// NewClientCustom is the same as NewClient, except it takes in a DialFunc which
// will be used to create all new connections to the master instances. This can
// be used to implement authentication, custom timeouts, etc...
func NewClientCustom(
	network, address string, poolSize int, df DialFunc, names ...string,
) (
	*Client, error,
) {

	// We use this to fetch initial details about masters before we upgrade it
	// to a pubsub client
	client, err := redis.Dial(network, address)
	if err != nil {
		return nil, &ClientError{err: err}
	}

	masterPools := map[string]*pool.Pool{}
	for _, name := range names {
		r := client.Cmd("SENTINEL", "MASTER", name)
		l, err := r.List()
		if err != nil {
			return nil, &ClientError{err: err, SentinelErr: true}
		}
		addr := l[3] + ":" + l[5]
		pool, err := pool.NewCustom("tcp", addr, poolSize, (pool.DialFunc)(df))
		if err != nil {
			return nil, &ClientError{err: err}
		}
		masterPools[name] = pool
	}

	subClient := pubsub.NewSubClient(client)
	r := subClient.Subscribe("+switch-master")
	if r.Err != nil {
		return nil, &ClientError{err: r.Err, SentinelErr: true}
	}

	c := &Client{
		poolSize:       poolSize,
		masterPools:    masterPools,
		subClient:      subClient,
		dialFunc:       (pool.DialFunc)(df),
		getCh:          make(chan *getReq),
		putCh:          make(chan *putReq),
		closeCh:        make(chan struct{}),
		alwaysErrCh:    make(chan *ClientError),
		switchMasterCh: make(chan *switchMaster),
	}

	go c.subSpin()
	go c.spin()
	return c, nil
}

func (c *Client) subSpin() {
	alwaysErr := func(err error) {
		select {
		case c.alwaysErrCh <- &ClientError{err: err, SentinelErr: true}:
		case <-c.closeCh:
		}
	}

	for {
		if err := c.subClient.Ping().Err; err != nil {
			alwaysErr(err)
			return
		}

		r := c.subClient.Receive()
		if r.Timeout() {
			continue
		}
		if r.Err != nil {
			alwaysErr(r.Err)
			return
		}
		sMsg := strings.Split(r.Message, " ")
		name := sMsg[0]
		newAddr := sMsg[3] + ":" + sMsg[4]
		select {
		case c.switchMasterCh <- &switchMaster{name, newAddr}:
		case <-c.closeCh:
			return
		}
	}
}

func (c *Client) spin() {
	for {
		select {
		case req := <-c.getCh:
			if c.alwaysErr != nil {
				req.retCh <- &getReqRet{nil, c.alwaysErr}
				continue
			}
			pool, ok := c.masterPools[req.name]
			if !ok {
				err := errors.New("unknown name: " + req.name)
				req.retCh <- &getReqRet{nil, &ClientError{err: err}}
				continue
			}
			conn, err := pool.Get()
			if err != nil {
				req.retCh <- &getReqRet{nil, &ClientError{err: err}}
				continue
			}
			req.retCh <- &getReqRet{conn, nil}

		case req := <-c.putCh:
			if pool, ok := c.masterPools[req.name]; ok {
				pool.Put(req.conn)
			}

		case err := <-c.alwaysErrCh:
			c.alwaysErr = err

		case sm := <-c.switchMasterCh:
			if p, ok := c.masterPools[sm.name]; ok {
				p.Empty()
				p, _ = pool.NewCustom("tcp", sm.addr, c.poolSize, c.dialFunc)
				c.masterPools[sm.name] = p
			}

		case <-c.closeCh:
			for name := range c.masterPools {
				c.masterPools[name].Empty()
			}
			c.subClient.Client.Close()
			close(c.getCh)
			close(c.putCh)
			return
		}
	}
}

// GetMaster retrieves a connection for the master of the given name. If
// sentinel has become unreachable this will always return an error. Close
// should be called in that case. The returned error is a *ClientError.
func (c *Client) GetMaster(name string) (*redis.Client, error) {
	req := getReq{name, make(chan *getReqRet)}
	c.getCh <- &req
	ret := <-req.retCh
	if ret.err != nil {
		return nil, ret.err
	}
	return ret.conn, nil
}

// PutMaster return a connection for a master of a given name
func (c *Client) PutMaster(name string, client *redis.Client) {
	c.putCh <- &putReq{name, client}
}
<|endoftext|>


<commit_before>package products

import (
	"bytes"
	"fmt"
	"html/template"

	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/media"
	"github.com/qor/media/media_library"
	"github.com/qor/qor"
	"github.com/qor/qor-example/config/application"
	"github.com/qor/qor-example/models/products"
	"github.com/qor/qor-example/utils/funcmapmaker"
	"github.com/qor/render"
)

var Genders = []string{"Men", "Women", "Kids"}

// New new home app
func New(config *Config) *App {
	return &App{Config: config}
}

// App home app
type App struct {
	Config *Config
}

// Config home config struct
type Config struct {
}

// ConfigureApplication configure application
func (app App) ConfigureApplication(application *application.Application) {
	controller := &Controller{View: render.New(&render.Config{AssetFileSystem: application.AssetFS.NameSpace("products")}, "app/products/views")}

	funcmapmaker.AddFuncMapMaker(controller.View)
	app.ConfigureAdmin(application.Admin)

	application.Router.Get("/products", controller.Index)
	application.Router.Get("/products/{code}", controller.Show)
	application.Router.Get("/{gender:^(men|women|kids)$}", controller.Gender)
	application.Router.Get("/category/{code}", controller.Category)
}

// ConfigureAdmin configure admin interface
func (App) ConfigureAdmin(Admin *admin.Admin) {
	// Produc Management
	Admin.AddMenu(&admin.Menu{Name: "Product Management", Priority: 1})
	color := Admin.AddResource(&products.Color{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -5})
	Admin.AddResource(&products.Size{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -4})
	Admin.AddResource(&products.Material{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -4})

	category := Admin.AddResource(&products.Category{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -3})
	category.Meta(&admin.Meta{Name: "Categories", Type: "select_many"})

	collection := Admin.AddResource(&products.Collection{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -2})

	// Add ProductImage as Media Libraray
	ProductImagesResource := Admin.AddResource(&products.ProductImage{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -1})

	ProductImagesResource.Filter(&admin.Filter{
		Name:       "SelectedType",
		Label:      "Media Type",
		Operations: []string{"contains"},
		Config:     &admin.SelectOneConfig{Collection: [][]string{{"video", "Video"}, {"image", "Image"}, {"file", "File"}, {"video_link", "Video Link"}}},
	})
	ProductImagesResource.Filter(&admin.Filter{
		Name:   "Color",
		Config: &admin.SelectOneConfig{RemoteDataResource: color},
	})
	ProductImagesResource.Filter(&admin.Filter{
		Name:   "Category",
		Config: &admin.SelectOneConfig{RemoteDataResource: category},
	})
	ProductImagesResource.IndexAttrs("File", "Title")

	// Add Product
	product := Admin.AddResource(&products.Product{}, &admin.Config{Menu: []string{"Product Management"}})
	product.Meta(&admin.Meta{Name: "Gender", Config: &admin.SelectOneConfig{Collection: Genders, AllowBlank: true}})

	productPropertiesRes := product.Meta(&admin.Meta{Name: "ProductProperties"}).Resource
	productPropertiesRes.NewAttrs(&admin.Section{
		Rows: [][]string{{"Name", "Value"}},
	})
	productPropertiesRes.EditAttrs(&admin.Section{
		Rows: [][]string{{"Name", "Value"}},
	})

	product.Meta(&admin.Meta{Name: "Description", Config: &admin.RichEditorConfig{Plugins: []admin.RedactorPlugin{
		{Name: "medialibrary", Source: "/admin/assets/javascripts/qor_redactor_medialibrary.js"},
		{Name: "table", Source: "/vendors/redactor_table.js"},
	},
		Settings: map[string]interface{}{
			"medialibraryUrl": "/admin/product_images",
		},
	}})
	product.Meta(&admin.Meta{Name: "Category", Config: &admin.SelectOneConfig{AllowBlank: true}})
	product.Meta(&admin.Meta{Name: "Collections", Config: &admin.SelectManyConfig{SelectMode: "bottom_sheet"}})

	product.Meta(&admin.Meta{Name: "MainImage", Config: &media_library.MediaBoxConfig{
		RemoteDataResource: ProductImagesResource,
		Max:                1,
		Sizes: map[string]*media.Size{
			"main": {Width: 560, Height: 700},
		},
	}})
	product.Meta(&admin.Meta{Name: "MainImageURL", Valuer: func(record interface{}, context *qor.Context) interface{} {
		if p, ok := record.(*products.Product); ok {
			result := bytes.NewBufferString("")
			tmpl, _ := template.New("").Parse("")
			tmpl.Execute(result, map[string]string{"image": p.MainImageURL()})
			return template.HTML(result.String())
		}
		return ""
	}})

	product.Filter(&admin.Filter{
		Name:   "Collections",
		Config: &admin.SelectOneConfig{RemoteDataResource: collection},
	})

	product.Filter(&admin.Filter{
		Name: "Name",
		Type: "string",
	})

	product.Filter(&admin.Filter{
		Name: "Code",
	})

	product.Filter(&admin.Filter{
		Name: "Price",
		Type: "number",
	})

	product.Action(&admin.Action{
		Name:        "Import Product",
		URLOpenType: "slideout",
		URL: func(record interface{}, context *admin.Context) string {
			return "/admin/workers/new?job=Import Products"
		},
		Modes: []string{"collection"},
	})

	type updateInfo struct {
		CategoryID  uint
		Category    *products.Category
		MadeCountry string
		Gender      string
	}

	updateInfoRes := Admin.NewResource(&updateInfo{})
	product.Action(&admin.Action{
		Name:     "Update Info",
		Resource: updateInfoRes,
		Handler: func(argument *admin.ActionArgument) error {
			newProductInfo := argument.Argument.(*updateInfo)
			for _, record := range argument.FindSelectedRecords() {
				fmt.Printf("%#v\n", record)
				if product, ok := record.(*products.Product); ok {
					if newProductInfo.Category != nil {
						product.Category = *newProductInfo.Category
					}
					if newProductInfo.MadeCountry != "" {
						product.MadeCountry = newProductInfo.MadeCountry
					}
					if newProductInfo.Gender != "" {
						product.Gender = newProductInfo.Gender
					}
					argument.Context.GetDB().Save(product)
				}
			}
			return nil
		},
		Modes: []string{"batch"},
	})

	product.UseTheme("grid")

	// variationsResource := product.Meta(&admin.Meta{Name: "Variations", Config: &variations.VariationsConfig{}}).Resource
	// if imagesMeta := variationsResource.GetMeta("Images"); imagesMeta != nil {
	// 	imagesMeta.Config = &media_library.MediaBoxConfig{
	// 		RemoteDataResource: ProductImagesResource,
	// 		Sizes: map[string]*media.Size{
	// 			"icon":    {Width: 50, Height: 50},
	// 			"thumb":   {Width: 100, Height: 100},
	// 			"display": {Width: 300, Height: 300},
	// 		},
	// 	}
	// }

	// variationsResource.EditAttrs("-ID", "-Product")
	// oldSearchHandler := product.SearchHandler
	// product.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB {
	// 	context.SetDB(context.GetDB().Preload("Variations.Color").Preload("Variations.Size").Preload("Variations.Material"))
	// 	return oldSearchHandler(keyword, context)
	// }
	colorVariationMeta := product.Meta(&admin.Meta{Name: "ColorVariations"})
	colorVariation := colorVariationMeta.Resource
	colorVariation.Meta(&admin.Meta{Name: "Images", Config: &media_library.MediaBoxConfig{
		RemoteDataResource: ProductImagesResource,
		Sizes: map[string]*media.Size{
			"icon":    {Width: 50, Height: 50},
			"preview": {Width: 300, Height: 300},
			"listing": {Width: 640, Height: 640},
		},
	}})

	colorVariation.NewAttrs("-Product", "-ColorCode")
	colorVariation.EditAttrs("-Product", "-ColorCode")

	sizeVariationMeta := colorVariation.Meta(&admin.Meta{Name: "SizeVariations"})
	sizeVariation := sizeVariationMeta.Resource
	sizeVariation.EditAttrs(
		&admin.Section{
			Rows: [][]string{
				{"Size", "AvailableQuantity"},
				{"ShareableVersion"},
			},
		},
	)
	sizeVariation.NewAttrs(sizeVariation.EditAttrs())

	product.SearchAttrs("Name", "Code", "Category.Name", "Brand.Name")
	product.IndexAttrs("MainImageURL", "Name", "Price", "VersionName", "PublishLiveNow")
	product.EditAttrs(
		&admin.Section{
			Title: "Seo Meta",
			Rows: [][]string{
				{"Seo"},
			}},
		&admin.Section{
			Title: "Basic Information",
			Rows: [][]string{
				{"Name"},
				{"Code", "Price"},
				{"MainImage"},
			}},
		&admin.Section{
			Title: "Organization",
			Rows: [][]string{
				{"Category", "Gender"},
				{"Collections"},
			}},
		"ProductProperties",
		"Description",
		"ColorVariations",
		"PublishReady",
	)
	// product.ShowAttrs(product.EditAttrs())
	product.NewAttrs(product.EditAttrs())

	for _, gender := range Genders {
		var gender = gender
		product.Scope(&admin.Scope{Name: gender, Group: "Gender", Handler: func(db *gorm.DB, ctx *qor.Context) *gorm.DB {
			return db.Where("gender = ?", gender)
		}})
	}

	product.Action(&admin.Action{
		Name: "View On Site",
		URL: func(record interface{}, context *admin.Context) string {
			if product, ok := record.(*products.Product); ok {
				return fmt.Sprintf("/products/%v", product.Code)
			}
			return "#"
		},
		Modes: []string{"menu_item", "edit"},
	})

}
<commit_msg>Add datetime filter<commit_after>package products

import (
	"bytes"
	"fmt"
	"html/template"

	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/media"
	"github.com/qor/media/media_library"
	"github.com/qor/qor"
	"github.com/qor/qor-example/config/application"
	"github.com/qor/qor-example/models/products"
	"github.com/qor/qor-example/utils/funcmapmaker"
	"github.com/qor/render"
)

var Genders = []string{"Men", "Women", "Kids"}

// New new home app
func New(config *Config) *App {
	return &App{Config: config}
}

// App home app
type App struct {
	Config *Config
}

// Config home config struct
type Config struct {
}

// ConfigureApplication configure application
func (app App) ConfigureApplication(application *application.Application) {
	controller := &Controller{View: render.New(&render.Config{AssetFileSystem: application.AssetFS.NameSpace("products")}, "app/products/views")}

	funcmapmaker.AddFuncMapMaker(controller.View)
	app.ConfigureAdmin(application.Admin)

	application.Router.Get("/products", controller.Index)
	application.Router.Get("/products/{code}", controller.Show)
	application.Router.Get("/{gender:^(men|women|kids)$}", controller.Gender)
	application.Router.Get("/category/{code}", controller.Category)
}

// ConfigureAdmin configure admin interface
func (App) ConfigureAdmin(Admin *admin.Admin) {
	// Produc Management
	Admin.AddMenu(&admin.Menu{Name: "Product Management", Priority: 1})
	color := Admin.AddResource(&products.Color{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -5})
	Admin.AddResource(&products.Size{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -4})
	Admin.AddResource(&products.Material{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -4})

	category := Admin.AddResource(&products.Category{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -3})
	category.Meta(&admin.Meta{Name: "Categories", Type: "select_many"})

	collection := Admin.AddResource(&products.Collection{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -2})

	// Add ProductImage as Media Libraray
	ProductImagesResource := Admin.AddResource(&products.ProductImage{}, &admin.Config{Menu: []string{"Product Management"}, Priority: -1})

	ProductImagesResource.Filter(&admin.Filter{
		Name:       "SelectedType",
		Label:      "Media Type",
		Operations: []string{"contains"},
		Config:     &admin.SelectOneConfig{Collection: [][]string{{"video", "Video"}, {"image", "Image"}, {"file", "File"}, {"video_link", "Video Link"}}},
	})
	ProductImagesResource.Filter(&admin.Filter{
		Name:   "Color",
		Config: &admin.SelectOneConfig{RemoteDataResource: color},
	})
	ProductImagesResource.Filter(&admin.Filter{
		Name:   "Category",
		Config: &admin.SelectOneConfig{RemoteDataResource: category},
	})
	ProductImagesResource.IndexAttrs("File", "Title")

	// Add Product
	product := Admin.AddResource(&products.Product{}, &admin.Config{Menu: []string{"Product Management"}})
	product.Meta(&admin.Meta{Name: "Gender", Config: &admin.SelectOneConfig{Collection: Genders, AllowBlank: true}})

	productPropertiesRes := product.Meta(&admin.Meta{Name: "ProductProperties"}).Resource
	productPropertiesRes.NewAttrs(&admin.Section{
		Rows: [][]string{{"Name", "Value"}},
	})
	productPropertiesRes.EditAttrs(&admin.Section{
		Rows: [][]string{{"Name", "Value"}},
	})

	product.Meta(&admin.Meta{Name: "Description", Config: &admin.RichEditorConfig{Plugins: []admin.RedactorPlugin{
		{Name: "medialibrary", Source: "/admin/assets/javascripts/qor_redactor_medialibrary.js"},
		{Name: "table", Source: "/vendors/redactor_table.js"},
	},
		Settings: map[string]interface{}{
			"medialibraryUrl": "/admin/product_images",
		},
	}})
	product.Meta(&admin.Meta{Name: "Category", Config: &admin.SelectOneConfig{AllowBlank: true}})
	product.Meta(&admin.Meta{Name: "Collections", Config: &admin.SelectManyConfig{SelectMode: "bottom_sheet"}})

	product.Meta(&admin.Meta{Name: "MainImage", Config: &media_library.MediaBoxConfig{
		RemoteDataResource: ProductImagesResource,
		Max:                1,
		Sizes: map[string]*media.Size{
			"main": {Width: 560, Height: 700},
		},
	}})
	product.Meta(&admin.Meta{Name: "MainImageURL", Valuer: func(record interface{}, context *qor.Context) interface{} {
		if p, ok := record.(*products.Product); ok {
			result := bytes.NewBufferString("")
			tmpl, _ := template.New("").Parse("")
			tmpl.Execute(result, map[string]string{"image": p.MainImageURL()})
			return template.HTML(result.String())
		}
		return ""
	}})

	product.Filter(&admin.Filter{
		Name:   "Collections",
		Config: &admin.SelectOneConfig{RemoteDataResource: collection},
	})

	product.Filter(&admin.Filter{
		Name: "Name",
		Type: "string",
	})

	product.Filter(&admin.Filter{
		Name: "Code",
	})

	product.Filter(&admin.Filter{
		Name: "CreatedAt",
	})

	product.Filter(&admin.Filter{
		Name: "Price",
		Type: "number",
	})

	product.Action(&admin.Action{
		Name:        "Import Product",
		URLOpenType: "slideout",
		URL: func(record interface{}, context *admin.Context) string {
			return "/admin/workers/new?job=Import Products"
		},
		Modes: []string{"collection"},
	})

	type updateInfo struct {
		CategoryID  uint
		Category    *products.Category
		MadeCountry string
		Gender      string
	}

	updateInfoRes := Admin.NewResource(&updateInfo{})
	product.Action(&admin.Action{
		Name:     "Update Info",
		Resource: updateInfoRes,
		Handler: func(argument *admin.ActionArgument) error {
			newProductInfo := argument.Argument.(*updateInfo)
			for _, record := range argument.FindSelectedRecords() {
				fmt.Printf("%#v\n", record)
				if product, ok := record.(*products.Product); ok {
					if newProductInfo.Category != nil {
						product.Category = *newProductInfo.Category
					}
					if newProductInfo.MadeCountry != "" {
						product.MadeCountry = newProductInfo.MadeCountry
					}
					if newProductInfo.Gender != "" {
						product.Gender = newProductInfo.Gender
					}
					argument.Context.GetDB().Save(product)
				}
			}
			return nil
		},
		Modes: []string{"batch"},
	})

	product.UseTheme("grid")

	// variationsResource := product.Meta(&admin.Meta{Name: "Variations", Config: &variations.VariationsConfig{}}).Resource
	// if imagesMeta := variationsResource.GetMeta("Images"); imagesMeta != nil {
	// 	imagesMeta.Config = &media_library.MediaBoxConfig{
	// 		RemoteDataResource: ProductImagesResource,
	// 		Sizes: map[string]*media.Size{
	// 			"icon":    {Width: 50, Height: 50},
	// 			"thumb":   {Width: 100, Height: 100},
	// 			"display": {Width: 300, Height: 300},
	// 		},
	// 	}
	// }

	// variationsResource.EditAttrs("-ID", "-Product")
	// oldSearchHandler := product.SearchHandler
	// product.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB {
	// 	context.SetDB(context.GetDB().Preload("Variations.Color").Preload("Variations.Size").Preload("Variations.Material"))
	// 	return oldSearchHandler(keyword, context)
	// }
	colorVariationMeta := product.Meta(&admin.Meta{Name: "ColorVariations"})
	colorVariation := colorVariationMeta.Resource
	colorVariation.Meta(&admin.Meta{Name: "Images", Config: &media_library.MediaBoxConfig{
		RemoteDataResource: ProductImagesResource,
		Sizes: map[string]*media.Size{
			"icon":    {Width: 50, Height: 50},
			"preview": {Width: 300, Height: 300},
			"listing": {Width: 640, Height: 640},
		},
	}})

	colorVariation.NewAttrs("-Product", "-ColorCode")
	colorVariation.EditAttrs("-Product", "-ColorCode")

	sizeVariationMeta := colorVariation.Meta(&admin.Meta{Name: "SizeVariations"})
	sizeVariation := sizeVariationMeta.Resource
	sizeVariation.EditAttrs(
		&admin.Section{
			Rows: [][]string{
				{"Size", "AvailableQuantity"},
				{"ShareableVersion"},
			},
		},
	)
	sizeVariation.NewAttrs(sizeVariation.EditAttrs())

	product.SearchAttrs("Name", "Code", "Category.Name", "Brand.Name")
	product.IndexAttrs("MainImageURL", "Name", "Price", "VersionName", "PublishLiveNow")
	product.EditAttrs(
		&admin.Section{
			Title: "Seo Meta",
			Rows: [][]string{
				{"Seo"},
			}},
		&admin.Section{
			Title: "Basic Information",
			Rows: [][]string{
				{"Name"},
				{"Code", "Price"},
				{"MainImage"},
			}},
		&admin.Section{
			Title: "Organization",
			Rows: [][]string{
				{"Category", "Gender"},
				{"Collections"},
			}},
		"ProductProperties",
		"Description",
		"ColorVariations",
		"PublishReady",
	)
	// product.ShowAttrs(product.EditAttrs())
	product.NewAttrs(product.EditAttrs())

	for _, gender := range Genders {
		var gender = gender
		product.Scope(&admin.Scope{Name: gender, Group: "Gender", Handler: func(db *gorm.DB, ctx *qor.Context) *gorm.DB {
			return db.Where("gender = ?", gender)
		}})
	}

	product.Action(&admin.Action{
		Name: "View On Site",
		URL: func(record interface{}, context *admin.Context) string {
			if product, ok := record.(*products.Product); ok {
				return fmt.Sprintf("/products/%v", product.Code)
			}
			return "#"
		},
		Modes: []string{"menu_item", "edit"},
	})

}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Oyster
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package halfshell

import (
	"os"
	"path/filepath"
	"strings"
)

const (
	IMAGE_SOURCE_TYPE_FILESYSTEM ImageSourceType = "filesystem"
)

type FileSystemImageSource struct {
	Config *SourceConfig
	Logger *Logger
}

var BlankImage = &Image{
	Bytes:    make([]byte, 0),
	MimeType: "",
}

func NewFileSystemImageSourceWithConfig(config *SourceConfig) ImageSource {
	source := &FileSystemImageSource{
		Config: config,
		Logger: NewLogger("source.fs.%s", config.Name),
	}

	baseDirectory, err := os.Open(source.Config.Directory)
	if os.IsNotExist(err) {
		source.Logger.Info(source.Config.Directory, " does not exit. Creating.")
		_ = os.MkdirAll(source.Config.Directory, 0700)
		baseDirectory, err = os.Open(source.Config.Directory)
	}

	if err != nil {
		source.Logger.Fatal(err)
	}

	fileInfo, err := baseDirectory.Stat()
	if err != nil || !fileInfo.IsDir() {
		source.Logger.Fatal("Directory ", source.Config.Directory, " not a directory", err)
	}

	return source
}

func (s *FileSystemImageSource) GetImage(request *ImageSourceOptions) *Image {
	fileName := s.fileNameForRequest(request)

	file, err := os.Open(fileName)
	if err != nil {
		s.Logger.Warn("Failed to open file: %v", err)
		return nil
	}

	image, err := NewImageFromFile(file)
	if err != nil {
		s.Logger.Warn("Failed to read image: %v", err)
		return nil
	}
	return image
}

func (s *FileSystemImageSource) fileNameForRequest(request *ImageSourceOptions) string {
	// Remove the leading / from the file name and replace the
	// directory separator (/) with something safe for file names (_)
	return filepath.Join(s.Config.Directory, strings.Replace(strings.TrimLeft(request.Path, string(filepath.Separator)), string(filepath.Separator), "_", -1))
}

func init() {
	RegisterSource(IMAGE_SOURCE_TYPE_FILESYSTEM, NewFileSystemImageSourceWithConfig)
}
<commit_msg>Remove unused variable<commit_after>// Copyright (c) 2014 Oyster
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package halfshell

import (
	"os"
	"path/filepath"
	"strings"
)

const (
	IMAGE_SOURCE_TYPE_FILESYSTEM ImageSourceType = "filesystem"
)

type FileSystemImageSource struct {
	Config *SourceConfig
	Logger *Logger
}

func NewFileSystemImageSourceWithConfig(config *SourceConfig) ImageSource {
	source := &FileSystemImageSource{
		Config: config,
		Logger: NewLogger("source.fs.%s", config.Name),
	}

	baseDirectory, err := os.Open(source.Config.Directory)
	if os.IsNotExist(err) {
		source.Logger.Info(source.Config.Directory, " does not exit. Creating.")
		_ = os.MkdirAll(source.Config.Directory, 0700)
		baseDirectory, err = os.Open(source.Config.Directory)
	}

	if err != nil {
		source.Logger.Fatal(err)
	}

	fileInfo, err := baseDirectory.Stat()
	if err != nil || !fileInfo.IsDir() {
		source.Logger.Fatal("Directory ", source.Config.Directory, " not a directory", err)
	}

	return source
}

func (s *FileSystemImageSource) GetImage(request *ImageSourceOptions) *Image {
	fileName := s.fileNameForRequest(request)

	file, err := os.Open(fileName)
	if err != nil {
		s.Logger.Warn("Failed to open file: %v", err)
		return nil
	}

	image, err := NewImageFromFile(file)
	if err != nil {
		s.Logger.Warn("Failed to read image: %v", err)
		return nil
	}
	return image
}

func (s *FileSystemImageSource) fileNameForRequest(request *ImageSourceOptions) string {
	// Remove the leading / from the file name and replace the
	// directory separator (/) with something safe for file names (_)
	return filepath.Join(s.Config.Directory, strings.Replace(strings.TrimLeft(request.Path, string(filepath.Separator)), string(filepath.Separator), "_", -1))
}

func init() {
	RegisterSource(IMAGE_SOURCE_TYPE_FILESYSTEM, NewFileSystemImageSourceWithConfig)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	"github.com/skia-dev/glog"
	"go.skia.org/infra/go/metrics2"
)

// rebootMonitoringInit checks once a minute if the machine needs to be
// rebooted and stuffs that information into influx.
func rebootMonitoringInit() {
	name, err := os.Hostname()
	if err != nil {
		glog.Errorf("Failed to get hostname: %s", err)
		return
	}
	reboot := metrics2.GetBoolMetric("reboot-required", map[string]string{"host": name})
	go func() {
		for _ = range time.Tick(time.Minute) {
			_, err := os.Stat("/var/run/reboot-required")
			reboot.Update(err == nil)
		}
	}()
}
<commit_msg>pulld: Can't do arithmetic on boolean metrics, so switch to Int64.<commit_after>package main

import (
	"os"
	"time"

	"github.com/skia-dev/glog"
	"go.skia.org/infra/go/metrics2"
)

// rebootMonitoringInit checks once a minute if the machine needs to be
// rebooted and stuffs that information into influx.
func rebootMonitoringInit() {
	name, err := os.Hostname()
	if err != nil {
		glog.Errorf("Failed to get hostname: %s", err)
		return
	}
	reboot := metrics2.GetInt64Metric("reboot-required-i", map[string]string{"host": name})
	go func() {
		for _ = range time.Tick(time.Minute) {
			_, err := os.Stat("/var/run/reboot-required")
			if err == nil {
				reboot.Update(1)
			} else {
				reboot.Update(0)
			}
		}
	}()
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/cluster"
)

var flynnHostLogs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin"},
	{os.Args[0], "version"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
	{"iptables", "-L", "-v", "-n", "--line-numbers"},
}

func init() {
	Register("collect-debug-info", runCollectDebugInfo, `
usage: flynn-host collect-debug-info [options]

Options:
  --tarball      Create a tarball instead of uploading to a gist
  --include-env  Include sensitive environment variables

Collect debug information into an anonymous gist or tarball`)
}

func runCollectDebugInfo(args *docopt.Args) error {
	log := log15.New()
	if args.Bool["--tarball"] {
		log.Info("creating a tarball containing logs and debug information")
	} else {
		log.Info("uploading logs and debug information to a private, anonymous gist")
	}
	log.Info("this may take a while depending on the size of your logs")

	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	log.Info("getting flynn-host logs")
	for name, filepath := range flynnHostLogs {
		if err := gist.AddLocalFile(name, filepath); err != nil && !os.IsNotExist(err) {
			log.Error(fmt.Sprintf("error getting flynn-host log %q", filepath), "err", err)
		}
	}

	log.Info("getting job logs")
	if err := captureJobs(gist, args.Bool["--include-env"]); err != nil {
		log.Error("error getting job logs", "err", err)
	}

	log.Info("getting system information")
	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Error(fmt.Sprintf("error capturing output of %q", strings.Join(cmd, " ")), "err", err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if args.Bool["--tarball"] {
		path, err := gist.CreateTarball()
		if err != nil {
			log.Error("error creating tarball", "err", err)
			return err
		}
		log.Info(fmt.Sprintf("created tarball containing debug information at %s", path))
		return nil
	}

	if err := gist.Upload(log); err != nil {
		return err
	}

	log.Info(fmt.Sprintf("debug information uploaded to: %s", gist.URL))
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist, env bool) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content, env)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, true, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<commit_msg>host/cli: Fallback to collecting container logs from disk<commit_after>package cli

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/cluster"
)

var flynnHostLogs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin"},
	{os.Args[0], "version"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
	{"iptables", "-L", "-v", "-n", "--line-numbers"},
}

func init() {
	Register("collect-debug-info", runCollectDebugInfo, `
usage: flynn-host collect-debug-info [options]

Options:
  --tarball      Create a tarball instead of uploading to a gist
  --include-env  Include sensitive environment variables

Collect debug information into an anonymous gist or tarball`)
}

func runCollectDebugInfo(args *docopt.Args) error {
	log := log15.New()
	if args.Bool["--tarball"] {
		log.Info("creating a tarball containing logs and debug information")
	} else {
		log.Info("uploading logs and debug information to a private, anonymous gist")
	}
	log.Info("this may take a while depending on the size of your logs")

	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	log.Info("getting flynn-host logs")
	for name, filepath := range flynnHostLogs {
		if err := gist.AddLocalFile(name, filepath); err != nil && !os.IsNotExist(err) {
			log.Error(fmt.Sprintf("error getting flynn-host log %q", filepath), "err", err)
		}
	}

	log.Info("getting job logs")
	if err := captureJobs(gist, args.Bool["--include-env"]); err != nil {
		log.Error("error getting job logs, falling back to on-disk logs", "err", err)
		debugCmds = append(debugCmds, []string{"bash", "-c", "tail -n +1 /tmp/flynn-host-logs/**/*.log"})
	}

	log.Info("getting system information")
	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Error(fmt.Sprintf("error capturing output of %q", strings.Join(cmd, " ")), "err", err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if args.Bool["--tarball"] {
		path, err := gist.CreateTarball()
		if err != nil {
			log.Error("error creating tarball", "err", err)
			return err
		}
		log.Info(fmt.Sprintf("created tarball containing debug information at %s", path))
		return nil
	}

	if err := gist.Upload(log); err != nil {
		return err
	}

	log.Info(fmt.Sprintf("debug information uploaded to: %s", gist.URL))
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist, env bool) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content, env)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, true, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<|endoftext|>


<commit_before>// This file contains functions for transpiling common branching and control
// flow, such as "if", "while", "do" and "for". The more complicated control
// flows like "switch" will be put into their own file of the same or sensible
// name.

package transpiler

import (
	"fmt"
	"go/token"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/types"
	"github.com/elliotchance/c2go/util"

	goast "go/ast"
)

func transpileIfStmt(n *ast.IfStmt, p *program.Program) (
	*goast.IfStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	children := n.Children

	// There is always 4 or 5 children in an IfStmt. For example:
	//
	//     if (i == 0) {
	//         return 0;
	//     } else {
	//         return 1;
	//     }
	//
	// 1. Not sure what this is for. This gets removed.
	// 2. Not sure what this is for.
	// 3. conditional = BinaryOperator: i == 0
	// 4. body = CompoundStmt: { return 0; }
	// 5. elseBody = CompoundStmt: { return 1; }
	//
	// elseBody will be nil if there is no else clause.

	// On linux I have seen only 4 children for an IfStmt with the same
	// definitions above, but missing the first argument. Since we don't
	// know what the first argument is for anyway we will just remove it on
	// Mac if necessary.
	if len(children) == 5 && children[0] != nil {
		panic("non-nil child 0 in IfStmt")
	}
	if len(children) == 5 {
		children = children[1:]
	}

	// From here on there must be 4 children.
	if len(children) != 4 {
		panic(fmt.Sprintf("Expected 4 children in IfStmt, got %#v", children))
	}

	// Maybe we will discover what the nil value is?
	if children[0] != nil {
		panic("non-nil child 0 in IfStmt")
	}

	conditional, conditionalType, newPre, newPost, err := transpileToExpr(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	// The condition in Go must always be a bool.
	boolCondition, err := types.CastExpr(p, conditional, conditionalType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, boolCondition == nil))

	if boolCondition == nil {
		boolCondition = util.NewNil()
	}

	body, newPre, newPost, err := transpileToBlockStmt(children[2], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	r := &goast.IfStmt{
		Cond: boolCondition,
		Body: body,
	}

	if children[3] != nil {
		elseBody, newPre, newPost, err := transpileToBlockStmt(children[3], p)
		if err != nil {
			return nil, nil, nil, err
		}

		preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

		r.Else = elseBody
	}

	return r, newPre, newPost, nil
}

func transpileForStmt(n *ast.ForStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}

	children := n.Children

	// There are always 5 children in a ForStmt, for example:
	//
	//     for ( c = 0 ; c < n ; c++ ) {
	//         doSomething();
	//     }
	//
	// 1. initExpression = BinaryStmt: c = 0
	// 2. Not sure what this is for, but it's always nil. There is a panic
	//    below in case we discover what it is used for (pun intended).
	// 3. conditionalExpression = BinaryStmt: c < n
	// 4. stepExpression = BinaryStmt: c++
	// 5. body = CompoundStmt: { CallExpr }

	if len(children) != 5 {
		panic(fmt.Sprintf("Expected 5 children in ForStmt, got %#v", children))
	}

	// TODO: The second child of a ForStmt appears to always be null.
	// Are there any cases where it is used?
	if children[1] != nil {
		panic("non-nil child 1 in ForStmt")
	}

	// If we have 2 and more initializations like
	// in operator for
	// for( a = 0, b = 0, c = 0; a < 5; a ++)
	switch children[0].(type) {
	case *ast.BinaryOperator:
		c := children[0].(*ast.BinaryOperator)
		if c.Operator == "," {
			// recursive action to code like that:
			// a = 0;
			// b = 0;
			// for(c = 0 ; a < 5 ; a++)
			//
			// Add after in preStmts
			beforeFor, newPre, newPost, err := transpileToStmt(c.Children[0], p)
			if err != nil {
				return nil, nil, nil, err
			}
			preStmts = append(preStmts, beforeFor)
			preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)
			// Now, in init only initialization
			children[0] = c.Children[len(c.Children)-1]
		}
	}

	init, newPre, newPost, err := transpileToStmt(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// If we have 2 and more increments
	// in operator for
	// for( a = 0; a < 5; a ++, b++, c+=2)
	switch children[3].(type) {
	case *ast.BinaryOperator:
		c := children[3].(*ast.BinaryOperator)
		if c.Operator == "," {
			// recursive action to code like that:
			// a = 0;
			// b = 0;
			// for(a = 0 ; a < 5 ; c+=2){
			// body
			// a++;
			// b++
			// }
			//
			// Add after in preStmts
			var compound ast.CompoundStmt
			compound.Children = append(compound.Children, children[4])
			compound.Children = append(compound.Children, c.Children[0:len(c.Children)-1]...)
			children[4] = &compound
			children[3] = c.Children[len(c.Children)-1]
		}
	}

	post, newPre, newPost, err := transpileToStmt(children[3], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	body, newPre, newPost, err := transpileToBlockStmt(children[4], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// The condition can be nil. This means an infinite loop and will be
	// rendered in Go as "for {".
	var condition goast.Expr
	if children[2] != nil {
		var conditionType string
		var newPre, newPost []goast.Stmt
		condition, conditionType, newPre, newPost, err = transpileToExpr(children[2], p)
		if err != nil {
			return nil, nil, nil, err
		}

		preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

		condition, err = types.CastExpr(p, condition, conditionType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, condition == nil))

		if condition == nil {
			condition = util.NewNil()
		}
	}

	return &goast.ForStmt{
		Init: init,
		Cond: condition,
		Post: post,
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileWhileStmt(n *ast.WhileStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}

	// TODO: The first child of a WhileStmt appears to always be null.
	// Are there any cases where it is used?
	children := n.Children[1:]

	body, newPre, newPost, err := transpileToBlockStmt(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	condition, conditionType, newPre, newPost, err := transpileToExpr(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	cond, err := types.CastExpr(p, condition, conditionType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, cond == nil))

	if cond == nil {
		cond = util.NewNil()
	}

	return &goast.ForStmt{
		Cond: cond,
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileDoStmt(n *ast.DoStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	children := n.Children

	body, newPre, newPost, err := transpileToBlockStmt(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	condition, conditionType, newPre, newPost, err := transpileToExpr(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// Add IfStmt to the end of the loop to check the condition.
	x, err := types.CastExpr(p, condition, conditionType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, x == nil))

	if x == nil {
		x = util.NewNil()
	}

	body.List = append(body.List, &goast.IfStmt{
		Cond: &goast.UnaryExpr{
			Op: token.NOT,
			X:  x,
		},
		Body: &goast.BlockStmt{
			List: []goast.Stmt{&goast.BranchStmt{Tok: token.BREAK}},
		},
	})

	return &goast.ForStmt{
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileContinueStmt(n *ast.ContinueStmt, p *program.Program) (*goast.BranchStmt, error) {
	return &goast.BranchStmt{
		Tok: token.CONTINUE,
	}, nil
}
<commit_msg>fix: correct COMMA for init<commit_after>// This file contains functions for transpiling common branching and control
// flow, such as "if", "while", "do" and "for". The more complicated control
// flows like "switch" will be put into their own file of the same or sensible
// name.

package transpiler

import (
	"fmt"
	"go/token"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/types"
	"github.com/elliotchance/c2go/util"

	goast "go/ast"
)

func transpileIfStmt(n *ast.IfStmt, p *program.Program) (
	*goast.IfStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	children := n.Children

	// There is always 4 or 5 children in an IfStmt. For example:
	//
	//     if (i == 0) {
	//         return 0;
	//     } else {
	//         return 1;
	//     }
	//
	// 1. Not sure what this is for. This gets removed.
	// 2. Not sure what this is for.
	// 3. conditional = BinaryOperator: i == 0
	// 4. body = CompoundStmt: { return 0; }
	// 5. elseBody = CompoundStmt: { return 1; }
	//
	// elseBody will be nil if there is no else clause.

	// On linux I have seen only 4 children for an IfStmt with the same
	// definitions above, but missing the first argument. Since we don't
	// know what the first argument is for anyway we will just remove it on
	// Mac if necessary.
	if len(children) == 5 && children[0] != nil {
		panic("non-nil child 0 in IfStmt")
	}
	if len(children) == 5 {
		children = children[1:]
	}

	// From here on there must be 4 children.
	if len(children) != 4 {
		panic(fmt.Sprintf("Expected 4 children in IfStmt, got %#v", children))
	}

	// Maybe we will discover what the nil value is?
	if children[0] != nil {
		panic("non-nil child 0 in IfStmt")
	}

	conditional, conditionalType, newPre, newPost, err := transpileToExpr(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	// The condition in Go must always be a bool.
	boolCondition, err := types.CastExpr(p, conditional, conditionalType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, boolCondition == nil))

	if boolCondition == nil {
		boolCondition = util.NewNil()
	}

	body, newPre, newPost, err := transpileToBlockStmt(children[2], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	r := &goast.IfStmt{
		Cond: boolCondition,
		Body: body,
	}

	if children[3] != nil {
		elseBody, newPre, newPost, err := transpileToBlockStmt(children[3], p)
		if err != nil {
			return nil, nil, nil, err
		}

		preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

		r.Else = elseBody
	}

	return r, newPre, newPost, nil
}

func transpileForStmt(n *ast.ForStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}

	children := n.Children

	// There are always 5 children in a ForStmt, for example:
	//
	//     for ( c = 0 ; c < n ; c++ ) {
	//         doSomething();
	//     }
	//
	// 1. initExpression = BinaryStmt: c = 0
	// 2. Not sure what this is for, but it's always nil. There is a panic
	//    below in case we discover what it is used for (pun intended).
	// 3. conditionalExpression = BinaryStmt: c < n
	// 4. stepExpression = BinaryStmt: c++
	// 5. body = CompoundStmt: { CallExpr }

	if len(children) != 5 {
		panic(fmt.Sprintf("Expected 5 children in ForStmt, got %#v", children))
	}

	// TODO: The second child of a ForStmt appears to always be null.
	// Are there any cases where it is used?
	if children[1] != nil {
		panic("non-nil child 1 in ForStmt")
	}

	// If we have 2 and more initializations like
	// in operator for
	// for( a = 0, b = 0, c = 0; a < 5; a ++)
	switch children[0].(type) {
	case *ast.BinaryOperator:
		c := children[0].(*ast.BinaryOperator)
		if c.Operator == "," {
			// recursive action to code like that:
			// a = 0;
			// b = 0;
			// for(c = 0 ; a < 5 ; a++)
			//
			before, newPre, newPost, err := transpileToStmt(c.Children[0], p)
			if err != nil {
				return nil, nil, nil, err
			}
			preStmts = append(preStmts, newPre...)
			preStmts = append(preStmts, before)
			preStmts = append(preStmts, newPost...)
			children[0] = c.Children[1]
		}
	}

	init, newPre, newPost, err := transpileToStmt(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// If we have 2 and more increments
	// in operator for
	// for( a = 0; a < 5; a ++, b++, c+=2)
	switch children[3].(type) {
	case *ast.BinaryOperator:
		c := children[3].(*ast.BinaryOperator)
		if c.Operator == "," {
			// recursive action to code like that:
			// a = 0;
			// b = 0;
			// for(a = 0 ; a < 5 ; c+=2){
			// body
			// a++;
			// b++
			// }
			//
			var compound ast.CompoundStmt
			compound.Children = append(compound.Children, children[4])
			compound.Children = append(compound.Children, c.Children[0:len(c.Children)-1]...)
			children[4] = &compound
			children[3] = c.Children[len(c.Children)-1]
		}
	}

	post, newPre, newPost, err := transpileToStmt(children[3], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	body, newPre, newPost, err := transpileToBlockStmt(children[4], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// The condition can be nil. This means an infinite loop and will be
	// rendered in Go as "for {".
	var condition goast.Expr
	if children[2] != nil {
		var conditionType string
		var newPre, newPost []goast.Stmt
		condition, conditionType, newPre, newPost, err = transpileToExpr(children[2], p)
		if err != nil {
			return nil, nil, nil, err
		}

		preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

		condition, err = types.CastExpr(p, condition, conditionType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, condition == nil))

		if condition == nil {
			condition = util.NewNil()
		}
	}

	return &goast.ForStmt{
		Init: init,
		Cond: condition,
		Post: post,
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileWhileStmt(n *ast.WhileStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}

	// TODO: The first child of a WhileStmt appears to always be null.
	// Are there any cases where it is used?
	children := n.Children[1:]

	body, newPre, newPost, err := transpileToBlockStmt(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	condition, conditionType, newPre, newPost, err := transpileToExpr(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	cond, err := types.CastExpr(p, condition, conditionType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, cond == nil))

	if cond == nil {
		cond = util.NewNil()
	}

	return &goast.ForStmt{
		Cond: cond,
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileDoStmt(n *ast.DoStmt, p *program.Program) (
	*goast.ForStmt, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	children := n.Children

	body, newPre, newPost, err := transpileToBlockStmt(children[0], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	condition, conditionType, newPre, newPost, err := transpileToExpr(children[1], p)
	if err != nil {
		return nil, nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	// Add IfStmt to the end of the loop to check the condition.
	x, err := types.CastExpr(p, condition, conditionType, "bool")
	p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, x == nil))

	if x == nil {
		x = util.NewNil()
	}

	body.List = append(body.List, &goast.IfStmt{
		Cond: &goast.UnaryExpr{
			Op: token.NOT,
			X:  x,
		},
		Body: &goast.BlockStmt{
			List: []goast.Stmt{&goast.BranchStmt{Tok: token.BREAK}},
		},
	})

	return &goast.ForStmt{
		Body: body,
	}, preStmts, postStmts, nil
}

func transpileContinueStmt(n *ast.ContinueStmt, p *program.Program) (*goast.BranchStmt, error) {
	return &goast.BranchStmt{
		Tok: token.CONTINUE,
	}, nil
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"sync"
	"syscall"
)

func main() {
	var mountProc bool
	flag.BoolVar(&mountProc, "withproc", mountProc, "mount /proc inside container (docker doesn't need this)")
	flag.Parse()
	if len(flag.Args()) == 0 {
		log.Fatal("no command to run")
	}
	if mountProc {
		if err := syscall.Mount("proc", "/proc", "proc", 0, ""); err != nil {
			log.Fatal("failed to mount /proc: " + err.Error())
		}
	}
	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
	)
	ps := newProcSet()
	go func() {
		for sig := range sigch {
			log.Printf("%s, propagating signal to children", sig)
			ps.Signal(sig)
		}
	}()
	for _, arg := range flag.Args() {
		cmd := exec.Command(arg)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Start(); err != nil {
			ps.Signal(syscall.SIGCONT)
			ps.Signal(syscall.SIGTERM)
			log.Fatal(err)
		}
		ps.Add(cmd.Process)
	}
	finishing := false
	var ws syscall.WaitStatus
	for {
		pid, err := syscall.Wait4(-1, &ws, 0, nil)
		if err != nil {
			log.Fatal(err)
		}
		if ps.HasPid(pid) {
			if finishing {
				log.Printf("watched pid %d finished (%s)",
					pid, exitReason(ws))
				continue
			}
			log.Printf("watched pid %d finished (%s), terminating others",
				pid, exitReason(ws))
			ps.Signal(syscall.SIGCONT)
			ps.Signal(syscall.SIGTERM)
			finishing = true
		}
	}
}

func init() {
	log.SetFlags(0)
	log.SetPrefix("boxinit: ")
}

func exitReason(ws syscall.WaitStatus) string {
	if ws.Signaled() {
		return ws.Signal().String()
	}
	return "code " + strconv.Itoa(ws.ExitStatus())
}

func newProcSet() *procSet {
	return &procSet{
		h: make(map[*os.Process]struct{}),
	}
}

type procSet struct {
	m sync.RWMutex
	h map[*os.Process]struct{}
}

func (ps *procSet) HasPid(pid int) bool {
	ps.m.RLock()
	defer ps.m.RUnlock()
	for p := range ps.h {
		if p.Pid == pid {
			return true
		}
	}
	return false
}

func (ps *procSet) Add(p *os.Process) {
	ps.m.Lock()
	ps.h[p] = struct{}{}
	ps.m.Unlock()
}

func (ps *procSet) Signal(sig os.Signal) {
	// if we're really working as init (pid 1), send signal to all
	// processes except ourself
	if syscall.Getpid() == 1 {
		if err := syscall.Kill(-1, sig.(syscall.Signal)); err != nil {
			log.Print("error sending signal: ", err)
		}
		return
	}
	// send signal only to processes we explicitly spawned
	ps.m.RLock()
	for p := range ps.h {
		p.Signal(sig)
	}
	ps.m.RUnlock()
}
<commit_msg>Handle SIGQUIT<commit_after>// +build linux

package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"sync"
	"syscall"
)

func main() {
	var mountProc bool
	flag.BoolVar(&mountProc, "withproc", mountProc, "mount /proc inside container (docker doesn't need this)")
	flag.Parse()
	if len(flag.Args()) == 0 {
		log.Fatal("no command to run")
	}
	if mountProc {
		if err := syscall.Mount("proc", "/proc", "proc", 0, ""); err != nil {
			log.Fatal("failed to mount /proc: " + err.Error())
		}
	}
	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
	)
	ps := newProcSet()
	go func() {
		for sig := range sigch {
			log.Printf("%s, propagating signal to children", sig)
			ps.Signal(sig)
		}
	}()
	for _, arg := range flag.Args() {
		cmd := exec.Command(arg)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Start(); err != nil {
			ps.Signal(syscall.SIGCONT)
			ps.Signal(syscall.SIGTERM)
			log.Fatal(err)
		}
		ps.Add(cmd.Process)
	}
	finishing := false
	var ws syscall.WaitStatus
	for {
		pid, err := syscall.Wait4(-1, &ws, 0, nil)
		if err != nil {
			log.Fatal(err)
		}
		if ps.HasPid(pid) {
			if finishing {
				log.Printf("watched pid %d finished (%s)",
					pid, exitReason(ws))
				continue
			}
			log.Printf("watched pid %d finished (%s), terminating others",
				pid, exitReason(ws))
			ps.Signal(syscall.SIGCONT)
			ps.Signal(syscall.SIGTERM)
			finishing = true
		}
	}
}

func init() {
	log.SetFlags(0)
	log.SetPrefix("boxinit: ")
}

func exitReason(ws syscall.WaitStatus) string {
	if ws.Signaled() {
		return ws.Signal().String()
	}
	return "code " + strconv.Itoa(ws.ExitStatus())
}

func newProcSet() *procSet {
	return &procSet{
		h: make(map[*os.Process]struct{}),
	}
}

type procSet struct {
	m sync.RWMutex
	h map[*os.Process]struct{}
}

func (ps *procSet) HasPid(pid int) bool {
	ps.m.RLock()
	defer ps.m.RUnlock()
	for p := range ps.h {
		if p.Pid == pid {
			return true
		}
	}
	return false
}

func (ps *procSet) Add(p *os.Process) {
	ps.m.Lock()
	ps.h[p] = struct{}{}
	ps.m.Unlock()
}

func (ps *procSet) Signal(sig os.Signal) {
	// if we're really working as init (pid 1), send signal to all
	// processes except ourself
	if syscall.Getpid() == 1 {
		if err := syscall.Kill(-1, sig.(syscall.Signal)); err != nil {
			log.Print("error sending signal: ", err)
		}
		return
	}
	// send signal only to processes we explicitly spawned
	ps.m.RLock()
	for p := range ps.h {
		p.Signal(sig)
	}
	ps.m.RUnlock()
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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.

// nolint:errcheck
package humioexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/humioexporter"

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/consumer/consumererror"
)

// HumioUnstructuredEvents represents a payload of multiple unstructured events (strings) to send to Humio
type HumioUnstructuredEvents struct {
	// Key-value pairs to associate with the messages as metadata
	Fields map[string]string `json:"fields,omitempty"`

	// Tags used to target specific data sources in Humio
	Tags map[string]string `json:"tags,omitempty"`

	// The name of the parser to handle these messages inside Humio
	Type string `json:"type,omitempty"`

	// The series of unstructured messages
	Messages []string `json:"messages"`
}

// HumioStructuredEvents represents a payload of multiple structured events to send to Humio
type HumioStructuredEvents struct {
	// Tags used to target specific data sources in Humio
	Tags map[string]string `json:"tags,omitempty"`

	// The series of structured events
	Events []*HumioStructuredEvent `json:"events"`
}

// HumioStructuredEvent represents a single structured event to send to Humio
type HumioStructuredEvent struct {
	// The time where the event occurred
	Timestamp time.Time

	// Whether to serialize the timestamp as Unix or ISO
	AsUnix bool

	// The event payload
	Attributes interface{}
}

// MarshalJSON formats the timestamp in a HumioStructuredEvent as either an ISO string or a
// Unix timestamp in milliseconds with time zone
func (e *HumioStructuredEvent) MarshalJSON() ([]byte, error) {
	if e.AsUnix {
		return json.Marshal(struct {
			Timestamp  int64       `json:"timestamp"`
			TimeZone   string      `json:"timezone"`
			Attributes interface{} `json:"attributes,omitempty"`
		}{
			Timestamp:  e.Timestamp.Local().UnixNano() * int64(time.Nanosecond) / int64(time.Millisecond),
			TimeZone:   e.Timestamp.Location().String(),
			Attributes: e.Attributes,
		})
	}

	return json.Marshal(struct {
		Timestamp  time.Time   `json:"timestamp"`
		Attributes interface{} `json:"attributes,omitempty"`
	}{
		Timestamp:  e.Timestamp,
		Attributes: e.Attributes,
	})
}

// Abstract interface describing the capabilities of an HTTP client for sending
// unstructured and structured events
type exporterClient interface {
	sendUnstructuredEvents(context.Context, []*HumioUnstructuredEvents) error
	sendStructuredEvents(context.Context, []*HumioStructuredEvents) error
}

// A concrete HTTP client for sending unstructured and structured events to Humio
type humioClient struct {
	cfg      *Config
	client   *http.Client
	gzipPool *sync.Pool
	settings component.TelemetrySettings
}

// Constructs a new HTTP client for sending payloads to Humio
func newHumioClient(cfg *Config, settings component.TelemetrySettings, host component.Host) (exporterClient, error) {
	client, err := cfg.HTTPClientSettings.ToClient(host.GetExtensions(), settings)
	if err != nil {
		return nil, err
	}

	return &humioClient{
		cfg:    cfg,
		client: client,
		gzipPool: &sync.Pool{New: func() interface{} {
			return gzip.NewWriter(nil)
		}},
		settings: settings,
	}, nil
}

// Send a payload of unstructured events to the corresponding Humio API. Will eventually be renamed as sendLogs
func (h *humioClient) sendUnstructuredEvents(ctx context.Context, evts []*HumioUnstructuredEvents) error {
	return h.sendEvents(ctx, evts, h.cfg.unstructuredEndpoint.String(), h.cfg.Logs.IngestToken)
}

// Send a payload of structured events to the corresponding Humio API. Will eventually be renamed as sendTraces
func (h *humioClient) sendStructuredEvents(ctx context.Context, evts []*HumioStructuredEvents) error {
	return h.sendEvents(ctx, evts, h.cfg.structuredEndpoint.String(), h.cfg.Traces.IngestToken)
}

// Send a payload of generic events to the specified Humio API. This method should
// never be called directly
func (h *humioClient) sendEvents(ctx context.Context, evts interface{}, url string, token string) error {
	body, err := h.encodeBody(evts)
	if err != nil {
		return consumererror.NewPermanent(err)
	}

	req, err := http.NewRequestWithContext(
		ctx,
		"POST",
		url,
		body,
	)
	if err != nil {
		return consumererror.NewPermanent(err)
	}

	for h, v := range h.cfg.Headers {
		req.Header.Set(h, v)
	}
	req.Header.Set("authorization", "Bearer "+token)

	res, err := h.client.Do(req)
	if err != nil {
		return err
	}
	// Response body needs to both be read to EOF and closed to avoid leaks
	defer res.Body.Close()
	io.Copy(ioutil.Discard, res.Body)

	// If an error has occurred, determine if it would make sense to retry
	// This check is not exhaustive, but should cover the most common cases
	if res.StatusCode < http.StatusOK ||
		res.StatusCode >= http.StatusMultipleChoices {
		err = errors.New("unable to export events to Humio, got " + res.Status)

		// These indicate a programming or configuration error
		if res.StatusCode == http.StatusBadRequest ||
			res.StatusCode == http.StatusUnauthorized ||
			res.StatusCode == http.StatusForbidden {
			return consumererror.NewPermanent(err)
		}

		return err
	}

	return nil
}

// Encode the specified payload as json
func (h *humioClient) encodeBody(body interface{}) (io.Reader, error) {
	b, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	if h.cfg.DisableCompression {
		return bytes.NewReader(b), nil
	}
	return h.compressBody(b)
}

func (h *humioClient) compressBody(body []byte) (io.Reader, error) {
	gzipper := h.gzipPool.Get().(*gzip.Writer)
	defer h.gzipPool.Put(gzipper)

	// Must reset writer because we reuse it
	b := new(bytes.Buffer)
	gzipper.Reset(b)

	_, err := gzipper.Write(body)
	if err != nil {
		return nil, err
	}

	err = gzipper.Close()
	if err != nil {
		return nil, err
	}

	return b, nil
}
<commit_msg>fix errcheck issue for humio (#10354)<commit_after>// Copyright The OpenTelemetry 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 humioexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/humioexporter"

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/consumer/consumererror"
)

// HumioUnstructuredEvents represents a payload of multiple unstructured events (strings) to send to Humio
type HumioUnstructuredEvents struct {
	// Key-value pairs to associate with the messages as metadata
	Fields map[string]string `json:"fields,omitempty"`

	// Tags used to target specific data sources in Humio
	Tags map[string]string `json:"tags,omitempty"`

	// The name of the parser to handle these messages inside Humio
	Type string `json:"type,omitempty"`

	// The series of unstructured messages
	Messages []string `json:"messages"`
}

// HumioStructuredEvents represents a payload of multiple structured events to send to Humio
type HumioStructuredEvents struct {
	// Tags used to target specific data sources in Humio
	Tags map[string]string `json:"tags,omitempty"`

	// The series of structured events
	Events []*HumioStructuredEvent `json:"events"`
}

// HumioStructuredEvent represents a single structured event to send to Humio
type HumioStructuredEvent struct {
	// The time where the event occurred
	Timestamp time.Time

	// Whether to serialize the timestamp as Unix or ISO
	AsUnix bool

	// The event payload
	Attributes interface{}
}

// MarshalJSON formats the timestamp in a HumioStructuredEvent as either an ISO string or a
// Unix timestamp in milliseconds with time zone
func (e *HumioStructuredEvent) MarshalJSON() ([]byte, error) {
	if e.AsUnix {
		return json.Marshal(struct {
			Timestamp  int64       `json:"timestamp"`
			TimeZone   string      `json:"timezone"`
			Attributes interface{} `json:"attributes,omitempty"`
		}{
			Timestamp:  e.Timestamp.Local().UnixNano() * int64(time.Nanosecond) / int64(time.Millisecond),
			TimeZone:   e.Timestamp.Location().String(),
			Attributes: e.Attributes,
		})
	}

	return json.Marshal(struct {
		Timestamp  time.Time   `json:"timestamp"`
		Attributes interface{} `json:"attributes,omitempty"`
	}{
		Timestamp:  e.Timestamp,
		Attributes: e.Attributes,
	})
}

// Abstract interface describing the capabilities of an HTTP client for sending
// unstructured and structured events
type exporterClient interface {
	sendUnstructuredEvents(context.Context, []*HumioUnstructuredEvents) error
	sendStructuredEvents(context.Context, []*HumioStructuredEvents) error
}

// A concrete HTTP client for sending unstructured and structured events to Humio
type humioClient struct {
	cfg      *Config
	client   *http.Client
	gzipPool *sync.Pool
	settings component.TelemetrySettings
}

// Constructs a new HTTP client for sending payloads to Humio
func newHumioClient(cfg *Config, settings component.TelemetrySettings, host component.Host) (exporterClient, error) {
	client, err := cfg.HTTPClientSettings.ToClient(host.GetExtensions(), settings)
	if err != nil {
		return nil, err
	}

	return &humioClient{
		cfg:    cfg,
		client: client,
		gzipPool: &sync.Pool{New: func() interface{} {
			return gzip.NewWriter(nil)
		}},
		settings: settings,
	}, nil
}

// Send a payload of unstructured events to the corresponding Humio API. Will eventually be renamed as sendLogs
func (h *humioClient) sendUnstructuredEvents(ctx context.Context, evts []*HumioUnstructuredEvents) error {
	return h.sendEvents(ctx, evts, h.cfg.unstructuredEndpoint.String(), h.cfg.Logs.IngestToken)
}

// Send a payload of structured events to the corresponding Humio API. Will eventually be renamed as sendTraces
func (h *humioClient) sendStructuredEvents(ctx context.Context, evts []*HumioStructuredEvents) error {
	return h.sendEvents(ctx, evts, h.cfg.structuredEndpoint.String(), h.cfg.Traces.IngestToken)
}

// Send a payload of generic events to the specified Humio API. This method should
// never be called directly
func (h *humioClient) sendEvents(ctx context.Context, evts interface{}, url string, token string) error {
	body, err := h.encodeBody(evts)
	if err != nil {
		return consumererror.NewPermanent(err)
	}

	req, err := http.NewRequestWithContext(
		ctx,
		"POST",
		url,
		body,
	)
	if err != nil {
		return consumererror.NewPermanent(err)
	}

	for h, v := range h.cfg.Headers {
		req.Header.Set(h, v)
	}
	req.Header.Set("authorization", "Bearer "+token)

	res, err := h.client.Do(req)
	if err != nil {
		return err
	}
	// Response body needs to both be read to EOF and closed to avoid leaks
	defer res.Body.Close()
	_, err = io.Copy(ioutil.Discard, res.Body)
	if err != nil {
		return err
	}

	// If an error has occurred, determine if it would make sense to retry
	// This check is not exhaustive, but should cover the most common cases
	if res.StatusCode < http.StatusOK ||
		res.StatusCode >= http.StatusMultipleChoices {
		err = errors.New("unable to export events to Humio, got " + res.Status)

		// These indicate a programming or configuration error
		if res.StatusCode == http.StatusBadRequest ||
			res.StatusCode == http.StatusUnauthorized ||
			res.StatusCode == http.StatusForbidden {
			return consumererror.NewPermanent(err)
		}

		return err
	}

	return nil
}

// Encode the specified payload as json
func (h *humioClient) encodeBody(body interface{}) (io.Reader, error) {
	b, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	if h.cfg.DisableCompression {
		return bytes.NewReader(b), nil
	}
	return h.compressBody(b)
}

func (h *humioClient) compressBody(body []byte) (io.Reader, error) {
	gzipper := h.gzipPool.Get().(*gzip.Writer)
	defer h.gzipPool.Put(gzipper)

	// Must reset writer because we reuse it
	b := new(bytes.Buffer)
	gzipper.Reset(b)

	_, err := gzipper.Write(body)
	if err != nil {
		return nil, err
	}

	err = gzipper.Close()
	if err != nil {
		return nil, err
	}

	return b, nil
}
<|endoftext|>


<commit_before>package http

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/urfave/negroni"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/provider"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

// Opts - http server options
type Opts struct {
	Port int

	// available providers
	Providers provider.Providers

	ApprovalManager approvals.Manager
}

// TriggerServer - webhook trigger & healthcheck server
type TriggerServer struct {
	providers        provider.Providers
	approvalsManager approvals.Manager
	port             int
	server           *http.Server
	router           *mux.Router
}

// NewTriggerServer - create new HTTP trigger based server
func NewTriggerServer(opts *Opts) *TriggerServer {
	return &TriggerServer{
		port:             opts.Port,
		providers:        opts.Providers,
		approvalsManager: opts.ApprovalManager,
		router:           mux.NewRouter(),
	}
}

// Start - start server
func (s *TriggerServer) Start() error {

	s.registerRoutes(s.router)

	n := negroni.New(negroni.NewRecovery())
	n.UseHandler(s.router)

	s.server = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: n,
	}

	log.WithFields(log.Fields{
		"port": s.port,
	}).Info("webhook trigger server starting...")
	return s.server.ListenAndServe()
}

// Stop - stop webhook server
func (s *TriggerServer) Stop() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	s.server.Shutdown(ctx)
}

func getID(req *http.Request) string {
	return mux.Vars(req)["id"]
}

func (s *TriggerServer) registerRoutes(mux *mux.Router) {
	// health endpoint for k8s to be happy
	mux.HandleFunc("/healthz", s.healthHandler).Methods("GET", "OPTIONS")
	// version handler
	mux.HandleFunc("/version", s.versionHandler).Methods("GET", "OPTIONS")

	// approvals
	mux.HandleFunc("/v1/approvals", s.approvalsHandler).Methods("GET", "OPTIONS")
	mux.HandleFunc("/v1/approvals/{id}", s.approvalDeleteHandler).Methods("DELETE", "OPTIONS")

	// native webhooks handler
	mux.HandleFunc("/v1/webhooks/native", s.nativeHandler).Methods("POST", "OPTIONS")

	// dockerhub webhooks handler
	mux.HandleFunc("/v1/webhooks/dockerhub", s.dockerHubHandler).Methods("POST", "OPTIONS")
	mux.HandleFunc("/v1/webhooks/quay", s.quayHandler).Methods("POST", "OPTIONS")
}

func (s *TriggerServer) healthHandler(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusOK)
}

func (s *TriggerServer) versionHandler(resp http.ResponseWriter, req *http.Request) {
	v := version.GetKeelVersion()

	encoded, err := json.Marshal(v)
	if err != nil {
		log.WithError(err).Error("trigger.http: failed to marshal version")
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.WriteHeader(http.StatusOK)
	resp.Write(encoded)
}

func (s *TriggerServer) trigger(event types.Event) error {
	return s.providers.Submit(event)
}
<commit_msg>route to approvals function<commit_after>package http

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/urfave/negroni"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/provider"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

// Opts - http server options
type Opts struct {
	Port int

	// available providers
	Providers provider.Providers

	ApprovalManager approvals.Manager
}

// TriggerServer - webhook trigger & healthcheck server
type TriggerServer struct {
	providers        provider.Providers
	approvalsManager approvals.Manager
	port             int
	server           *http.Server
	router           *mux.Router
}

// NewTriggerServer - create new HTTP trigger based server
func NewTriggerServer(opts *Opts) *TriggerServer {
	return &TriggerServer{
		port:             opts.Port,
		providers:        opts.Providers,
		approvalsManager: opts.ApprovalManager,
		router:           mux.NewRouter(),
	}
}

// Start - start server
func (s *TriggerServer) Start() error {

	s.registerRoutes(s.router)

	n := negroni.New(negroni.NewRecovery())
	n.UseHandler(s.router)

	s.server = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: n,
	}

	log.WithFields(log.Fields{
		"port": s.port,
	}).Info("webhook trigger server starting...")
	return s.server.ListenAndServe()
}

// Stop - stop webhook server
func (s *TriggerServer) Stop() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	s.server.Shutdown(ctx)
}

func getID(req *http.Request) string {
	return mux.Vars(req)["id"]
}

func (s *TriggerServer) registerRoutes(mux *mux.Router) {
	// health endpoint for k8s to be happy
	mux.HandleFunc("/healthz", s.healthHandler).Methods("GET", "OPTIONS")
	// version handler
	mux.HandleFunc("/version", s.versionHandler).Methods("GET", "OPTIONS")

	// approvals
	mux.HandleFunc("/v1/approvals", s.approvalsHandler).Methods("GET", "OPTIONS")
	// approving
	mux.HandleFunc("/v1/approvals", s.approvalApproveHandler).Methods("POST", "OPTIONS")
	mux.HandleFunc("/v1/approvals/{id}", s.approvalDeleteHandler).Methods("DELETE", "OPTIONS")

	// native webhooks handler
	mux.HandleFunc("/v1/webhooks/native", s.nativeHandler).Methods("POST", "OPTIONS")

	// dockerhub webhooks handler
	mux.HandleFunc("/v1/webhooks/dockerhub", s.dockerHubHandler).Methods("POST", "OPTIONS")
	mux.HandleFunc("/v1/webhooks/quay", s.quayHandler).Methods("POST", "OPTIONS")
}

func (s *TriggerServer) healthHandler(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusOK)
}

func (s *TriggerServer) versionHandler(resp http.ResponseWriter, req *http.Request) {
	v := version.GetKeelVersion()

	encoded, err := json.Marshal(v)
	if err != nil {
		log.WithError(err).Error("trigger.http: failed to marshal version")
		resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.WriteHeader(http.StatusOK)
	resp.Write(encoded)
}

func (s *TriggerServer) trigger(event types.Event) error {
	return s.providers.Submit(event)
}
<|endoftext|>