<commit_before>package handlers

import (
	"os"
	"strconv"

	"gopkg.in/alexcesaro/statsd.v2"
	"gopkg.in/gin-contrib/cors.v1"
	"gopkg.in/gin-gonic/gin.v1"

	"github.com/dgrijalva/jwt-go"

	"github.com/ghmeier/bloodlines/gateways"
	coi "github.com/ghmeier/coinage/gateways"
	t "github.com/jakelong95/TownCenter/gateways"
	w "github.com/lcollin/warehouse/gateways"
	cov "github.com/yuderekyu/covenant/gateways"
)

/*BaseHandler contains wrapper methods that all handlers need and should use
  for consistency across services*/
type BaseHandler struct {
	Stats *statsd.Client
}

/*GatewayContext contains references to each type of gateway used for simple
  use in handler construction*/
type GatewayContext struct {
	Sql        gateways.SQL
	Sendgrid   gateways.SendgridI
	TownCenter t.TownCenterI
	Covenant   cov.Covenant
	Warehouse  w.Warehouse
	Bloodlines gateways.Bloodlines
	Coinage    coi.Coinage
	Rabbit     gateways.RabbitI
	Stats      *statsd.Client
	Stripe     coi.Stripe
	S3         gateways.S3
}

/*NewBaseHandler returns a new BaseHandler instance from a given stats*/
func NewBaseHandler(stats *statsd.Client) *BaseHandler {
	return &BaseHandler{Stats: stats}
}

/*GetPaging returns the offset and limit parameters from a gin request context
defaults to offset=0 and limit=20*/
func (b *BaseHandler) GetPaging(ctx *gin.Context) (int, int) {
	offset, _ := strconv.Atoi(ctx.DefaultQuery("offset", "0"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "20"))
	return offset, limit
}

/*UserError sends a 400 response with the given message string and error object*/
func (b *BaseHandler) UserError(ctx *gin.Context, msg string, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("400")
	}
	b.send(ctx, 400, &gin.H{"success": false, "message": msg, "data": obj})
}

/*NotFoundError sends a 404 response and false success when a resource is not present*/
func (b *BaseHandler) NotFoundError(ctx *gin.Context, msg string) {
	if b.Stats != nil {
		b.Stats.Increment("404")
	}
	b.send(ctx, 404, &gin.H{"success": false, "message": msg})
}

/*Unauthorized sends a 401 response along with a message*/
func (b *BaseHandler) Unauthorized(ctx *gin.Context, msg string) {
	if b.Stats != nil {
		b.Stats.Increment("401")
	}
	b.send(ctx, 401, &gin.H{"success": false, "message": msg})
}

/*ServerError sends a 500 response with the given error and object*/
func (b *BaseHandler) ServerError(ctx *gin.Context, err error, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("500")
	}
	b.send(ctx, 500, &gin.H{"success": false, "message": err.Error(), "data": obj})
}

/*Success sends a 200 response with the given object*/
func (b *BaseHandler) Success(ctx *gin.Context, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("200")
	}
	b.send(ctx, 200, &gin.H{"success": true, "data": obj})
}

func (b *BaseHandler) send(ctx *gin.Context, status int, json *gin.H) {
	ctx.JSON(status, json)
}

/*Time sets up gin middleware for sending timing stats*/
func (b *BaseHandler) Time() gin.HandlerFunc {
	return func(c *gin.Context) {
		if b.Stats != nil {
			defer b.Stats.NewTiming().Send(c.Request.Method)
		}
		c.Next()
	}
}

/*GetCors returns a gin handlerFunc for CORS reuquests in expresso services */
func GetCors() gin.HandlerFunc {
	config := cors.DefaultConfig()
	config.AddAllowMethods("DELETE")
	config.AddAllowHeaders("X-Auth")
	config.AddExposeHeaders("X-Auth")
	config.AllowAllOrigins = true
	return cors.New(config)
}

/*GetJWT returns a gin handlerfunc for authenticating JWTs in expresso services*/
func (b *BaseHandler) GetJWT() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if gin.Mode() == gin.TestMode || gin.Mode() == gin.DebugMode {
			ctx.Next()
			return
		}

		tokenHeader := ctx.Request.Header.Get("X-Token")
		if tokenHeader != "" && tokenHeader == os.Getenv("JWT") {
			ctx.Next()
			return
		}

		authHeader := ctx.Request.Header.Get("X-Auth")
		token, err := jwt.ParseWithClaims(authHeader, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(os.Getenv("JWT")), nil
		})

		if err != nil {
			b.Unauthorized(ctx, "Unable to parse token")
			ctx.Abort()
			return
		}

		claims := token.Claims
		if err != nil || !token.Valid || claims.Valid() != nil {
			b.Unauthorized(ctx, "Invalid token")
			ctx.Abort()
			return
		}
	}
}
<commit_msg>Add X-Token auth header<commit_after>package handlers

import (
	"os"
	"strconv"

	"gopkg.in/alexcesaro/statsd.v2"
	"gopkg.in/gin-contrib/cors.v1"
	"gopkg.in/gin-gonic/gin.v1"

	"github.com/dgrijalva/jwt-go"

	"github.com/ghmeier/bloodlines/gateways"
	coi "github.com/ghmeier/coinage/gateways"
	t "github.com/jakelong95/TownCenter/gateways"
	w "github.com/lcollin/warehouse/gateways"
	cov "github.com/yuderekyu/covenant/gateways"
)

/*BaseHandler contains wrapper methods that all handlers need and should use
  for consistency across services*/
type BaseHandler struct {
	Stats *statsd.Client
}

/*GatewayContext contains references to each type of gateway used for simple
  use in handler construction*/
type GatewayContext struct {
	Sql        gateways.SQL
	Sendgrid   gateways.SendgridI
	TownCenter t.TownCenterI
	Covenant   cov.Covenant
	Warehouse  w.Warehouse
	Bloodlines gateways.Bloodlines
	Coinage    coi.Coinage
	Rabbit     gateways.RabbitI
	Stats      *statsd.Client
	Stripe     coi.Stripe
	S3         gateways.S3
}

/*NewBaseHandler returns a new BaseHandler instance from a given stats*/
func NewBaseHandler(stats *statsd.Client) *BaseHandler {
	return &BaseHandler{Stats: stats}
}

/*GetPaging returns the offset and limit parameters from a gin request context
defaults to offset=0 and limit=20*/
func (b *BaseHandler) GetPaging(ctx *gin.Context) (int, int) {
	offset, _ := strconv.Atoi(ctx.DefaultQuery("offset", "0"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "20"))
	return offset, limit
}

/*UserError sends a 400 response with the given message string and error object*/
func (b *BaseHandler) UserError(ctx *gin.Context, msg string, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("400")
	}
	b.send(ctx, 400, &gin.H{"success": false, "message": msg, "data": obj})
}

/*NotFoundError sends a 404 response and false success when a resource is not present*/
func (b *BaseHandler) NotFoundError(ctx *gin.Context, msg string) {
	if b.Stats != nil {
		b.Stats.Increment("404")
	}
	b.send(ctx, 404, &gin.H{"success": false, "message": msg})
}

/*Unauthorized sends a 401 response along with a message*/
func (b *BaseHandler) Unauthorized(ctx *gin.Context, msg string) {
	if b.Stats != nil {
		b.Stats.Increment("401")
	}
	b.send(ctx, 401, &gin.H{"success": false, "message": msg})
}

/*ServerError sends a 500 response with the given error and object*/
func (b *BaseHandler) ServerError(ctx *gin.Context, err error, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("500")
	}
	b.send(ctx, 500, &gin.H{"success": false, "message": err.Error(), "data": obj})
}

/*Success sends a 200 response with the given object*/
func (b *BaseHandler) Success(ctx *gin.Context, obj interface{}) {
	if b.Stats != nil {
		b.Stats.Increment("200")
	}
	b.send(ctx, 200, &gin.H{"success": true, "data": obj})
}

func (b *BaseHandler) send(ctx *gin.Context, status int, json *gin.H) {
	ctx.JSON(status, json)
}

/*Time sets up gin middleware for sending timing stats*/
func (b *BaseHandler) Time() gin.HandlerFunc {
	return func(c *gin.Context) {
		if b.Stats != nil {
			defer b.Stats.NewTiming().Send(c.Request.Method)
		}
		c.Next()
	}
}

/*GetCors returns a gin handlerFunc for CORS reuquests in expresso services */
func GetCors() gin.HandlerFunc {
	config := cors.DefaultConfig()
	config.AddAllowMethods("DELETE")
	config.AddAllowHeaders("X-Auth")
	config.AddExposeHeaders("X-Auth")
	config.AddAllowHeaders("X-Token")
	config.AddExposeHeaders("X-Token")
	config.AllowAllOrigins = true
	return cors.New(config)
}

/*GetJWT returns a gin handlerfunc for authenticating JWTs in expresso services*/
func (b *BaseHandler) GetJWT() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if gin.Mode() == gin.TestMode || gin.Mode() == gin.DebugMode {
			ctx.Next()
			return
		}

		tokenHeader := ctx.Request.Header.Get("X-Token")
		if tokenHeader != "" && tokenHeader == os.Getenv("JWT") {
			ctx.Next()
			return
		}

		authHeader := ctx.Request.Header.Get("X-Auth")
		token, err := jwt.ParseWithClaims(authHeader, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(os.Getenv("JWT")), nil
		})

		if err != nil {
			b.Unauthorized(ctx, "Unable to parse token")
			ctx.Abort()
			return
		}

		claims := token.Claims
		if err != nil || !token.Valid || claims.Valid() != nil {
			b.Unauthorized(ctx, "Invalid token")
			ctx.Abort()
			return
		}
	}
}
<|endoftext|>


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

package bpf_test

import (
	"net"
	"runtime"
	"testing"
	"time"

	"golang.org/x/net/bpf"
	"golang.org/x/net/ipv4"
)

// A virtualMachine is a BPF virtual machine which can process an
// input packet against a BPF program and render a verdict.
type virtualMachine interface {
	Run(in []byte) (int, error)
}

// canUseOSVM indicates if the OS BPF VM is available on this platform.
func canUseOSVM() bool {
	// OS BPF VM can only be used on platforms where x/net/ipv4 supports
	// attaching a BPF program to a socket.
	switch runtime.GOOS {
	case "linux":
		return true
	}

	return false
}

// All BPF tests against both the Go VM and OS VM are assumed to
// be used with a UDP socket. As a result, the entire contents
// of a UDP datagram is sent through the BPF program, but only
// the body after the UDP header will ever be returned in output.

// testVM sets up a Go BPF VM, and if available, a native OS BPF VM
// for integration testing.
func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) {
	goVM, err := bpf.NewVM(filter)
	if err != nil {
		// Some tests expect an error, so this error must be returned
		// instead of fatally exiting the test
		return nil, nil, err
	}

	mvm := &multiVirtualMachine{
		goVM: goVM,

		t: t,
	}

	// If available, add the OS VM for tests which verify that both the Go
	// VM and OS VM have exactly the same output for the same input program
	// and packet.
	done := func() {}
	if canUseOSVM() {
		osVM, osVMDone := testOSVM(t, filter)
		done = func() { osVMDone() }
		mvm.osVM = osVM
	}

	return mvm, done, nil
}

// udpHeaderLen is the length of a UDP header.
const udpHeaderLen = 8

// A multiVirtualMachine is a virtualMachine which can call out to both the Go VM
// and the native OS VM, if the OS VM is available.
type multiVirtualMachine struct {
	goVM virtualMachine
	osVM virtualMachine

	t *testing.T
}

func (mvm *multiVirtualMachine) Run(in []byte) (int, error) {
	if len(in) < udpHeaderLen {
		mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d",
			udpHeaderLen, len(in))
	}

	// All tests have a UDP header as part of input, because the OS VM
	// packets always will. For the Go VM, this output is trimmed before
	// being sent back to tests.
	goOut, goErr := mvm.goVM.Run(in)
	if goOut >= udpHeaderLen {
		goOut -= udpHeaderLen
	}

	// If Go output is larger than the size of the packet, packet filtering
	// interop tests must trim the output bytes to the length of the packet.
	// The BPF VM should not do this on its own, as other uses of it do
	// not trim the output byte count.
	trim := len(in) - udpHeaderLen
	if goOut > trim {
		goOut = trim
	}

	// When the OS VM is not available, process using the Go VM alone
	if mvm.osVM == nil {
		return goOut, goErr
	}

	// The OS VM will apply its own UDP header, so remove the pseudo header
	// that the Go VM needs.
	osOut, err := mvm.osVM.Run(in[udpHeaderLen:])
	if err != nil {
		mvm.t.Fatalf("error while running OS VM: %v", err)
	}

	// Verify both VMs return same number of bytes
	var mismatch bool
	if goOut != osOut {
		mismatch = true
		mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut)
	}

	if mismatch {
		mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match")
	}

	return goOut, goErr
}

// An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for
// processing BPF programs.
type osVirtualMachine struct {
	l net.PacketConn
	s net.Conn
}

// testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting
// packets into a UDP listener with a BPF program attached to it.
func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
	l, err := net.ListenPacket("udp4", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to open OS VM UDP listener: %v", err)
	}

	prog, err := bpf.Assemble(filter)
	if err != nil {
		t.Fatalf("failed to compile BPF program: %v", err)
	}

	p := ipv4.NewPacketConn(l)
	if err = p.SetBPF(prog); err != nil {
		t.Fatalf("failed to attach BPF program to listener: %v", err)
	}

	s, err := net.Dial("udp4", l.LocalAddr().String())
	if err != nil {
		t.Fatalf("failed to dial connection to listener: %v", err)
	}

	done := func() {
		_ = s.Close()
		_ = l.Close()
	}

	return &osVirtualMachine{
		l: l,
		s: s,
	}, done
}

// Run sends the input bytes into the OS's BPF VM and returns its verdict.
func (vm *osVirtualMachine) Run(in []byte) (int, error) {
	go func() {
		_, _ = vm.s.Write(in)
	}()

	vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond))

	var b [512]byte
	n, _, err := vm.l.ReadFrom(b[:])
	if err != nil {
		// A timeout indicates that BPF filtered out the packet, and thus,
		// no input should be returned.
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			return n, nil
		}

		return n, err
	}

	return n, nil
}
<commit_msg>bpf: use of nettest<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bpf_test

import (
	"net"
	"runtime"
	"testing"
	"time"

	"golang.org/x/net/bpf"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"golang.org/x/net/nettest"
)

// A virtualMachine is a BPF virtual machine which can process an
// input packet against a BPF program and render a verdict.
type virtualMachine interface {
	Run(in []byte) (int, error)
}

// canUseOSVM indicates if the OS BPF VM is available on this platform.
func canUseOSVM() bool {
	// OS BPF VM can only be used on platforms where x/net/ipv4 supports
	// attaching a BPF program to a socket.
	switch runtime.GOOS {
	case "linux":
		return true
	}

	return false
}

// All BPF tests against both the Go VM and OS VM are assumed to
// be used with a UDP socket. As a result, the entire contents
// of a UDP datagram is sent through the BPF program, but only
// the body after the UDP header will ever be returned in output.

// testVM sets up a Go BPF VM, and if available, a native OS BPF VM
// for integration testing.
func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) {
	goVM, err := bpf.NewVM(filter)
	if err != nil {
		// Some tests expect an error, so this error must be returned
		// instead of fatally exiting the test
		return nil, nil, err
	}

	mvm := &multiVirtualMachine{
		goVM: goVM,

		t: t,
	}

	// If available, add the OS VM for tests which verify that both the Go
	// VM and OS VM have exactly the same output for the same input program
	// and packet.
	done := func() {}
	if canUseOSVM() {
		osVM, osVMDone := testOSVM(t, filter)
		done = func() { osVMDone() }
		mvm.osVM = osVM
	}

	return mvm, done, nil
}

// udpHeaderLen is the length of a UDP header.
const udpHeaderLen = 8

// A multiVirtualMachine is a virtualMachine which can call out to both the Go VM
// and the native OS VM, if the OS VM is available.
type multiVirtualMachine struct {
	goVM virtualMachine
	osVM virtualMachine

	t *testing.T
}

func (mvm *multiVirtualMachine) Run(in []byte) (int, error) {
	if len(in) < udpHeaderLen {
		mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d",
			udpHeaderLen, len(in))
	}

	// All tests have a UDP header as part of input, because the OS VM
	// packets always will. For the Go VM, this output is trimmed before
	// being sent back to tests.
	goOut, goErr := mvm.goVM.Run(in)
	if goOut >= udpHeaderLen {
		goOut -= udpHeaderLen
	}

	// If Go output is larger than the size of the packet, packet filtering
	// interop tests must trim the output bytes to the length of the packet.
	// The BPF VM should not do this on its own, as other uses of it do
	// not trim the output byte count.
	trim := len(in) - udpHeaderLen
	if goOut > trim {
		goOut = trim
	}

	// When the OS VM is not available, process using the Go VM alone
	if mvm.osVM == nil {
		return goOut, goErr
	}

	// The OS VM will apply its own UDP header, so remove the pseudo header
	// that the Go VM needs.
	osOut, err := mvm.osVM.Run(in[udpHeaderLen:])
	if err != nil {
		mvm.t.Fatalf("error while running OS VM: %v", err)
	}

	// Verify both VMs return same number of bytes
	var mismatch bool
	if goOut != osOut {
		mismatch = true
		mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut)
	}

	if mismatch {
		mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match")
	}

	return goOut, goErr
}

// An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for
// processing BPF programs.
type osVirtualMachine struct {
	l net.PacketConn
	s net.Conn
}

// testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting
// packets into a UDP listener with a BPF program attached to it.
func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
	l, err := nettest.NewLocalPacketListener("udp")
	if err != nil {
		t.Fatalf("failed to open OS VM UDP listener: %v", err)
	}

	prog, err := bpf.Assemble(filter)
	if err != nil {
		t.Fatalf("failed to compile BPF program: %v", err)
	}

	ip := l.LocalAddr().(*net.UDPAddr).IP
	if ip.To4() != nil && ip.To16() == nil {
		err = ipv4.NewPacketConn(l).SetBPF(prog)
	} else {
		err = ipv6.NewPacketConn(l).SetBPF(prog)
	}
	if err != nil {
		t.Fatalf("failed to attach BPF program to listener: %v", err)
	}

	s, err := net.Dial(l.LocalAddr().Network(), l.LocalAddr().String())
	if err != nil {
		t.Fatalf("failed to dial connection to listener: %v", err)
	}

	done := func() {
		_ = s.Close()
		_ = l.Close()
	}

	return &osVirtualMachine{
		l: l,
		s: s,
	}, done
}

// Run sends the input bytes into the OS's BPF VM and returns its verdict.
func (vm *osVirtualMachine) Run(in []byte) (int, error) {
	go func() {
		_, _ = vm.s.Write(in)
	}()

	vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond))

	var b [512]byte
	n, _, err := vm.l.ReadFrom(b[:])
	if err != nil {
		// A timeout indicates that BPF filtered out the packet, and thus,
		// no input should be returned.
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			return n, nil
		}

		return n, err
	}

	return n, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative Authors

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

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

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

package v1beta2

import (
	"context"
	"errors"
	"testing"

	"knative.dev/pkg/apis"
)

// implement apis.Convertible
type dummyObject struct{}

func (*dummyObject) ConvertTo(ctx context.Context, obj apis.Convertible) error {
	return errors.New("Won't go")
}

func (*dummyObject) ConvertFrom(ctx context.Context, obj apis.Convertible) error {
	return errors.New("Won't go")
}

func TestPingSourceConversionBadType(t *testing.T) {
	good, bad := &PingSource{}, &dummyObject{}

	if err := good.ConvertTo(context.Background(), bad); err == nil {
		t.Errorf("ConvertTo() = %#v, wanted error", bad)
	}

	if err := good.ConvertFrom(context.Background(), bad); err == nil {
		t.Errorf("ConvertFrom() = %#v, wanted error", good)
	}
}
<commit_msg>Remove some more woke warnings (#4553)<commit_after>/*
Copyright 2020 The Knative Authors

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

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

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

package v1beta2

import (
	"context"
	"errors"
	"testing"

	"knative.dev/pkg/apis"
)

// implement apis.Convertible
type testObject struct{}

func (*testObject) ConvertTo(ctx context.Context, obj apis.Convertible) error {
	return errors.New("Won't go")
}

func (*testObject) ConvertFrom(ctx context.Context, obj apis.Convertible) error {
	return errors.New("Won't go")
}

func TestPingSourceConversionBadType(t *testing.T) {
	good, bad := &PingSource{}, &testObject{}

	if err := good.ConvertTo(context.Background(), bad); err == nil {
		t.Errorf("ConvertTo() = %#v, wanted error", bad)
	}

	if err := good.ConvertFrom(context.Background(), bad); err == nil {
		t.Errorf("ConvertFrom() = %#v, wanted error", good)
	}
}
<|endoftext|>


<commit_before>package conditions

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/tsdb"
)

func TestSimpleReducer(t *testing.T) {
	Convey("Test simple reducer by calculating", t, func() {

		Convey("sum", func() {
			result := testReducer("sum", 1, 2, 3)
			So(result, ShouldEqual, float64(6))
		})

		Convey("min", func() {
			result := testReducer("min", 3, 2, 1)
			So(result, ShouldEqual, float64(1))
		})

		Convey("max", func() {
			result := testReducer("max", 1, 2, 3)
			So(result, ShouldEqual, float64(3))
		})

		Convey("count", func() {
			result := testReducer("count", 1, 2, 3000)
			So(result, ShouldEqual, float64(3))
		})

		Convey("last", func() {
			result := testReducer("last", 1, 2, 3000)
			So(result, ShouldEqual, float64(3000))
		})

		Convey("median odd amount of numbers", func() {
			result := testReducer("median", 1, 2, 3000)
			So(result, ShouldEqual, float64(2))
		})

		Convey("median even amount of numbers", func() {
			result := testReducer("median", 1, 2, 4, 3000)
			So(result, ShouldEqual, float64(3))
		})

		Convey("median with one values", func() {
			result := testReducer("median", 1)
			So(result, ShouldEqual, float64(1))
		})

		Convey("avg", func() {
			result := testReducer("avg", 1, 2, 3)
			So(result, ShouldEqual, float64(2))
		})

		Convey("avg with only nulls", func() {
			reducer := NewSimpleReducer("avg")
			series := &tsdb.TimeSeries{
				Name: "test time serie",
			}

			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
			So(reducer.Reduce(series).Valid, ShouldEqual, false)
		})

		Convey("count_non_null", func() {
			Convey("with null values and real values", func() {
				reducer := NewSimpleReducer("count_non_null")
				series := &tsdb.TimeSeries{
					Name: "test time serie",
				}

				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 3))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))

				So(reducer.Reduce(series).Valid, ShouldEqual, true)
				So(reducer.Reduce(series).Float64, ShouldEqual, 2)
			})

			Convey("with null values", func() {
				reducer := NewSimpleReducer("count_non_null")
				series := &tsdb.TimeSeries{
					Name: "test time serie",
				}

				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))

				So(reducer.Reduce(series).Valid, ShouldEqual, false)
			})
		})

		Convey("avg of number values and null values should ignore nulls", func() {
			reducer := NewSimpleReducer("avg")
			series := &tsdb.TimeSeries{
				Name: "test time serie",
			}

			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 1))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 3))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))

			So(reducer.Reduce(series).Float64, ShouldEqual, float64(3))
		})

		Convey("diff one point", func() {
			result := testReducer("diff", 30)
			So(result, ShouldEqual, float64(0))
		})

		Convey("diff two points", func() {
			result := testReducer("diff", 30, 40)
			So(result, ShouldEqual, float64(10))
		})

		Convey("diff three points", func() {
			result := testReducer("diff", 30, 40, 40)
			So(result, ShouldEqual, float64(10))
		})

		Convey("percent_diff one point", func() {
			result := testReducer("percent_diff", 40)
			So(result, ShouldEqual, float64(0))
		})

		Convey("percent_diff two points", func() {
			result := testReducer("percent_diff", 30, 40)
			So(result, ShouldEqual, float64(33.33333333333333))
		})

		Convey("percent_diff three points", func() {
			result := testReducer("percent_diff", 30, 40, 40)
			So(result, ShouldEqual, float64(33.33333333333333))
		})
	})
}

func testReducer(typ string, datapoints ...float64) float64 {
	reducer := NewSimpleReducer(typ)
	series := &tsdb.TimeSeries{
		Name: "test time serie",
	}

	for idx := range datapoints {
		series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(datapoints[idx]), 1234134))
	}

	return reducer.Reduce(series).Float64
}
<commit_msg>alerting: adds tests for the median reducer<commit_after>package conditions

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/tsdb"
)

func TestSimpleReducer(t *testing.T) {
	Convey("Test simple reducer by calculating", t, func() {

		Convey("sum", func() {
			result := testReducer("sum", 1, 2, 3)
			So(result, ShouldEqual, float64(6))
		})

		Convey("min", func() {
			result := testReducer("min", 3, 2, 1)
			So(result, ShouldEqual, float64(1))
		})

		Convey("max", func() {
			result := testReducer("max", 1, 2, 3)
			So(result, ShouldEqual, float64(3))
		})

		Convey("count", func() {
			result := testReducer("count", 1, 2, 3000)
			So(result, ShouldEqual, float64(3))
		})

		Convey("last", func() {
			result := testReducer("last", 1, 2, 3000)
			So(result, ShouldEqual, float64(3000))
		})

		Convey("median odd amount of numbers", func() {
			result := testReducer("median", 1, 2, 3000)
			So(result, ShouldEqual, float64(2))
		})

		Convey("median even amount of numbers", func() {
			result := testReducer("median", 1, 2, 4, 3000)
			So(result, ShouldEqual, float64(3))
		})

		Convey("median with one values", func() {
			result := testReducer("median", 1)
			So(result, ShouldEqual, float64(1))
		})

		Convey("median should ignore null values", func() {
			reducer := NewSimpleReducer("median")
			series := &tsdb.TimeSeries{
				Name: "test time serie",
			}

			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 3))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(float64(1)), 4))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(float64(2)), 5))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(float64(3)), 6))

			result := reducer.Reduce(series)
			So(result.Valid, ShouldEqual, true)
			So(result.Float64, ShouldEqual, float64(2))
		})

		Convey("avg", func() {
			result := testReducer("avg", 1, 2, 3)
			So(result, ShouldEqual, float64(2))
		})

		Convey("avg with only nulls", func() {
			reducer := NewSimpleReducer("avg")
			series := &tsdb.TimeSeries{
				Name: "test time serie",
			}

			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
			So(reducer.Reduce(series).Valid, ShouldEqual, false)
		})

		Convey("count_non_null", func() {
			Convey("with null values and real values", func() {
				reducer := NewSimpleReducer("count_non_null")
				series := &tsdb.TimeSeries{
					Name: "test time serie",
				}

				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 3))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))

				So(reducer.Reduce(series).Valid, ShouldEqual, true)
				So(reducer.Reduce(series).Float64, ShouldEqual, 2)
			})

			Convey("with null values", func() {
				reducer := NewSimpleReducer("count_non_null")
				series := &tsdb.TimeSeries{
					Name: "test time serie",
				}

				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))

				So(reducer.Reduce(series).Valid, ShouldEqual, false)
			})
		})

		Convey("avg of number values and null values should ignore nulls", func() {
			reducer := NewSimpleReducer("avg")
			series := &tsdb.TimeSeries{
				Name: "test time serie",
			}

			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 1))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 3))
			series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))

			So(reducer.Reduce(series).Float64, ShouldEqual, float64(3))
		})

		Convey("diff one point", func() {
			result := testReducer("diff", 30)
			So(result, ShouldEqual, float64(0))
		})

		Convey("diff two points", func() {
			result := testReducer("diff", 30, 40)
			So(result, ShouldEqual, float64(10))
		})

		Convey("diff three points", func() {
			result := testReducer("diff", 30, 40, 40)
			So(result, ShouldEqual, float64(10))
		})

		Convey("percent_diff one point", func() {
			result := testReducer("percent_diff", 40)
			So(result, ShouldEqual, float64(0))
		})

		Convey("percent_diff two points", func() {
			result := testReducer("percent_diff", 30, 40)
			So(result, ShouldEqual, float64(33.33333333333333))
		})

		Convey("percent_diff three points", func() {
			result := testReducer("percent_diff", 30, 40, 40)
			So(result, ShouldEqual, float64(33.33333333333333))
		})
	})
}

func testReducer(typ string, datapoints ...float64) float64 {
	reducer := NewSimpleReducer(typ)
	series := &tsdb.TimeSeries{
		Name: "test time serie",
	}

	for idx := range datapoints {
		series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(datapoints[idx]), 1234134))
	}

	return reducer.Reduce(series).Float64
}
<|endoftext|>


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

// Package ipv6 implements IP-level socket options for the Internet
// Protocol version 6.
//
// The package provides IP-level socket options that allow
// manipulation of IPv6 facilities.
//
// The IPv6 protocol is defined in RFC 2460.
// Basic and advanced socket interface extensions are defined in RFC
// 3493 and RFC 3542.
// Socket interface extensions for multicast source filters are
// defined in RFC 3678.
// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
// Source-specific multicast is defined in RFC 4607.
//
// On Darwin, this package requires OS X Mavericks version 10.9 or
// above, or equivalent.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv6 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv6.Conn is used to set the traffic class field on the
// IPv6 header for each packet.
//
//	ln, err := net.Listen("tcp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv6.NewConn(c).SetTrafficClass(0x28); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv6 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.ParseIP("ff02::114")
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv6 and Ethernet.
//
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv6.PacketConn is used to enable control
// message transmissions.
//
//	if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, rcm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if rcm.Dst.IsMulticast() {
//			if rcm.Dst.Equal(group) {
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTrafficClass(0x0)
//		p.SetHopLimit(16)
//		if _, err := p.WriteTo(data[:n], nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		wcm := ipv6.ControlMessage{TrafficClass: 0xe0, HopLimit: 1}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			wcm.IfIndex = ifi.Index
//			if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn may join multiple multicast
// groups.  For example, a UDP listener with port 1024 might join two
// different groups across over two different network interfaces by
// using:
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv6.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	p2 := ipv6.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn on MLDv2 supported platform is
// able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
//	ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on MLDv2 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// MLDv1 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv6 // import "golang.org/x/net/ipv6"
<commit_msg>ipv6: simplify references to RFCs in package documentation<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv6 implements IP-level socket options for the Internet
// Protocol version 6.
//
// The package provides IP-level socket options that allow
// manipulation of IPv6 facilities.
//
// The IPv6 protocol is defined in RFC 2460.
// Socket interface extensions are defined in RFC 3493, RFC 3542 and
// RFC 3678.
// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
// Source-specific multicast is defined in RFC 4607.
//
// On Darwin, this package requires OS X Mavericks version 10.9 or
// above, or equivalent.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv6 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv6.Conn is used to set the traffic class field on the
// IPv6 header for each packet.
//
//	ln, err := net.Listen("tcp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv6.NewConn(c).SetTrafficClass(0x28); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv6 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.ParseIP("ff02::114")
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv6 and Ethernet.
//
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv6.PacketConn is used to enable control
// message transmissions.
//
//	if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, rcm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if rcm.Dst.IsMulticast() {
//			if rcm.Dst.Equal(group) {
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTrafficClass(0x0)
//		p.SetHopLimit(16)
//		if _, err := p.WriteTo(data[:n], nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		wcm := ipv6.ControlMessage{TrafficClass: 0xe0, HopLimit: 1}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			wcm.IfIndex = ifi.Index
//			if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn may join multiple multicast
// groups.  For example, a UDP listener with port 1024 might join two
// different groups across over two different network interfaces by
// using:
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv6.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	p2 := ipv6.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn on MLDv2 supported platform is
// able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
//	ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on MLDv2 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// MLDv1 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv6 // import "golang.org/x/net/ipv6"
<|endoftext|>


<commit_before>package filestore

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"

	"github.com/ketchuphq/ketchup/plugins/pkg"
	"github.com/ketchuphq/ketchup/proto/ketchup/models"
	"github.com/ketchuphq/ketchup/proto/ketchup/packages"
	"github.com/ketchuphq/ketchup/server/content/templates/store"
	"github.com/ketchuphq/ketchup/util/errors"
)

const (
	configFileName       = "theme.json"
	fileStoreTemplateDir = "templates"
	fileStoreAssetsDir   = "assets"
)

var jpb = &jsonpb.Marshaler{
	EnumsAsInts:  false,
	EmitDefaults: false,
	Indent:       "  ",
	OrigName:     false,
}

// FileStore stores and loads templates on the filesystem
type FileStore struct {
	baseDir     string
	themeDirMap map[string]string // maps theme name to dir
}

// New returns a new file store which updates periodically
func New(baseDir string, updateInterval time.Duration, log func(args ...interface{})) (*FileStore, error) {
	f := &FileStore{baseDir: baseDir}
	err := os.MkdirAll(baseDir, 0700)
	if err != nil {
		return nil, err
	}
	err = f.updateThemeDirMap()
	if err != nil {
		return nil, err
	}
	go func() {
		for range time.Tick(updateInterval) {
			err := f.updateThemeDirMap()
			if err != nil {
				log(err)
			}
		}
	}()
	return f, nil
}

// updateThemeDirMap iterates over all folders in the base dir and reads all the
// theme configs found. also updates the mapping of folder name to theme name,
// if different.
func (f *FileStore) updateThemeDirMap() error {
	lst, err := ioutil.ReadDir(f.baseDir)
	if err != nil {
		return errors.Wrap(err)
	}
	m := map[string]string{}
	for _, fi := range lst {
		if !fi.IsDir() {
			continue
		}
		themeConfigPath := path.Join(f.baseDir, fi.Name(), configFileName)
		c, err := readConfig(themeConfigPath)
		if err != nil {
			return nil
		}
		if c.GetName() != "" && fi.Name() != c.GetName() {
			m[c.GetName()] = fi.Name()
		}
	}
	f.themeDirMap = m
	return nil
}

// GetTemplate fetches a theme's template from the filesystem. The
// template's Engine is inferred from the extension in templateName
func (f *FileStore) getTemplate(theme *models.Theme, templateName string) (*models.ThemeTemplate, error) {
	if theme == nil || theme.GetName() == "" {
		return nil, nil
	}
	themeDir := theme.GetName()
	if altDir := f.themeDirMap[theme.GetName()]; altDir != "" {
		themeDir = altDir
	}
	p := path.Join(f.baseDir, themeDir, fileStoreTemplateDir, templateName)
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	data := string(b)
	ext := strings.TrimLeft(path.Ext(templateName), ".")
	t := proto.Clone(theme.GetTemplates()[templateName]).(*models.ThemeTemplate)
	t.Theme = theme.Name
	t.Data = &data
	t.Name = &templateName
	t.Engine = &ext
	return t, nil
}

// GetAsset fetches an asset from the filesystem
func (f *FileStore) getAsset(theme *models.Theme, assetName string) (*models.ThemeAsset, error) {
	if theme == nil || theme.GetName() == "" {
		return nil, nil
	}
	themeDir := theme.GetName()
	if altDir := f.themeDirMap[theme.GetName()]; altDir != "" {
		themeDir = altDir
	}

	p := path.Join(f.baseDir, themeDir, fileStoreAssetsDir, assetName)
	b, err := ioutil.ReadFile(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	data := string(b)
	t := &models.ThemeAsset{
		Theme: theme.Name,
		Data:  &data,
		Name:  &assetName,
	}
	return t, nil
}

// Get a theme from the file store. Template and asset data (i.e. the file
// contents) are NOT loaded.
func (f *FileStore) Get(themeName string) (store.Theme, error) {
	themeDir := themeName
	if altDir := f.themeDirMap[themeName]; altDir != "" {
		themeDir = altDir
	}

	themeConfigPath := path.Join(f.baseDir, themeDir, configFileName)
	t, err := readConfig(themeConfigPath)
	if err != nil || t == nil {
		return nil, nil
	}

	// get templates (todo: supported subdirs)
	baseTemplateDir := path.Clean(path.Join(f.baseDir, themeDir, fileStoreTemplateDir))
	err = filepath.Walk(baseTemplateDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		p = strings.TrimPrefix(path.Clean(p), baseTemplateDir)
		p = strings.TrimLeft(p, "/")
		q := path.Base(p)
		e := strings.TrimLeft(path.Ext(p), ".")
		if t.Templates[p] == nil {
			t.Templates[p] = &models.ThemeTemplate{}
		}
		t.Templates[p].Name = &q
		t.Templates[p].Engine = &e
		return nil
	})

	baseAssetDir := path.Clean(path.Join(f.baseDir, themeDir, fileStoreAssetsDir))
	err = filepath.Walk(baseAssetDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return errors.Wrap(err)
		}
		if info.IsDir() {
			return nil
		}
		p = strings.TrimPrefix(path.Clean(p), baseAssetDir)
		p = strings.TrimLeft(p, "/")
		q := path.Base(p)
		if strings.HasPrefix(q, ".") {
			return nil
		}
		t.Assets[p] = &models.ThemeAsset{Name: &q}
		return nil
	})

	if err != nil {
		return nil, err
	}

	latestRef, err := getLatestRef(path.Join(f.baseDir, themeDir))
	if err != nil {
		return nil, errors.Wrap(err)
	}

	return &Theme{Theme: t, store: f, ref: latestRef}, nil
}

// List all themes in the store
func (f *FileStore) List() ([]*models.Theme, error) {
	glob := path.Join(f.baseDir, "*", configFileName)
	paths, err := filepath.Glob(glob)
	if err != nil {
		return nil, err
	}
	themes := []*models.Theme{}
	for _, p := range paths {
		b, err := ioutil.ReadFile(p)
		if err != nil {
			return nil, err
		}
		theme := &models.Theme{}
		err = jsonpb.Unmarshal(bytes.NewBuffer(b), theme)
		if err != nil {
			return nil, err
		}
		if theme.GetName() == "" {
			dir := themeNameFromPath(p)
			theme.Name = &dir
		}
		themes = append(themes, theme)
	}
	return themes, nil
}

// AddPackage adds a theme from a theme file by cloning it from
// the VCS location to the themeDir.
func (f *FileStore) AddPackage(p *packages.Package) error {
	themeDir := path.Join(f.baseDir, p.GetName())
	return pkg.CloneToDir(themeDir, p.GetVcsUrl())
}

// Add a theme directly to the themeDir.
func (f *FileStore) Add(theme *models.Theme) error {
	theme = proto.Clone(theme).(*models.Theme)
	templateDir := path.Join(f.baseDir, theme.GetName())
	perm := os.FileMode(0600)

	err := themeIterator(theme, func(fn string, el themeFile) error {
		p := path.Clean(path.Join(templateDir, fn))
		if strings.HasPrefix(p, "..") {
			return nil
		}

		err := os.MkdirAll(path.Dir(p), os.ModePerm)
		if err != nil {
			return errors.Wrap(err)
		}

		err = ioutil.WriteFile(p, []byte(el.GetData()), perm)
		if err != nil {
			return errors.Wrap(err)
		}
		el.SetData(nil)
		return nil
	})

	if err != nil {
		return err
	}

	fw, err := os.Create(path.Join(templateDir, configFileName))
	if err != nil {
		return err
	}
	return jpb.Marshal(fw, theme)
}

func (f *FileStore) UpdateThemeToRef(themeName, commitHash string) error {
	themeDir := themeName
	if altDir := f.themeDirMap[themeName]; altDir != "" {
		themeDir = altDir
	}
	repoDir := path.Join(f.baseDir, themeDir)
	return pkg.FetchDir(repoDir, commitHash)
}

func (f *FileStore) GetAsset(assetName string) (*models.ThemeAsset, error) {
	lst, err := ioutil.ReadDir(f.baseDir)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	for _, fi := range lst {
		if !fi.IsDir() {
			continue
		}
		p := path.Join(f.baseDir, fi.Name(), fileStoreAssetsDir, assetName)
		b, err := ioutil.ReadFile(p)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, errors.Wrap(err)
		}
		data := string(b)
		t := &models.ThemeAsset{
			// Theme: theme.Name,
			Data: &data,
			Name: &assetName,
		}
		return t, nil
	}

	return nil, nil
}
<commit_msg>filestore: Fix name for template files in subfolders.<commit_after>package filestore

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"

	"github.com/ketchuphq/ketchup/plugins/pkg"
	"github.com/ketchuphq/ketchup/proto/ketchup/models"
	"github.com/ketchuphq/ketchup/proto/ketchup/packages"
	"github.com/ketchuphq/ketchup/server/content/templates/store"
	"github.com/ketchuphq/ketchup/util/errors"
)

const (
	configFileName       = "theme.json"
	fileStoreTemplateDir = "templates"
	fileStoreAssetsDir   = "assets"
)

var jpb = &jsonpb.Marshaler{
	EnumsAsInts:  false,
	EmitDefaults: false,
	Indent:       "  ",
	OrigName:     false,
}

// FileStore stores and loads templates on the filesystem
type FileStore struct {
	baseDir     string
	themeDirMap map[string]string // maps theme name to dir
}

// New returns a new file store which updates periodically
func New(baseDir string, updateInterval time.Duration, log func(args ...interface{})) (*FileStore, error) {
	f := &FileStore{baseDir: baseDir}
	err := os.MkdirAll(baseDir, 0700)
	if err != nil {
		return nil, err
	}
	err = f.updateThemeDirMap()
	if err != nil {
		return nil, err
	}
	go func() {
		for range time.Tick(updateInterval) {
			err := f.updateThemeDirMap()
			if err != nil {
				log(err)
			}
		}
	}()
	return f, nil
}

// updateThemeDirMap iterates over all folders in the base dir and reads all the
// theme configs found. also updates the mapping of folder name to theme name,
// if different.
func (f *FileStore) updateThemeDirMap() error {
	lst, err := ioutil.ReadDir(f.baseDir)
	if err != nil {
		return errors.Wrap(err)
	}
	m := map[string]string{}
	for _, fi := range lst {
		if !fi.IsDir() {
			continue
		}
		themeConfigPath := path.Join(f.baseDir, fi.Name(), configFileName)
		c, err := readConfig(themeConfigPath)
		if err != nil {
			return nil
		}
		if c.GetName() != "" && fi.Name() != c.GetName() {
			m[c.GetName()] = fi.Name()
		}
	}
	f.themeDirMap = m
	return nil
}

// GetTemplate fetches a theme's template from the filesystem. The
// template's Engine is inferred from the extension in templateName
func (f *FileStore) getTemplate(theme *models.Theme, templateName string) (*models.ThemeTemplate, error) {
	if theme == nil || theme.GetName() == "" {
		return nil, nil
	}
	themeDir := theme.GetName()
	if altDir := f.themeDirMap[theme.GetName()]; altDir != "" {
		themeDir = altDir
	}
	p := path.Join(f.baseDir, themeDir, fileStoreTemplateDir, templateName)
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	data := string(b)
	ext := strings.TrimLeft(path.Ext(templateName), ".")
	t := proto.Clone(theme.GetTemplates()[templateName]).(*models.ThemeTemplate)
	t.Theme = theme.Name
	t.Data = &data
	t.Name = &templateName
	t.Engine = &ext
	return t, nil
}

// GetAsset fetches an asset from the filesystem
func (f *FileStore) getAsset(theme *models.Theme, assetName string) (*models.ThemeAsset, error) {
	if theme == nil || theme.GetName() == "" {
		return nil, nil
	}
	themeDir := theme.GetName()
	if altDir := f.themeDirMap[theme.GetName()]; altDir != "" {
		themeDir = altDir
	}

	p := path.Join(f.baseDir, themeDir, fileStoreAssetsDir, assetName)
	b, err := ioutil.ReadFile(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	data := string(b)
	t := &models.ThemeAsset{
		Theme: theme.Name,
		Data:  &data,
		Name:  &assetName,
	}
	return t, nil
}

// Get a theme from the file store. Template and asset data (i.e. the file
// contents) are NOT loaded.
func (f *FileStore) Get(themeName string) (store.Theme, error) {
	themeDir := themeName
	if altDir := f.themeDirMap[themeName]; altDir != "" {
		themeDir = altDir
	}

	themeConfigPath := path.Join(f.baseDir, themeDir, configFileName)
	t, err := readConfig(themeConfigPath)
	if err != nil || t == nil {
		return nil, nil
	}

	// get templates (todo: supported subdirs)
	baseTemplateDir := path.Clean(path.Join(f.baseDir, themeDir, fileStoreTemplateDir))
	err = filepath.Walk(baseTemplateDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		p = strings.TrimPrefix(path.Clean(p), baseTemplateDir)
		p = strings.TrimLeft(p, "/")
		if strings.HasPrefix(path.Base(p), ".") {
			return nil
		}
		e := strings.TrimLeft(path.Ext(p), ".")
		if t.Templates[p] == nil {
			t.Templates[p] = &models.ThemeTemplate{}
		}
		t.Templates[p].Name = &p
		t.Templates[p].Engine = &e
		return nil
	})
	if err != nil {
		return nil, err
	}

	baseAssetDir := path.Clean(path.Join(f.baseDir, themeDir, fileStoreAssetsDir))
	err = filepath.Walk(baseAssetDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return errors.Wrap(err)
		}
		if info.IsDir() {
			return nil
		}
		p = strings.TrimPrefix(path.Clean(p), baseAssetDir)
		p = strings.TrimLeft(p, "/")
		if strings.HasPrefix(path.Base(p), ".") {
			return nil
		}
		t.Assets[p] = &models.ThemeAsset{Name: &p}
		return nil
	})

	if err != nil {
		return nil, err
	}

	latestRef, err := getLatestRef(path.Join(f.baseDir, themeDir))
	if err != nil {
		return nil, errors.Wrap(err)
	}

	return &Theme{Theme: t, store: f, ref: latestRef}, nil
}

// List all themes in the store
func (f *FileStore) List() ([]*models.Theme, error) {
	glob := path.Join(f.baseDir, "*", configFileName)
	paths, err := filepath.Glob(glob)
	if err != nil {
		return nil, err
	}
	themes := []*models.Theme{}
	for _, p := range paths {
		b, err := ioutil.ReadFile(p)
		if err != nil {
			return nil, err
		}
		theme := &models.Theme{}
		err = jsonpb.Unmarshal(bytes.NewBuffer(b), theme)
		if err != nil {
			return nil, err
		}
		if theme.GetName() == "" {
			dir := themeNameFromPath(p)
			theme.Name = &dir
		}
		themes = append(themes, theme)
	}
	return themes, nil
}

// AddPackage adds a theme from a theme file by cloning it from
// the VCS location to the themeDir.
func (f *FileStore) AddPackage(p *packages.Package) error {
	themeDir := path.Join(f.baseDir, p.GetName())
	return pkg.CloneToDir(themeDir, p.GetVcsUrl())
}

// Add a theme directly to the themeDir.
func (f *FileStore) Add(theme *models.Theme) error {
	theme = proto.Clone(theme).(*models.Theme)
	templateDir := path.Join(f.baseDir, theme.GetName())
	perm := os.FileMode(0600)

	err := themeIterator(theme, func(fn string, el themeFile) error {
		p := path.Clean(path.Join(templateDir, fn))
		if strings.HasPrefix(p, "..") {
			return nil
		}

		err := os.MkdirAll(path.Dir(p), os.ModePerm)
		if err != nil {
			return errors.Wrap(err)
		}

		err = ioutil.WriteFile(p, []byte(el.GetData()), perm)
		if err != nil {
			return errors.Wrap(err)
		}
		el.SetData(nil)
		return nil
	})

	if err != nil {
		return err
	}

	fw, err := os.Create(path.Join(templateDir, configFileName))
	if err != nil {
		return err
	}
	return jpb.Marshal(fw, theme)
}

func (f *FileStore) UpdateThemeToRef(themeName, commitHash string) error {
	themeDir := themeName
	if altDir := f.themeDirMap[themeName]; altDir != "" {
		themeDir = altDir
	}
	repoDir := path.Join(f.baseDir, themeDir)
	return pkg.FetchDir(repoDir, commitHash)
}

func (f *FileStore) GetAsset(assetName string) (*models.ThemeAsset, error) {
	lst, err := ioutil.ReadDir(f.baseDir)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	for _, fi := range lst {
		if !fi.IsDir() {
			continue
		}
		p := path.Join(f.baseDir, fi.Name(), fileStoreAssetsDir, assetName)
		b, err := ioutil.ReadFile(p)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, errors.Wrap(err)
		}
		data := string(b)
		t := &models.ThemeAsset{
			// Theme: theme.Name,
			Data: &data,
			Name: &assetName,
		}
		return t, nil
	}

	return nil, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Dean ChaoJun Pan.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
//
// An iterator yields a sequence of key/value pairs from a source.
// The following class defines the interface.  Multiple implementations
// are provided by this library.  In particular, iterators are provided
// to access the contents of a Table or a DB.
//
// Multiple threads can invoke const methods on an Iterator without
// external synchronization, but if any of the threads may call a
// non-const method, all threads accessing the same Iterator must use
// external synchronization.

package rocksdb

/*
#include "iterator.h"
*/
import "C"

import (
	"runtime"
	"unsafe"
	"sync"
)

// Go Iterator
type Iterator struct {
	it C.Iterator_t
	// Thread safe
	mutex sync.Mutex
	db *DB // make sure the iterator is deleted before the db
	// true if the underlying c object is deleted
	closed bool
}

// Release resources
func (it *Iterator) finalize() {
	if !it.closed {
		it.closed = true
		var cit *C.Iterator_t = &it.it
		C.DeleteIteratorT(cit, toCBool(false))
	}
}

// Close the Iterator
func (it *Iterator) Close() {
	runtime.SetFinalizer(it, nil)
	it.finalize()
}

// Iterator of C to go iterator
func (cit *C.Iterator_t) toIterator(db *DB) (it *Iterator) {
	it = &Iterator{it: *cit, db: db}
	runtime.SetFinalizer(it, finalize)
	return
}

// Array of C iterators to array of go iterators
func newIteratorArrayFromCArray(cit *C.Iterator_t, sz uint, db *DB) (its []*Iterator) {
	defer C.DeleteIteratorTArray(cit)
	its = make([]*Iterator, sz)
	for i := uint(0); i < sz; i++ {
		its[i] = &Iterator{it: (*[arrayDimenMax]C.Iterator_t)(unsafe.Pointer(cit))[i], db: db}
		runtime.SetFinalizer(its[i], finalize)
	}
	return
}

// An iterator is either positioned at a key/value pair, or
// not valid.  This method returns true iff the iterator is valid.
func (it *Iterator) Valid() bool {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	return C.IteratorValid(cit).toBool()
}

// Position at the first key in the source.  The iterator is Valid()
// after this call iff the source is not empty.
func (it *Iterator) SeekToFirst() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeekToFirst(cit)
}

// Position at the last key in the source.  The iterator is
// Valid() after this call iff the source is not empty.
func (it *Iterator) SeekToLast() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeekToLast(cit)
}

// Position at the first key in the source that at or past target
// The iterator is Valid() after this call iff the source contains
// an entry that comes at or past target.
func (it *Iterator) Seek(key []byte) {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	ckey := newSliceFromBytes(key)
	defer ckey.del()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeek(cit, &ckey.slc)
}

// Moves to the next entry in the source.  After this call, Valid() is
// true iff the iterator was not positioned at the last entry in the source.
// REQUIRES: Valid()
func (it *Iterator) Next() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorNext(cit)
}

// Moves to the previous entry in the source.  After this call, Valid() is
// true iff the iterator was not positioned at the first entry in source.
// REQUIRES: Valid()
func (it *Iterator) Prev() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorPrev(cit)
}

// Return the key for the current entry.  The underlying storage for
// the returned slice is valid only until the next modification of
// the iterator.
// REQUIRES: Valid()
func (it *Iterator) Key() (key []byte){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	ckey := C.IteratorKey(cit)
	key = ckey.cToBytes()
	return
}

// Return the value for the current entry.  The underlying storage for
// the returned slice is valid only until the next modification of
// the iterator.
// REQUIRES: !AtEnd() && !AtStart()
func (it *Iterator) Value() (val []byte){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	cval := C.IteratorValue(cit)
	val = cval.cToBytes()
	return
}

// If an error has occurred, return it.  Else return an ok status.
// If non-blocking IO is requested and this operation cannot be
// satisfied without doing some IO, then this returns Status::Incomplete().
func (it *Iterator) Status() (val *Status){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	cval := C.IteratorStatus(cit)
	val = cval.toStatus()
	return
}
<commit_msg>add more iterator code for db_test.go<commit_after>// Copyright (c) 2015, Dean ChaoJun Pan.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
//
// An iterator yields a sequence of key/value pairs from a source.
// The following class defines the interface.  Multiple implementations
// are provided by this library.  In particular, iterators are provided
// to access the contents of a Table or a DB.
//
// Multiple threads can invoke const methods on an Iterator without
// external synchronization, but if any of the threads may call a
// non-const method, all threads accessing the same Iterator must use
// external synchronization.

package rocksdb

/*
#include "iterator.h"
*/
import "C"

import (
	"runtime"
	"unsafe"
	"sync"
)

// Go Iterator
type Iterator struct {
	it C.Iterator_t
	// Thread safe
	mutex sync.Mutex
	db *DB // make sure the iterator is deleted before the db
	// true if the underlying c object is deleted
	closed bool
}

// Release resources
func (it *Iterator) finalize() {
	if !it.closed {
		it.closed = true
		var cit *C.Iterator_t = &it.it
		C.DeleteIteratorT(cit, toCBool(false))
	}
}

// Close the Iterator
func (it *Iterator) Close() {
	runtime.SetFinalizer(it, nil)
	it.finalize()
}

// Iterator of C to go iterator
func (cit *C.Iterator_t) toIterator(db *DB) (it *Iterator) {
	it = &Iterator{it: *cit, mutex: sync.Mutex{}, db: db}
	runtime.SetFinalizer(it, finalize)
	return
}

// Array of C iterators to array of go iterators
func newIteratorArrayFromCArray(cit *C.Iterator_t, sz uint, db *DB) (its []*Iterator) {
	defer C.DeleteIteratorTArray(cit)
	its = make([]*Iterator, sz)
	for i := uint(0); i < sz; i++ {
		its[i] = &Iterator{it: (*[arrayDimenMax]C.Iterator_t)(unsafe.Pointer(cit))[i], mutex: sync.Mutex{}, db: db}
		runtime.SetFinalizer(its[i], finalize)
	}
	return
}

// An iterator is either positioned at a key/value pair, or
// not valid.  This method returns true iff the iterator is valid.
func (it *Iterator) Valid() bool {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	return C.IteratorValid(cit).toBool()
}

// Position at the first key in the source.  The iterator is Valid()
// after this call iff the source is not empty.
func (it *Iterator) SeekToFirst() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeekToFirst(cit)
}

// Position at the last key in the source.  The iterator is
// Valid() after this call iff the source is not empty.
func (it *Iterator) SeekToLast() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeekToLast(cit)
}

// Position at the first key in the source that at or past target
// The iterator is Valid() after this call iff the source contains
// an entry that comes at or past target.
func (it *Iterator) Seek(key []byte) {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	ckey := newSliceFromBytes(key)
	defer ckey.del()

	var cit *C.Iterator_t = &it.it
	C.IteratorSeek(cit, &ckey.slc)
}

// Moves to the next entry in the source.  After this call, Valid() is
// true iff the iterator was not positioned at the last entry in the source.
// REQUIRES: Valid()
func (it *Iterator) Next() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorNext(cit)
}

// Moves to the previous entry in the source.  After this call, Valid() is
// true iff the iterator was not positioned at the first entry in source.
// REQUIRES: Valid()
func (it *Iterator) Prev() {
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	C.IteratorPrev(cit)
}

// Return the key for the current entry.  The underlying storage for
// the returned slice is valid only until the next modification of
// the iterator.
// REQUIRES: Valid()
func (it *Iterator) Key() (key []byte){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	ckey := C.IteratorKey(cit)
	key = ckey.cToBytes()
	return
}

// Return the value for the current entry.  The underlying storage for
// the returned slice is valid only until the next modification of
// the iterator.
// REQUIRES: !AtEnd() && !AtStart()
func (it *Iterator) Value() (val []byte){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	cval := C.IteratorValue(cit)
	val = cval.cToBytes()
	return
}

// If an error has occurred, return it.  Else return an ok status.
// If non-blocking IO is requested and this operation cannot be
// satisfied without doing some IO, then this returns Status::Incomplete().
func (it *Iterator) Status() (val *Status){
	defer it.mutex.Unlock()
	it.mutex.Lock()

	var cit *C.Iterator_t = &it.it
	cval := C.IteratorStatus(cit)
	val = cval.toStatus()
	return
}
<|endoftext|>


<commit_before>package helpers

import (
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"reflect"

	"strings"

	"crypto/tls"

	"net"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/models"
	"github.com/pkg/errors"
)

var (
	mDbSession  *mgo.Session
	mDbDatabase string
)

// ConnectDB connects to mongodb and stores the session
func ConnectMDB(url string, database string) {
	var err error

	log := cache.GetLogger()
	log.WithField("module", "mdb").Info("Connecting to " + url)

	// TODO: logger
	//mgo.SetLogger(cache.GetLogger())

	newUrl := strings.TrimSuffix(url, "?ssl=true")
	newUrl = strings.Replace(newUrl, "ssl=true&", "", -1)

	dialInfo, err := mgo.ParseURL(newUrl)
	if err != nil {
		log.WithField("module", "mdb").Error(err.Error())
		panic(err)
	}

	// setup TLS if we use SSL
	if newUrl != url {
		tlsConfig := &tls.Config{}
		tlsConfig.InsecureSkipVerify = true

		dialInfo.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
			conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
			return conn, err
		}
	}

	mDbSession, err = mgo.DialWithInfo(dialInfo)
	if err != nil {
		log.WithField("module", "mdb").Error(err.Error())
		panic(err)
	}

	mDbSession.SetMode(mgo.Monotonic, true)
	mDbSession.SetSafe(&mgo.Safe{WMode: "majority"})

	mDbDatabase = database

	log.WithField("module", "mdb").Info("Connected!")
}

// GetDB is a simple getter for the mongodb database.
func GetMDb() *mgo.Database {
	return mDbSession.DB(mDbDatabase)
}

// GetDB is a simple getter for the mongodb session.
func GetMDbSession() *mgo.Session {
	return mDbSession
}

func MDbInsert(collection models.MongoDbCollection, data interface{}) (rid bson.ObjectId, err error) {
	ptr := reflect.New(reflect.TypeOf(data))
	temp := ptr.Elem()
	temp.Set(reflect.ValueOf(data))

	v := temp.FieldByName("ID")

	if !v.IsValid() {
		return bson.ObjectId(""), errors.New("invalid data")
	}

	newID := bson.NewObjectId()
	if v.String() == "" {
		v.SetString(reflect.ValueOf(newID).String())
	}

	err = GetMDb().C(collection.String()).Insert(temp.Interface())

	if err != nil {
		return bson.ObjectId(""), err
	}

	return newID, nil
}

func MDbUpdate(collection models.MongoDbCollection, id bson.ObjectId, data interface{}) (rid bson.ObjectId, err error) {
	if !id.Valid() {
		return bson.ObjectId(""), errors.New("invalid id")
	}

	err = GetMDb().C(collection.String()).UpdateId(id, data)

	if err != nil {
		return bson.ObjectId(""), err
	}

	return id, nil
}

func MDbUpsertID(collection models.MongoDbCollection, id bson.ObjectId, data interface{}) (rid bson.ObjectId, err error) {
	if !id.Valid() {
		id = bson.NewObjectId()
	}

	_, err = GetMDb().C(collection.String()).UpsertId(id, data)

	if err != nil {
		return bson.ObjectId(""), err
	}

	return id, nil
}

func MDbUpsert(collection models.MongoDbCollection, selector interface{}, data interface{}) (err error) {
	_, err = GetMDb().C(collection.String()).Upsert(selector, data)

	return err
}

func MDbDelete(collection models.MongoDbCollection, id bson.ObjectId) (err error) {
	if !id.Valid() {
		return errors.New("invalid id")
	}

	err = GetMDb().C(collection.String()).RemoveId(id)

	if err != nil {
		return err
	}

	return nil
}

func MDbFind(collection models.MongoDbCollection, selection interface{}) (query *mgo.Query) {
	return GetMDb().C(collection.String()).Find(selection)
}
<commit_msg>[mongodb] less reflection, more sense<commit_after>package helpers

import (
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"reflect"

	"strings"

	"crypto/tls"

	"net"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/models"
	"github.com/pkg/errors"
)

var (
	mDbSession  *mgo.Session
	mDbDatabase string
)

// ConnectDB connects to mongodb and stores the session
func ConnectMDB(url string, database string) {
	var err error

	log := cache.GetLogger()
	log.WithField("module", "mdb").Info("Connecting to " + url)

	// TODO: logger
	//mgo.SetLogger(cache.GetLogger())

	newUrl := strings.TrimSuffix(url, "?ssl=true")
	newUrl = strings.Replace(newUrl, "ssl=true&", "", -1)

	dialInfo, err := mgo.ParseURL(newUrl)
	if err != nil {
		log.WithField("module", "mdb").Error(err.Error())
		panic(err)
	}

	// setup TLS if we use SSL
	if newUrl != url {
		tlsConfig := &tls.Config{}
		tlsConfig.InsecureSkipVerify = true

		dialInfo.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
			conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
			return conn, err
		}
	}

	mDbSession, err = mgo.DialWithInfo(dialInfo)
	if err != nil {
		log.WithField("module", "mdb").Error(err.Error())
		panic(err)
	}

	mDbSession.SetMode(mgo.Monotonic, true)
	mDbSession.SetSafe(&mgo.Safe{WMode: "majority"})

	mDbDatabase = database

	log.WithField("module", "mdb").Info("Connected!")
}

// GetDB is a simple getter for the mongodb database.
func GetMDb() *mgo.Database {
	return mDbSession.DB(mDbDatabase)
}

// GetDB is a simple getter for the mongodb session.
func GetMDbSession() *mgo.Session {
	return mDbSession
}

func MDbInsert(collection models.MongoDbCollection, data interface{}) (rid bson.ObjectId, err error) {
	ptr := reflect.New(reflect.TypeOf(data))
	temp := ptr.Elem()
	temp.Set(reflect.ValueOf(data))

	v := temp.FieldByName("ID")

	if !v.IsValid() {
		return bson.ObjectId(""), errors.New("invalid data")
	}

	newID := v.String()
	if newID == "" {
		newID = string(bson.NewObjectId())
		v.SetString(newID)
	}

	err = GetMDb().C(collection.String()).Insert(temp.Interface())

	if err != nil {
		return bson.ObjectId(""), err
	}

	return bson.ObjectId(newID), nil
}

func MDbUpdate(collection models.MongoDbCollection, id bson.ObjectId, data interface{}) (rid bson.ObjectId, err error) {
	if !id.Valid() {
		return bson.ObjectId(""), errors.New("invalid id")
	}

	err = GetMDb().C(collection.String()).UpdateId(id, data)

	if err != nil {
		return bson.ObjectId(""), err
	}

	return id, nil
}

func MDbUpsertID(collection models.MongoDbCollection, id bson.ObjectId, data interface{}) (rid bson.ObjectId, err error) {
	if !id.Valid() {
		id = bson.NewObjectId()
	}

	_, err = GetMDb().C(collection.String()).UpsertId(id, data)

	if err != nil {
		return bson.ObjectId(""), err
	}

	return id, nil
}

func MDbUpsert(collection models.MongoDbCollection, selector interface{}, data interface{}) (err error) {
	_, err = GetMDb().C(collection.String()).Upsert(selector, data)

	return err
}

func MDbDelete(collection models.MongoDbCollection, id bson.ObjectId) (err error) {
	if !id.Valid() {
		return errors.New("invalid id")
	}

	err = GetMDb().C(collection.String()).RemoveId(id)

	if err != nil {
		return err
	}

	return nil
}

func MDbFind(collection models.MongoDbCollection, selection interface{}) (query *mgo.Query) {
	return GetMDb().C(collection.String()).Find(selection)
}
<|endoftext|>


<commit_before>/*
 * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or 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 cmd

import (
	"encoding/xml"
	"io"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/lifecycle"
	"github.com/minio/minio/pkg/policy"
)

const (
	// Lifecycle configuration file.
	bucketLifecycleConfig = "lifecycle.xml"
)

// PutBucketLifecycleHandler - This HTTP handler stores given bucket lifecycle configuration as per
// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lifecycle-mgmt.html
func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "PutBucketLifecycle")

	defer logger.AuditLog(w, r, "PutBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// PutBucketLifecycle always needs a Content-Md5
	if _, ok := r.Header["Content-Md5"]; !ok {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
		return
	}

	bucketLifecycle, err := lifecycle.ParseLifecycleConfig(io.LimitReader(r.Body, r.ContentLength))
	if err != nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r))
		return
	}

	if err = objAPI.SetBucketLifecycle(ctx, bucket, bucketLifecycle); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	globalLifecycleSys.Set(bucket, *bucketLifecycle)
	globalNotificationSys.SetBucketLifecycle(ctx, bucket, bucketLifecycle)

	// Success.
	writeSuccessNoContent(w)
}

// GetBucketLifecycleHandler - This HTTP handler returns bucket policy configuration.
func (api objectAPIHandlers) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "GetBucketLifecycle")

	defer logger.AuditLog(w, r, "GetBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// Read bucket access lifecycle.
	bucketLifecycle, err := objAPI.GetBucketLifecycle(ctx, bucket)
	if err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	lifecycleData, err := xml.Marshal(bucketLifecycle)
	if err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// Write lifecycle configuration to client.
	writeSuccessResponseXML(w, lifecycleData)
}

// DeleteBucketLifecycleHandler - This HTTP handler removes bucket lifecycle configuration.
func (api objectAPIHandlers) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "DeleteBucketLifecycle")

	defer logger.AuditLog(w, r, "DeleteBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	if err := objAPI.DeleteBucketLifecycle(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	globalLifecycleSys.Remove(bucket)
	globalNotificationSys.RemoveBucketLifecycle(ctx, bucket)

	// Success.
	writeSuccessNoContent(w)
}
<commit_msg>PutBucketLifeCycleConfiguration: Return 200 instead of 204 (#8656)<commit_after>/*
 * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or 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 cmd

import (
	"encoding/xml"
	"io"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/lifecycle"
	"github.com/minio/minio/pkg/policy"
)

const (
	// Lifecycle configuration file.
	bucketLifecycleConfig = "lifecycle.xml"
)

// PutBucketLifecycleHandler - This HTTP handler stores given bucket lifecycle configuration as per
// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lifecycle-mgmt.html
func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "PutBucketLifecycle")

	defer logger.AuditLog(w, r, "PutBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// PutBucketLifecycle always needs a Content-Md5
	if _, ok := r.Header["Content-Md5"]; !ok {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
		return
	}

	bucketLifecycle, err := lifecycle.ParseLifecycleConfig(io.LimitReader(r.Body, r.ContentLength))
	if err != nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r))
		return
	}

	if err = objAPI.SetBucketLifecycle(ctx, bucket, bucketLifecycle); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	globalLifecycleSys.Set(bucket, *bucketLifecycle)
	globalNotificationSys.SetBucketLifecycle(ctx, bucket, bucketLifecycle)

	// Success.
	writeSuccessResponseHeadersOnly(w)
}

// GetBucketLifecycleHandler - This HTTP handler returns bucket policy configuration.
func (api objectAPIHandlers) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "GetBucketLifecycle")

	defer logger.AuditLog(w, r, "GetBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// Read bucket access lifecycle.
	bucketLifecycle, err := objAPI.GetBucketLifecycle(ctx, bucket)
	if err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	lifecycleData, err := xml.Marshal(bucketLifecycle)
	if err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	// Write lifecycle configuration to client.
	writeSuccessResponseXML(w, lifecycleData)
}

// DeleteBucketLifecycleHandler - This HTTP handler removes bucket lifecycle configuration.
func (api objectAPIHandlers) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "DeleteBucketLifecycle")

	defer logger.AuditLog(w, r, "DeleteBucketLifecycle", mustGetClaimsFromToken(r))

	objAPI := api.ObjectAPI()
	if objAPI == nil {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket"]

	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
		return
	}

	// Check if bucket exists.
	if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	if err := objAPI.DeleteBucketLifecycle(ctx, bucket); err != nil {
		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
		return
	}

	globalLifecycleSys.Remove(bucket)
	globalNotificationSys.RemoveBucketLifecycle(ctx, bucket)

	// Success.
	writeSuccessNoContent(w)
}
<|endoftext|>


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

package main

import (
	"context"
	"fmt"
	"os/exec"
	"sort"
	"strings"
	"unicode"

	"github.com/AlecAivazis/survey/v2"
	runapi "google.golang.org/api/run/v1beta1"
)

const (
	defaultRunRegion = "us-central1"
	defaultRunMemory = "512Mi"
)

func deploy(project, name, image, region string, envs []string) (string, error) {
	cmd := exec.Command("gcloud", "beta", "run", "deploy", "-q",
		name,
		"--project", project,
		"--platform", "managed",
		"--image", image,
		"--region", region,
		"--memory", defaultRunMemory,
		"--allow-unauthenticated",
		"--set-env-vars", strings.Join(envs, ","))
	if b, err := cmd.CombinedOutput(); err != nil {
		return "", fmt.Errorf("failed to deploy to Cloud Run: %+v. output:\n%s", err, string(b))
	}
	return serviceURL(project, name, region)
}

func projectRunLocations(ctx context.Context, project string) ([]string, error) {
	runSvc, err := runapi.NewService(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize Run API client: %+v", err)
	}

	var locations []string
	if err := runapi.NewProjectsLocationsService(runSvc).
		List("projects/"+project).Pages(ctx, func(resp *runapi.ListLocationsResponse) error {
		for _, v := range resp.Locations {
			locations = append(locations, v.LocationId)
		}
		return nil
	}); err != nil {
		return nil, fmt.Errorf("request to query Cloud Run locations failed: %+v", err)
	}
	sort.Strings(locations)
	return locations, nil
}

func promptDeploymentRegion(ctx context.Context, project string) (string, error) {
	locations, err := projectRunLocations(ctx, project)
	if err != nil {
		return "", fmt.Errorf("cannot retrieve Cloud Run locations: %+v", err)
	}

	var choice string
	if err := survey.AskOne(&survey.Select{
		Message: "Choose a region to deploy this application:",
		Options: locations,
		Default: defaultRunRegion,
	}, &choice,
		surveyIconOpts,
		survey.WithValidator(survey.Required),
	); err != nil {
		return choice, fmt.Errorf("could not choose a region: %+v", err)
	}
	return choice, nil
}

func serviceURL(project, name, region string) (string, error) {
	cmd := exec.Command("gcloud", "beta", "run", "services", "describe", name,
		"--project", project,
		"--platform", "managed",
		"--region", region,
		"--format", "value(status.domain)")

	b, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("deployment to Cloud Run failed: %+v. output:\n%s", err, string(b))
	}
	return strings.TrimSpace(string(b)), nil
}

// tryFixServiceName attempts replace the service name with a better one to
// prevent deployment failures due to Cloud Run service naming constraints such
// as:
//
//   * names with a leading non-letter (e.g. digit or '-') are prefixed
//   * names over 63 characters are truncated
//   * names ending with a '-' have the suffix trimmed
func tryFixServiceName(name string) string {
	if name == "" {
		return name
	}
	if !unicode.IsLetter([]rune(name)[0]) {
		name = fmt.Sprintf("svc-%s", name)
	}
	if len(name) > 63 {
		name = name[:63]
	}
	for name[len(name)-1] == '-' {
		name = name[:len(name)-1]
	}
	return name
}
<commit_msg>use run v1alpha1 api (#69)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"fmt"
	"os/exec"
	"sort"
	"strings"
	"unicode"

	"github.com/AlecAivazis/survey/v2"
	runapi "google.golang.org/api/run/v1alpha1"
)

const (
	defaultRunRegion = "us-central1"
	defaultRunMemory = "512Mi"
)

func deploy(project, name, image, region string, envs []string) (string, error) {
	cmd := exec.Command("gcloud", "beta", "run", "deploy", "-q",
		name,
		"--project", project,
		"--platform", "managed",
		"--image", image,
		"--region", region,
		"--memory", defaultRunMemory,
		"--allow-unauthenticated",
		"--set-env-vars", strings.Join(envs, ","))
	if b, err := cmd.CombinedOutput(); err != nil {
		return "", fmt.Errorf("failed to deploy to Cloud Run: %+v. output:\n%s", err, string(b))
	}
	return serviceURL(project, name, region)
}

func projectRunLocations(ctx context.Context, project string) ([]string, error) {
	runSvc, err := runapi.NewService(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize Run API client: %+v", err)
	}

	var locations []string
	if err := runapi.NewProjectsLocationsService(runSvc).
		List("projects/"+project).Pages(ctx, func(resp *runapi.ListLocationsResponse) error {
		for _, v := range resp.Locations {
			locations = append(locations, v.LocationId)
		}
		return nil
	}); err != nil {
		return nil, fmt.Errorf("request to query Cloud Run locations failed: %+v", err)
	}
	sort.Strings(locations)
	return locations, nil
}

func promptDeploymentRegion(ctx context.Context, project string) (string, error) {
	locations, err := projectRunLocations(ctx, project)
	if err != nil {
		return "", fmt.Errorf("cannot retrieve Cloud Run locations: %+v", err)
	}

	var choice string
	if err := survey.AskOne(&survey.Select{
		Message: "Choose a region to deploy this application:",
		Options: locations,
		Default: defaultRunRegion,
	}, &choice,
		surveyIconOpts,
		survey.WithValidator(survey.Required),
	); err != nil {
		return choice, fmt.Errorf("could not choose a region: %+v", err)
	}
	return choice, nil
}

func serviceURL(project, name, region string) (string, error) {
	cmd := exec.Command("gcloud", "beta", "run", "services", "describe", name,
		"--project", project,
		"--platform", "managed",
		"--region", region,
		"--format", "value(status.domain)")

	b, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("deployment to Cloud Run failed: %+v. output:\n%s", err, string(b))
	}
	return strings.TrimSpace(string(b)), nil
}

// tryFixServiceName attempts replace the service name with a better one to
// prevent deployment failures due to Cloud Run service naming constraints such
// as:
//
//   * names with a leading non-letter (e.g. digit or '-') are prefixed
//   * names over 63 characters are truncated
//   * names ending with a '-' have the suffix trimmed
func tryFixServiceName(name string) string {
	if name == "" {
		return name
	}
	if !unicode.IsLetter([]rune(name)[0]) {
		name = fmt.Sprintf("svc-%s", name)
	}
	if len(name) > 63 {
		name = name[:63]
	}
	for name[len(name)-1] == '-' {
		name = name[:len(name)-1]
	}
	return name
}
<|endoftext|>