<commit_before>package main

import (
	"log"
	"net/http"
	"time"

	_ "net/http/pprof"

	"github.com/kelseyhightower/envconfig"

	"github.com/brnstz/bus/api"
	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
)

func main() {
	var err error
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	err = envconfig.Process("bus", &conf.DB)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.API)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Cache)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Partner)
	if err != nil {
		log.Fatal(err)
	}

	time.Local, err = time.LoadLocation("America/New_York")
	if err != nil {
		log.Fatal(err)
	}

	etc.DBConn = etc.MustDB()

	if conf.API.BuildTimestamp == 0 {
		conf.API.BuildTimestamp = time.Now().Unix()
	}

	handler := api.NewHandler()

	err = api.InitRouteCache()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		http.ListenAndServe("localhost:6060", nil)
	}()

	log.Fatal(http.ListenAndServe(conf.API.Addr, handler))
}
<commit_msg>removing pprof stuff<commit_after>package main

import (
	"log"
	"net/http"
	"time"

	"github.com/kelseyhightower/envconfig"

	"github.com/brnstz/bus/api"
	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
)

func main() {
	var err error
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	err = envconfig.Process("bus", &conf.DB)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.API)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Cache)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Partner)
	if err != nil {
		log.Fatal(err)
	}

	time.Local, err = time.LoadLocation("America/New_York")
	if err != nil {
		log.Fatal(err)
	}

	etc.DBConn = etc.MustDB()

	if conf.API.BuildTimestamp == 0 {
		conf.API.BuildTimestamp = time.Now().Unix()
	}

	handler := api.NewHandler()

	err = api.InitRouteCache()
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.ListenAndServe(conf.API.Addr, handler))
}
<|endoftext|>


<commit_before>// +build go1.9

package instagrpc

import (
	"context"
	"io"
	"net"
	"net/http"

	instana "github.com/instana/go-sensor"
	ot "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// UnaryClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls.
// It injects Instana OpenTracing headers into outgoing unary requests to ensure trace propagation
// throughout the call.
// If the server call results with an error, its message will be attached to the span logs.
func UnaryClientInterceptor(sensor *instana.Sensor) grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, callOpts ...grpc.CallOption) error {
		parentSpan, ok := instana.SpanFromContext(ctx)
		if !ok {
			// don't trace the exit call if there was no entry span provided
			return invoker(ctx, method, req, reply, cc, callOpts...)
		}

		sp := startClientSpan(parentSpan, cc.Target(), method, "unary", sensor)
		defer sp.Finish()

		if err := invoker(outgoingTracingContext(ctx, sp), method, req, reply, cc, callOpts...); err != nil {
			addRPCError(sp, err)

			return err
		}

		return nil
	}
}

// StreamClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls.
// It injects Instana OpenTracing headers into outgoing stream requests to ensure trace propagation
// throughout the call. The span is finished as soon as server closes the stream or returns an error.
// Any error occurred during the request is attached to the span logs.
func StreamClientInterceptor(sensor *instana.Sensor) grpc.StreamClientInterceptor {
	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {

		parentSpan, ok := instana.SpanFromContext(ctx)
		if !ok {
			// don't trace the exit call if there was no entry span provided
			return streamer(ctx, desc, cc, method, opts...)
		}

		sp := startClientSpan(parentSpan, cc.Target(), method, "stream", sensor)
		stream, err := streamer(outgoingTracingContext(ctx, sp), desc, cc, method, opts...)
		if err != nil {
			addRPCError(sp, err)
			sp.Finish()

			return nil, err
		}

		return wrappedClientStream{
			ClientStream:  stream,
			Span:          sp,
			ServerStreams: desc.ServerStreams,
		}, nil
	}
}

func startClientSpan(parentSpan ot.Span, target, method, callType string, sensor *instana.Sensor) ot.Span {
	host, port, err := net.SplitHostPort(target)
	if err != nil {
		sensor.Logger().Info("failed to extract server host and port from request metadata: %s", err)

		// take our best guess and use :authority as a host if the net.SplitHostPort() fails to parse
		host, port = target, ""
	}

	opts := []ot.StartSpanOption{
		ext.SpanKindRPCClient,
		ot.ChildOf(parentSpan.Context()),
		ot.Tags{
			"rpc.flavor":    "grpc",
			"rpc.call":      method,
			"rpc.call_type": callType,
			"rpc.host":      host,
			"rpc.port":      port,
		},
	}

	return parentSpan.Tracer().StartSpan("rpc-client", opts...)
}

func outgoingTracingContext(ctx context.Context, span ot.Span) context.Context {
	// gather opentracing headers and inject them into request metadata omitting empty values
	md, ok := metadata.FromOutgoingContext(ctx)
	if !ok {
		md = metadata.New(nil)
	}

	headers := http.Header{}
	span.Tracer().Inject(span.Context(), ot.HTTPHeaders, ot.HTTPHeadersCarrier(headers))

	for k := range headers {
		if v := headers.Get(k); v != "" {
			md.Set(k, v)
		}
	}

	return metadata.NewOutgoingContext(ctx, md)
}

type wrappedClientStream struct {
	grpc.ClientStream
	Span          ot.Span
	ServerStreams bool
}

func (cs wrappedClientStream) RecvMsg(m interface{}) error {
	err := cs.ClientStream.RecvMsg(m)
	if err != nil {
		if err != io.EOF {
			addRPCError(cs.Span, err)
		}
	}

	if err != nil || !cs.ServerStreams {
		cs.Span.Finish()
	}

	return err
}
<commit_msg>Improve instrumented GRPC client log messages<commit_after>// +build go1.9

package instagrpc

import (
	"context"
	"io"
	"net"
	"net/http"

	instana "github.com/instana/go-sensor"
	ot "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// UnaryClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls.
// It injects Instana OpenTracing headers into outgoing unary requests to ensure trace propagation
// throughout the call.
// If the server call results with an error, its message will be attached to the span logs.
func UnaryClientInterceptor(sensor *instana.Sensor) grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, callOpts ...grpc.CallOption) error {
		parentSpan, ok := instana.SpanFromContext(ctx)
		if !ok {
			// don't trace the exit call if there was no entry span provided
			return invoker(ctx, method, req, reply, cc, callOpts...)
		}

		sp := startClientSpan(parentSpan, cc.Target(), method, "unary", sensor)
		defer sp.Finish()

		if err := invoker(outgoingTracingContext(ctx, sp), method, req, reply, cc, callOpts...); err != nil {
			addRPCError(sp, err)

			return err
		}

		return nil
	}
}

// StreamClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls.
// It injects Instana OpenTracing headers into outgoing stream requests to ensure trace propagation
// throughout the call. The span is finished as soon as server closes the stream or returns an error.
// Any error occurred during the request is attached to the span logs.
func StreamClientInterceptor(sensor *instana.Sensor) grpc.StreamClientInterceptor {
	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {

		parentSpan, ok := instana.SpanFromContext(ctx)
		if !ok {
			// don't trace the exit call if there was no entry span provided
			return streamer(ctx, desc, cc, method, opts...)
		}

		sp := startClientSpan(parentSpan, cc.Target(), method, "stream", sensor)
		stream, err := streamer(outgoingTracingContext(ctx, sp), desc, cc, method, opts...)
		if err != nil {
			addRPCError(sp, err)
			sp.Finish()

			return nil, err
		}

		return wrappedClientStream{
			ClientStream:  stream,
			Span:          sp,
			ServerStreams: desc.ServerStreams,
		}, nil
	}
}

func startClientSpan(parentSpan ot.Span, target, method, callType string, sensor *instana.Sensor) ot.Span {
	host, port, err := net.SplitHostPort(target)
	if err != nil {
		sensor.Logger().Info("failed to extract server host and port from request metadata:", err)

		// take our best guess and use :authority as a host if the net.SplitHostPort() fails to parse
		host, port = target, ""
	}

	opts := []ot.StartSpanOption{
		ext.SpanKindRPCClient,
		ot.ChildOf(parentSpan.Context()),
		ot.Tags{
			"rpc.flavor":    "grpc",
			"rpc.call":      method,
			"rpc.call_type": callType,
			"rpc.host":      host,
			"rpc.port":      port,
		},
	}

	return parentSpan.Tracer().StartSpan("rpc-client", opts...)
}

func outgoingTracingContext(ctx context.Context, span ot.Span) context.Context {
	// gather opentracing headers and inject them into request metadata omitting empty values
	md, ok := metadata.FromOutgoingContext(ctx)
	if !ok {
		md = metadata.New(nil)
	}

	headers := http.Header{}
	span.Tracer().Inject(span.Context(), ot.HTTPHeaders, ot.HTTPHeadersCarrier(headers))

	for k := range headers {
		if v := headers.Get(k); v != "" {
			md.Set(k, v)
		}
	}

	return metadata.NewOutgoingContext(ctx, md)
}

type wrappedClientStream struct {
	grpc.ClientStream
	Span          ot.Span
	ServerStreams bool
}

func (cs wrappedClientStream) RecvMsg(m interface{}) error {
	err := cs.ClientStream.RecvMsg(m)
	if err != nil {
		if err != io.EOF {
			addRPCError(cs.Span, err)
		}
	}

	if err != nil || !cs.ServerStreams {
		cs.Span.Finish()
	}

	return err
}
<|endoftext|>


<commit_before>package ansible_test

import (
	"os"
	"runtime"
	"strings"

	"github.com/werf/werf/integration/utils"

	"github.com/werf/werf/integration/utils/liveexec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Stapel builder with ansible", func() {
	Context("when building image based on alpine, ubuntu or centos", func() {
		AfterEach(func() {
			werfPurge("general", liveexec.ExecCommandOptions{}, "--force")
		})

		It("should successfully build image using arbitrary ansible modules", func() {
			Expect(werfBuild("general", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when building stapel image based on centos 7", func() {
		AfterEach(func() {
			werfPurge("yum1", liveexec.ExecCommandOptions{}, "--force")
		})

		It("successfully installs packages using yum module", func() {
			Expect(werfBuild("yum1", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when building stapel image based on centos 8", func() {
		AfterEach(func() {
			werfPurge("yum2", liveexec.ExecCommandOptions{}, "--force")
		})

		It("successfully installs packages using yum module", func() {
			Skip("FIXME https://github.com/werf/werf/issues/1983")
			Expect(werfBuild("yum2", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when become_user task option used", func() {
		AfterEach(func() {
			werfPurge("become_user", liveexec.ExecCommandOptions{}, "--force")
		})

		It("successfully installs packages using yum module", func() {
			Skip("FIXME https://github.com/werf/werf/issues/1806")
			Expect(werfBuild("become_user", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when using apt_key module used (1)", func() {
		AfterEach(func() {
			werfPurge("apt_key1-001", liveexec.ExecCommandOptions{}, "--force")
		})

		It("should fail to install package without a key and succeed with the key", func() {
			Skip("https://github.com/werf/werf/issues/2000")

			gotNoPubkey := false
			Expect(werfBuild("apt_key1-001", liveexec.ExecCommandOptions{
				OutputLineHandler: func(line string) {
					if strings.Index(line, `public key is not available: NO_PUBKEY`) != -1 {
						gotNoPubkey = true
					}
				},
			})).NotTo(Succeed())
			Expect(gotNoPubkey).To(BeTrue())

			gotPackageInstallDone := false
			Expect(werfBuild("apt_key1-002", liveexec.ExecCommandOptions{
				OutputLineHandler: func(line string) {
					if strings.Index(line, `apt 'Install package from new repository' [clickhouse-client]`) != -1 {
						gotPackageInstallDone = true
					}
					Expect(line).NotTo(MatchRegexp(`apt 'Install package from new repository' \[clickhouse-client\] \(".*" seconds\) FAILED`))
				},
			})).To(Succeed())
			Expect(gotPackageInstallDone).To(BeTrue())
		})
	})

	Context("when using apt_key module used (2)", func() {
		AfterEach(func() {
			werfPurge("apt_key2", liveexec.ExecCommandOptions{}, "--force")
		})

		It("should fail to install package without a key and succeed with the key", func() {
			Skip("https://github.com/werf/werf/issues/2000")

			Expect(werfBuild("apt_key2", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when apt-mark from apt module used (https://github.com/werf/werf/issues/1820)", func() {
		AfterEach(func() {
			werfPurge("apt_mark_panic_1820", liveexec.ExecCommandOptions{}, "--force")
		})

		It("should not panic in all supported ubuntu versions", func() {
			Expect(werfBuild("apt_mark_panic_1820", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when using yarn module to install nodejs packages", func() {
		BeforeEach(func() {
			os.RemoveAll("yarn/.git")
			os.RemoveAll("yarn_repo")
		})

		AfterEach(func() {
			werfPurge("yarn", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("yarn/.git")
			os.RemoveAll("yarn_repo")
		})

		It("should install packages successfully", func() {
			Expect(utils.SetGitRepoState("yarn", "yarn_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("yarn", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when installing python requirements using ansible and python files contain utf-8 chars", func() {
		BeforeEach(func() {
			os.RemoveAll("python_encoding/.git")
			os.RemoveAll("python_encoding_repo")
		})

		AfterEach(func() {
			werfPurge("python_encoding", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("python_encoding/.git")
			os.RemoveAll("python_encoding_repo")
		})

		It("should install packages successfully without utf-8 related problems", func() {
			if runtime.GOOS == "windows" {
				Skip("Skipping on windows")
			}

			Expect(utils.SetGitRepoState("python_encoding", "python_encoding_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("python_encoding", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("Non standard PATH used in the base image (https://github.com/werf/werf/issues/1836) ", func() {
		AfterEach(func() {
			werfPurge("path_redefined_in_stapel_1836", liveexec.ExecCommandOptions{}, "--force")
		})

		It("PATH should not be redefined in stapel build container", func() {
			Expect(werfBuild("path_redefined_in_stapel_1836", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})
})
<commit_msg>[test] Fixup ansible tests: initialize git-repo for fixture projects<commit_after>package ansible_test

import (
	"os"
	"runtime"
	"strings"

	"github.com/werf/werf/integration/utils"

	"github.com/werf/werf/integration/utils/liveexec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Stapel builder with ansible", func() {
	Context("when building image based on alpine, ubuntu or centos", func() {
		AfterEach(func() {
			werfPurge("general", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("general/.git")
			os.RemoveAll("general_repo")
		})

		It("should successfully build image using arbitrary ansible modules", func() {
			Expect(utils.SetGitRepoState("general", "general_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("general", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when building stapel image based on centos 7", func() {
		AfterEach(func() {
			werfPurge("yum1", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("yum1/.git")
			os.RemoveAll("yum1_repo")
		})

		It("successfully installs packages using yum module", func() {
			Expect(utils.SetGitRepoState("yum1", "yum1_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("yum1", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when building stapel image based on centos 8", func() {
		AfterEach(func() {
			werfPurge("yum2", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("yum2/.git")
			os.RemoveAll("yum2_repo")
		})

		It("successfully installs packages using yum module", func() {
			Skip("FIXME https://github.com/werf/werf/issues/1983")
			Expect(utils.SetGitRepoState("yum2", "yum2_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("yum2", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when become_user task option used", func() {
		AfterEach(func() {
			werfPurge("become_user", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("become_user/.git")
			os.RemoveAll("become_user_repo")
		})

		It("successfully installs packages using yum module", func() {
			Skip("FIXME https://github.com/werf/werf/issues/1806")
			Expect(utils.SetGitRepoState("become_user", "become_user_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("become_user", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when using apt_key module used (1)", func() {
		AfterEach(func() {
			werfPurge("apt_key1-001", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("apt_key1-001/.git")
			os.RemoveAll("apt_key1_repo")
		})

		It("should fail to install package without a key and succeed with the key", func() {
			Skip("https://github.com/werf/werf/issues/2000")

			Expect(utils.SetGitRepoState("apt_key1-001", "apt_key1_repo", "initial commit")).To(Succeed())

			gotNoPubkey := false
			Expect(werfBuild("apt_key1-001", liveexec.ExecCommandOptions{
				OutputLineHandler: func(line string) {
					if strings.Index(line, `public key is not available: NO_PUBKEY`) != -1 {
						gotNoPubkey = true
					}
				},
			})).NotTo(Succeed())
			Expect(gotNoPubkey).To(BeTrue())

			gotPackageInstallDone := false
			Expect(werfBuild("apt_key1-002", liveexec.ExecCommandOptions{
				OutputLineHandler: func(line string) {
					if strings.Index(line, `apt 'Install package from new repository' [clickhouse-client]`) != -1 {
						gotPackageInstallDone = true
					}
					Expect(line).NotTo(MatchRegexp(`apt 'Install package from new repository' \[clickhouse-client\] \(".*" seconds\) FAILED`))
				},
			})).To(Succeed())
			Expect(gotPackageInstallDone).To(BeTrue())
		})
	})

	Context("when using apt_key module used (2)", func() {
		AfterEach(func() {
			werfPurge("apt_key2", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("apt_key2/.git")
			os.RemoveAll("apt_key2_repo")
		})

		It("should fail to install package without a key and succeed with the key", func() {
			Skip("https://github.com/werf/werf/issues/2000")

			Expect(utils.SetGitRepoState("apt_key2", "apt_key2_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("apt_key2", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when apt-mark from apt module used (https://github.com/werf/werf/issues/1820)", func() {
		AfterEach(func() {
			werfPurge("apt_mark_panic_1820", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("apt_mark_panic_1820/.git")
			os.RemoveAll("apt_mark_panic_1820_repo")
		})

		It("should not panic in all supported ubuntu versions", func() {
			Expect(utils.SetGitRepoState("apt_mark_panic_1820", "apt_mark_panic_1820_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("apt_mark_panic_1820", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when using yarn module to install nodejs packages", func() {
		BeforeEach(func() {
			os.RemoveAll("yarn/.git")
			os.RemoveAll("yarn_repo")
		})

		AfterEach(func() {
			werfPurge("yarn", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("yarn/.git")
			os.RemoveAll("yarn_repo")
		})

		It("should install packages successfully", func() {
			Expect(utils.SetGitRepoState("yarn", "yarn_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("yarn", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("when installing python requirements using ansible and python files contain utf-8 chars", func() {
		BeforeEach(func() {
			os.RemoveAll("python_encoding/.git")
			os.RemoveAll("python_encoding_repo")
		})

		AfterEach(func() {
			werfPurge("python_encoding", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("python_encoding/.git")
			os.RemoveAll("python_encoding_repo")
		})

		It("should install packages successfully without utf-8 related problems", func() {
			if runtime.GOOS == "windows" {
				Skip("Skipping on windows")
			}

			Expect(utils.SetGitRepoState("python_encoding", "python_encoding_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("python_encoding", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})

	Context("Non standard PATH used in the base image (https://github.com/werf/werf/issues/1836) ", func() {
		AfterEach(func() {
			werfPurge("path_redefined_in_stapel_1836", liveexec.ExecCommandOptions{}, "--force")
			os.RemoveAll("path_redefined_in_stapel_1836/.git")
			os.RemoveAll("path_redefined_in_stapel_1836_repo")
		})

		It("PATH should not be redefined in stapel build container", func() {
			Expect(utils.SetGitRepoState("path_redefined_in_stapel_1836", "path_redefined_in_stapel_1836_repo", "initial commit")).To(Succeed())
			Expect(werfBuild("path_redefined_in_stapel_1836", liveexec.ExecCommandOptions{})).To(Succeed())
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	".."
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-media-v" + gitmedia.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "installers", buildos+"-"+buildarch, name)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		cmd := filepath.Base(path)
		cmd = cmd[0 : len(cmd)-3]
		return buildCommand(path, dir, buildos, buildarch)
	})

	if addenv {
		err := setupInstaller(buildos, buildarch, dir)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
		}
	}
}

func buildCommand(path, dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := filepath.Base(path)
	name = name[0 : len(name)-3]

	bin := filepath.Join(dir, name)

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	cmd := exec.Command("go", "build", "-o", bin, path)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir)
	} else {
		return unixInstaller(buildos, buildarch, dir)
	}
}

func unixInstaller(buildos, buildarch, dir string) error {
	cmd := exec.Command("cp", "script/install.sh.example", filepath.Join(dir, "install.sh"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func winInstaller(buildos, buildarch, dir string) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-media-%s-%s-v%s", os, arch, gitmedia.Version)
}
<commit_msg>rename dir to releases<commit_after>package main

import (
	".."
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-media-v" + gitmedia.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		cmd := filepath.Base(path)
		cmd = cmd[0 : len(cmd)-3]
		return buildCommand(path, dir, buildos, buildarch)
	})

	if addenv {
		err := setupInstaller(buildos, buildarch, dir)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
		}
	}
}

func buildCommand(path, dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := filepath.Base(path)
	name = name[0 : len(name)-3]

	bin := filepath.Join(dir, name)

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	cmd := exec.Command("go", "build", "-o", bin, path)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir)
	} else {
		return unixInstaller(buildos, buildarch, dir)
	}
}

func unixInstaller(buildos, buildarch, dir string) error {
	cmd := exec.Command("cp", "script/install.sh.example", filepath.Join(dir, "install.sh"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func winInstaller(buildos, buildarch, dir string) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-media-%s-%s-v%s", os, arch, gitmedia.Version)
}
<|endoftext|>


<commit_before>package pin

import (
	"context"
	"testing"
	"time"

	"github.com/ipfs/go-ipfs/blocks/blockstore"
	bs "github.com/ipfs/go-ipfs/blockservice"
	"github.com/ipfs/go-ipfs/exchange/offline"
	mdag "github.com/ipfs/go-ipfs/merkledag"

	"gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore"
	dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
)

func randNode() (*mdag.ProtoNode, *cid.Cid) {
	nd := new(mdag.ProtoNode)
	nd.SetData(make([]byte, 32))
	util.NewTimeSeededRand().Read(nd.Data())
	k := nd.Cid()
	return nd, k
}

func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) {
	_, pinned, err := p.IsPinned(c)
	if err != nil {
		t.Fatal(err)
	}

	if !pinned {
		t.Fatal(failmsg)
	}
}

func assertUnpinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) {
	_, pinned, err := p.IsPinned(c)
	if err != nil {
		t.Fatal(err)
	}

	if pinned {
		t.Fatal(failmsg)
	}
}

func TestPinnerBasic(t *testing.T) {
	ctx := context.Background()

	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	a, ak := randNode()
	err := dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// Pin A{}
	err = p.Pin(ctx, a, false)
	if err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, ak, "Failed to find key")

	// create new node c, to be indirectly pinned through b
	c, _ := randNode()
	err = dserv.Add(ctx, c)
	if err != nil {
		t.Fatal(err)
	}
	ck := c.Cid()

	// Create new node b, to be parent to a and c
	b, _ := randNode()
	err = b.AddNodeLink("child", a)
	if err != nil {
		t.Fatal(err)
	}

	err = b.AddNodeLink("otherchild", c)
	if err != nil {
		t.Fatal(err)
	}

	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}
	bk := b.Cid()

	// recursively pin B{A,C}
	err = p.Pin(ctx, b, true)
	if err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, ck, "child of recursively pinned node not found")

	assertPinned(t, p, bk, "Recursively pinned node not found..")

	d, _ := randNode()
	d.AddNodeLink("a", a)
	d.AddNodeLink("c", c)

	e, _ := randNode()
	d.AddNodeLink("e", e)

	// Must be in dagserv for unpin to work
	err = dserv.Add(ctx, e)
	if err != nil {
		t.Fatal(err)
	}
	err = dserv.Add(ctx, d)
	if err != nil {
		t.Fatal(err)
	}

	// Add D{A,C,E}
	err = p.Pin(ctx, d, true)
	if err != nil {
		t.Fatal(err)
	}

	dk := d.Cid()
	assertPinned(t, p, dk, "pinned node not found.")

	// Test recursive unpin
	err = p.Unpin(ctx, dk, true)
	if err != nil {
		t.Fatal(err)
	}

	err = p.Flush()
	if err != nil {
		t.Fatal(err)
	}

	np, err := LoadPinner(dstore, dserv, dserv)
	if err != nil {
		t.Fatal(err)
	}

	// Test directly pinned
	assertPinned(t, np, ak, "Could not find pinned node!")

	// Test recursively pinned
	assertPinned(t, np, bk, "could not find recursively pinned node")
}

func TestIsPinnedLookup(t *testing.T) {
	// We are going to test that lookups work in pins which share
	// the same branches. For that we will construct this tree:
	//
	// A5->A4->A3->A2->A1->A0
	//         /           /
	// B-------           /
	//  \                /
	//   C---------------
	//
	// We will ensure that IsPinned works for all objects both when they
	// are pinned and once they have been unpinned.
	aBranchLen := 6
	if aBranchLen < 3 {
		t.Fatal("set aBranchLen to at least 3")
	}

	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	aNodes := make([]*mdag.ProtoNode, aBranchLen)
	aKeys := make([]*cid.Cid, aBranchLen)
	for i := 0; i < aBranchLen; i++ {
		a, _ := randNode()
		if i >= 1 {
			err := a.AddNodeLink("child", aNodes[i-1])
			if err != nil {
				t.Fatal(err)
			}
		}

		err := dserv.Add(ctx, a)
		if err != nil {
			t.Fatal(err)
		}
		//t.Logf("a[%d] is %s", i, ak)
		aNodes[i] = a
		aKeys[i] = a.Cid()
	}

	// Pin A5 recursively
	if err := p.Pin(ctx, aNodes[aBranchLen-1], true); err != nil {
		t.Fatal(err)
	}

	// Create node B and add A3 as child
	b, _ := randNode()
	if err := b.AddNodeLink("mychild", aNodes[3]); err != nil {
		t.Fatal(err)
	}

	// Create C node
	c, _ := randNode()
	// Add A0 as child of C
	if err := c.AddNodeLink("child", aNodes[0]); err != nil {
		t.Fatal(err)
	}

	// Add C
	err := dserv.Add(ctx, c)
	if err != nil {
		t.Fatal(err)
	}
	ck := c.Cid()
	//t.Logf("C is %s", ck)

	// Add C to B and Add B
	if err := b.AddNodeLink("myotherchild", c); err != nil {
		t.Fatal(err)
	}
	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}
	bk := b.Cid()
	//t.Logf("B is %s", bk)

	// Pin C recursively

	if err := p.Pin(ctx, c, true); err != nil {
		t.Fatal(err)
	}

	// Pin B recursively

	if err := p.Pin(ctx, b, true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, aKeys[0], "A0 should be pinned")
	assertPinned(t, p, aKeys[1], "A1 should be pinned")
	assertPinned(t, p, ck, "C should be pinned")
	assertPinned(t, p, bk, "B should be pinned")

	// Unpin A5 recursively
	if err := p.Unpin(ctx, aKeys[5], true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, aKeys[0], "A0 should still be pinned through B")
	assertUnpinned(t, p, aKeys[4], "A4 should be unpinned")

	// Unpin B recursively
	if err := p.Unpin(ctx, bk, true); err != nil {
		t.Fatal(err)
	}
	assertUnpinned(t, p, bk, "B should be unpinned")
	assertUnpinned(t, p, aKeys[1], "A1 should be unpinned")
	assertPinned(t, p, aKeys[0], "A0 should still be pinned through C")
}

func TestDuplicateSemantics(t *testing.T) {
	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	a, _ := randNode()
	err := dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// pin is recursively
	err = p.Pin(ctx, a, true)
	if err != nil {
		t.Fatal(err)
	}

	// pinning directly should fail
	err = p.Pin(ctx, a, false)
	if err == nil {
		t.Fatal("expected direct pin to fail")
	}

	// pinning recursively again should succeed
	err = p.Pin(ctx, a, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFlush(t *testing.T) {
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)
	p := NewPinner(dstore, dserv, dserv)
	_, k := randNode()

	p.PinWithMode(k, Recursive)
	if err := p.Flush(); err != nil {
		t.Fatal(err)
	}
	assertPinned(t, p, k, "expected key to still be pinned")
}

func TestPinRecursiveFail(t *testing.T) {
	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))
	dserv := mdag.NewDAGService(bserv)

	p := NewPinner(dstore, dserv, dserv)

	a, _ := randNode()
	b, _ := randNode()
	err := a.AddNodeLinkClean("child", b)
	if err != nil {
		t.Fatal(err)
	}

	// NOTE: This isnt a time based test, we expect the pin to fail
	mctx, cancel := context.WithTimeout(ctx, time.Millisecond)
	defer cancel()

	err = p.Pin(mctx, a, true)
	if err == nil {
		t.Fatal("should have failed to pin here")
	}

	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}

	err = dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// this one is time based... but shouldnt cause any issues
	mctx, cancel = context.WithTimeout(ctx, time.Second)
	defer cancel()
	err = p.Pin(mctx, a, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPinUpdate(t *testing.T) {
	ctx := context.Background()

	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)
	p := NewPinner(dstore, dserv, dserv)
	n1, c1 := randNode()
	n2, c2 := randNode()

	dserv.Add(ctx, n1)
	dserv.Add(ctx, n2)

	if err := p.Pin(ctx, n1, true); err != nil {
		t.Fatal(err)
	}

	if err := p.Update(ctx, c1, c2, true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, c2, "c2 should be pinned now")
	assertUnpinned(t, p, c1, "c1 should no longer be pinned")

	if err := p.Update(ctx, c2, c1, false); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, c2, "c2 should be pinned still")
	assertPinned(t, p, c1, "c1 should be pinned now")
}
<commit_msg>fix pin test on windows<commit_after>package pin

import (
	"context"
	"testing"
	"time"

	"github.com/ipfs/go-ipfs/blocks/blockstore"
	bs "github.com/ipfs/go-ipfs/blockservice"
	"github.com/ipfs/go-ipfs/exchange/offline"
	mdag "github.com/ipfs/go-ipfs/merkledag"

	"gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore"
	dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
)

var rand = util.NewTimeSeededRand()

func randNode() (*mdag.ProtoNode, *cid.Cid) {
	nd := new(mdag.ProtoNode)
	nd.SetData(make([]byte, 32))
	rand.Read(nd.Data())
	k := nd.Cid()
	return nd, k
}

func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) {
	_, pinned, err := p.IsPinned(c)
	if err != nil {
		t.Fatal(err)
	}

	if !pinned {
		t.Fatal(failmsg)
	}
}

func assertUnpinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) {
	_, pinned, err := p.IsPinned(c)
	if err != nil {
		t.Fatal(err)
	}

	if pinned {
		t.Fatal(failmsg)
	}
}

func TestPinnerBasic(t *testing.T) {
	ctx := context.Background()

	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	a, ak := randNode()
	err := dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// Pin A{}
	err = p.Pin(ctx, a, false)
	if err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, ak, "Failed to find key")

	// create new node c, to be indirectly pinned through b
	c, _ := randNode()
	err = dserv.Add(ctx, c)
	if err != nil {
		t.Fatal(err)
	}
	ck := c.Cid()

	// Create new node b, to be parent to a and c
	b, _ := randNode()
	err = b.AddNodeLink("child", a)
	if err != nil {
		t.Fatal(err)
	}

	err = b.AddNodeLink("otherchild", c)
	if err != nil {
		t.Fatal(err)
	}

	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}
	bk := b.Cid()

	// recursively pin B{A,C}
	err = p.Pin(ctx, b, true)
	if err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, ck, "child of recursively pinned node not found")

	assertPinned(t, p, bk, "Recursively pinned node not found..")

	d, _ := randNode()
	d.AddNodeLink("a", a)
	d.AddNodeLink("c", c)

	e, _ := randNode()
	d.AddNodeLink("e", e)

	// Must be in dagserv for unpin to work
	err = dserv.Add(ctx, e)
	if err != nil {
		t.Fatal(err)
	}
	err = dserv.Add(ctx, d)
	if err != nil {
		t.Fatal(err)
	}

	// Add D{A,C,E}
	err = p.Pin(ctx, d, true)
	if err != nil {
		t.Fatal(err)
	}

	dk := d.Cid()
	assertPinned(t, p, dk, "pinned node not found.")

	// Test recursive unpin
	err = p.Unpin(ctx, dk, true)
	if err != nil {
		t.Fatal(err)
	}

	err = p.Flush()
	if err != nil {
		t.Fatal(err)
	}

	np, err := LoadPinner(dstore, dserv, dserv)
	if err != nil {
		t.Fatal(err)
	}

	// Test directly pinned
	assertPinned(t, np, ak, "Could not find pinned node!")

	// Test recursively pinned
	assertPinned(t, np, bk, "could not find recursively pinned node")
}

func TestIsPinnedLookup(t *testing.T) {
	// We are going to test that lookups work in pins which share
	// the same branches. For that we will construct this tree:
	//
	// A5->A4->A3->A2->A1->A0
	//         /           /
	// B-------           /
	//  \                /
	//   C---------------
	//
	// We will ensure that IsPinned works for all objects both when they
	// are pinned and once they have been unpinned.
	aBranchLen := 6
	if aBranchLen < 3 {
		t.Fatal("set aBranchLen to at least 3")
	}

	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	aNodes := make([]*mdag.ProtoNode, aBranchLen)
	aKeys := make([]*cid.Cid, aBranchLen)
	for i := 0; i < aBranchLen; i++ {
		a, _ := randNode()
		if i >= 1 {
			err := a.AddNodeLink("child", aNodes[i-1])
			if err != nil {
				t.Fatal(err)
			}
		}

		err := dserv.Add(ctx, a)
		if err != nil {
			t.Fatal(err)
		}
		//t.Logf("a[%d] is %s", i, ak)
		aNodes[i] = a
		aKeys[i] = a.Cid()
	}

	// Pin A5 recursively
	if err := p.Pin(ctx, aNodes[aBranchLen-1], true); err != nil {
		t.Fatal(err)
	}

	// Create node B and add A3 as child
	b, _ := randNode()
	if err := b.AddNodeLink("mychild", aNodes[3]); err != nil {
		t.Fatal(err)
	}

	// Create C node
	c, _ := randNode()
	// Add A0 as child of C
	if err := c.AddNodeLink("child", aNodes[0]); err != nil {
		t.Fatal(err)
	}

	// Add C
	err := dserv.Add(ctx, c)
	if err != nil {
		t.Fatal(err)
	}
	ck := c.Cid()
	//t.Logf("C is %s", ck)

	// Add C to B and Add B
	if err := b.AddNodeLink("myotherchild", c); err != nil {
		t.Fatal(err)
	}
	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}
	bk := b.Cid()
	//t.Logf("B is %s", bk)

	// Pin C recursively

	if err := p.Pin(ctx, c, true); err != nil {
		t.Fatal(err)
	}

	// Pin B recursively

	if err := p.Pin(ctx, b, true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, aKeys[0], "A0 should be pinned")
	assertPinned(t, p, aKeys[1], "A1 should be pinned")
	assertPinned(t, p, ck, "C should be pinned")
	assertPinned(t, p, bk, "B should be pinned")

	// Unpin A5 recursively
	if err := p.Unpin(ctx, aKeys[5], true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, aKeys[0], "A0 should still be pinned through B")
	assertUnpinned(t, p, aKeys[4], "A4 should be unpinned")

	// Unpin B recursively
	if err := p.Unpin(ctx, bk, true); err != nil {
		t.Fatal(err)
	}
	assertUnpinned(t, p, bk, "B should be unpinned")
	assertUnpinned(t, p, aKeys[1], "A1 should be unpinned")
	assertPinned(t, p, aKeys[0], "A0 should still be pinned through C")
}

func TestDuplicateSemantics(t *testing.T) {
	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)

	// TODO does pinner need to share datastore with blockservice?
	p := NewPinner(dstore, dserv, dserv)

	a, _ := randNode()
	err := dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// pin is recursively
	err = p.Pin(ctx, a, true)
	if err != nil {
		t.Fatal(err)
	}

	// pinning directly should fail
	err = p.Pin(ctx, a, false)
	if err == nil {
		t.Fatal("expected direct pin to fail")
	}

	// pinning recursively again should succeed
	err = p.Pin(ctx, a, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFlush(t *testing.T) {
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)
	p := NewPinner(dstore, dserv, dserv)
	_, k := randNode()

	p.PinWithMode(k, Recursive)
	if err := p.Flush(); err != nil {
		t.Fatal(err)
	}
	assertPinned(t, p, k, "expected key to still be pinned")
}

func TestPinRecursiveFail(t *testing.T) {
	ctx := context.Background()
	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))
	dserv := mdag.NewDAGService(bserv)

	p := NewPinner(dstore, dserv, dserv)

	a, _ := randNode()
	b, _ := randNode()
	err := a.AddNodeLinkClean("child", b)
	if err != nil {
		t.Fatal(err)
	}

	// NOTE: This isnt a time based test, we expect the pin to fail
	mctx, cancel := context.WithTimeout(ctx, time.Millisecond)
	defer cancel()

	err = p.Pin(mctx, a, true)
	if err == nil {
		t.Fatal("should have failed to pin here")
	}

	err = dserv.Add(ctx, b)
	if err != nil {
		t.Fatal(err)
	}

	err = dserv.Add(ctx, a)
	if err != nil {
		t.Fatal(err)
	}

	// this one is time based... but shouldnt cause any issues
	mctx, cancel = context.WithTimeout(ctx, time.Second)
	defer cancel()
	err = p.Pin(mctx, a, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPinUpdate(t *testing.T) {
	ctx := context.Background()

	dstore := dssync.MutexWrap(ds.NewMapDatastore())
	bstore := blockstore.NewBlockstore(dstore)
	bserv := bs.New(bstore, offline.Exchange(bstore))

	dserv := mdag.NewDAGService(bserv)
	p := NewPinner(dstore, dserv, dserv)
	n1, c1 := randNode()
	n2, c2 := randNode()

	dserv.Add(ctx, n1)
	dserv.Add(ctx, n2)

	if err := p.Pin(ctx, n1, true); err != nil {
		t.Fatal(err)
	}

	if err := p.Update(ctx, c1, c2, true); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, c2, "c2 should be pinned now")
	assertUnpinned(t, p, c1, "c1 should no longer be pinned")

	if err := p.Update(ctx, c2, c1, false); err != nil {
		t.Fatal(err)
	}

	assertPinned(t, p, c2, "c2 should be pinned still")
	assertPinned(t, p, c1, "c1 should be pinned now")
}
<|endoftext|>


<commit_before>package distro

import (
	"github.com/drasko/edgex-export"
	MQTT "github.com/eclipse/paho.mqtt.golang"
	"go.uber.org/zap"
	"strconv"
)

type mqttSender struct {
	mqttClient MQTT.Client
	topic      string
}

const clientID = "edgex"
const topic = "EdgeX"

func NewMqttSender(addr export.Addressable) Sender {
	opts := MQTT.NewClientOptions()
	// CHN: Should be added protocol from Addressable instead of include it the address param.
	// CHN: We will maintain this behaviour for compatibility with Java
	broker := addr.Address + ":" + strconv.Itoa(addr.Port)
	opts.AddBroker(broker)
	opts.SetClientID(clientID)
	opts.SetUsername(addr.User)
	opts.SetPassword(addr.Password)

	var sender mqttSender

	sender.mqttClient = MQTT.NewClient(opts)
	sender.topic = addr.Topic

	if token := sender.mqttClient.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	logger.Info("Sample Publisher Started")

	return sender
}

func (sender mqttSender) Send(data []byte) {
	token := sender.mqttClient.Publish(sender.topic, 0, false, data)
	// FIXME: could be removed? set of tokens?
	token.Wait()
	logger.Info("Sent data: ", zap.String("data", data))
}
<commit_msg>Inlined declaration and mqttClient renamed<commit_after>package distro

import (
	"github.com/drasko/edgex-export"
	MQTT "github.com/eclipse/paho.mqtt.golang"
	"go.uber.org/zap"
	"strconv"
)

type mqttSender struct {
	client MQTT.Client
	topic  string
}

const clientID = "edgex"
const topic = "EdgeX"

func NewMqttSender(addr export.Addressable) Sender {
	opts := MQTT.NewClientOptions()
	// CHN: Should be added protocol from Addressable instead of include it the address param.
	// CHN: We will maintain this behaviour for compatibility with Java
	broker := addr.Address + ":" + strconv.Itoa(addr.Port)
	opts.AddBroker(broker)
	opts.SetClientID(clientID)
	opts.SetUsername(addr.User)
	opts.SetPassword(addr.Password)

	var sender mqttSender

	sender := mqttClient{
		client: MQTT.NewClient(opts),
		topic:  addr.Topic,
	}

	if token := sender.mqttClient.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	logger.Info("Sample Publisher Started")

	return sender
}

func (sender mqttSender) Send(data []byte) {
	token := sender.mqttClient.Publish(sender.topic, 0, false, data)
	// FIXME: could be removed? set of tokens?
	token.Wait()
	logger.Info("Sent data: ", zap.String("data", data))
}
<|endoftext|>


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

// +build !js

package opengl

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"

	mgl "golang.org/x/mobile/gl"
)

type Texture mgl.Texture
type Framebuffer mgl.Framebuffer
type Shader mgl.Shader
type Program mgl.Program
type Buffer mgl.Texture

var ZeroFramebuffer Framebuffer

// TODO: Remove this after the GopherJS bug was fixed (#159)
func (p Program) Equals(other Program) bool {
	return p == other
}

type UniformLocation mgl.Uniform
type AttribLocation mgl.Attrib

type ProgramID uint32

func GetProgramID(p Program) ProgramID {
	return ProgramID(p.Value)
}

type context struct {
	worker mgl.Worker
	funcs  chan func()
}

// TODO: This variable can be in the context struct.
var (
	gl mgl.Context
)

func NewContext() *Context {
	c := &Context{
		Nearest:            mgl.NEAREST,
		Linear:             mgl.LINEAR,
		VertexShader:       mgl.VERTEX_SHADER,
		FragmentShader:     mgl.FRAGMENT_SHADER,
		ArrayBuffer:        mgl.ARRAY_BUFFER,
		ElementArrayBuffer: mgl.ELEMENT_ARRAY_BUFFER,
		DynamicDraw:        mgl.DYNAMIC_DRAW,
		StaticDraw:         mgl.STATIC_DRAW,
		Triangles:          mgl.TRIANGLES,
		Lines:              mgl.LINES,
	}
	c.funcs = make(chan func())
	gl, c.worker = mgl.NewContext()
	return c
}

func (c *Context) Loop() {
	for {
		select {
		case <-c.worker.WorkAvailable():
			c.worker.DoWork()
		case f := <-c.funcs:
			f()
		}
	}
}

func (c *Context) Init() {
	// This initialization must be done after Loop is called.
	// This is why Init is separated from NewContext.

	// Textures' pixel formats are alpha premultiplied.
	gl.Enable(mgl.BLEND)
	gl.BlendFunc(mgl.ONE, mgl.ONE_MINUS_SRC_ALPHA)
}

func (c *Context) RunOnContextThread(f func()) {
	ch := make(chan struct{})
	c.funcs <- func() {
		f()
		close(ch)
	}
	<-ch
	return
}

func (c *Context) Check() {
	if e := gl.GetError(); e != mgl.NO_ERROR {
		panic(fmt.Sprintf("check failed: %d", e))
	}
}

func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
	t := gl.CreateTexture()
	if t.Value < 0 {
		return Texture{}, errors.New("opengl: glGenTexture failed")
	}
	gl.PixelStorei(mgl.UNPACK_ALIGNMENT, 4)
	gl.BindTexture(mgl.TEXTURE_2D, t)

	gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MAG_FILTER, int(filter))
	gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MIN_FILTER, int(filter))

	var p []uint8
	if pixels != nil {
		p = pixels
	}
	gl.TexImage2D(mgl.TEXTURE_2D, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)

	return Texture(t), nil
}

func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
	gl.Flush()

	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f))

	pixels := make([]uint8, 4*width*height)
	gl.ReadPixels(pixels, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE)
	if e := gl.GetError(); e != mgl.NO_ERROR {
		return nil, errors.New(fmt.Sprintf("opengl: glReadPixels: %d", e))
	}
	return pixels, nil
}

func (c *Context) BindTexture(t Texture) {
	gl.BindTexture(mgl.TEXTURE_2D, mgl.Texture(t))
}

func (c *Context) DeleteTexture(t Texture) {
	gl.DeleteTexture(mgl.Texture(t))
}

func (c *Context) TexSubImage2D(p []uint8, width, height int) {
	gl.TexSubImage2D(mgl.TEXTURE_2D, 0, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)
}

func (c *Context) BindZeroFramebuffer() {
	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(ZeroFramebuffer))
}

func (c *Context) NewFramebuffer(texture Texture) (Framebuffer, error) {
	f := gl.CreateFramebuffer()
	gl.BindFramebuffer(mgl.FRAMEBUFFER, f)

	gl.FramebufferTexture2D(mgl.FRAMEBUFFER, mgl.COLOR_ATTACHMENT0, mgl.TEXTURE_2D, mgl.Texture(texture), 0)
	s := gl.CheckFramebufferStatus(mgl.FRAMEBUFFER)
	if s != mgl.FRAMEBUFFER_COMPLETE {
		if s != 0 {
			return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %v", s))
		}
		if e := gl.GetError(); e != mgl.NO_ERROR {
			return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: (glGetError) %d", e))
		}
		return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: unknown error"))
	}

	return Framebuffer(f), nil
}

func (c *Context) SetViewport(f Framebuffer, width, height int) error {
	gl.Flush()
	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f))
	if err := gl.CheckFramebufferStatus(mgl.FRAMEBUFFER); err != mgl.FRAMEBUFFER_COMPLETE {
		if e := gl.GetError(); e != 0 {
			return errors.New(fmt.Sprintf("opengl: glBindFramebuffer failed: %d", e))
		}
		return errors.New("opengl: glBindFramebuffer failed: the context is different?")
	}
	gl.Viewport(0, 0, width, height)
	return nil
}

func (c *Context) FillFramebuffer(r, g, b, a float64) error {
	gl.ClearColor(float32(r), float32(g), float32(b), float32(a))
	gl.Clear(mgl.COLOR_BUFFER_BIT)
	return nil
}

func (c *Context) DeleteFramebuffer(f Framebuffer) {
	gl.DeleteFramebuffer(mgl.Framebuffer(f))
}

func (c *Context) NewShader(shaderType ShaderType, source string) (Shader, error) {
	s := gl.CreateShader(mgl.Enum(shaderType))
	if s.Value == 0 {
		return Shader{}, errors.New("opengl: glCreateShader failed")
	}
	gl.ShaderSource(s, source)
	gl.CompileShader(s)

	v := gl.GetShaderi(s, mgl.COMPILE_STATUS)
	if v == mgl.FALSE {
		log := gl.GetShaderInfoLog(s)
		return Shader{}, errors.New(fmt.Sprintf("opengl: shader compile failed: %s", log))
	}
	return Shader(s), nil
}

func (c *Context) DeleteShader(s Shader) {
	gl.DeleteShader(mgl.Shader(s))
}

func (c *Context) GlslHighpSupported() bool {
	return false
}

func (c *Context) NewProgram(shaders []Shader) (Program, error) {
	p := gl.CreateProgram()
	if p.Value == 0 {
		return Program{}, errors.New("opengl: glCreateProgram failed")
	}

	for _, shader := range shaders {
		gl.AttachShader(p, mgl.Shader(shader))
	}
	gl.LinkProgram(p)
	v := gl.GetProgrami(p, mgl.LINK_STATUS)
	if v == mgl.FALSE {
		return Program{}, errors.New("opengl: program error")
	}
	return Program(p), nil
}

func (c *Context) UseProgram(p Program) {
	gl.UseProgram(mgl.Program(p))
}

func (c *Context) GetUniformLocation(p Program, location string) UniformLocation {
	u := UniformLocation(gl.GetUniformLocation(mgl.Program(p), location))
	if u.Value == -1 {
		panic("invalid uniform location: " + location)
	}
	return u
}

func (c *Context) UniformInt(p Program, location string, v int) {
	gl.Uniform1i(gl.GetUniformLocation(mgl.Program(p), location), v)
}

func (c *Context) UniformFloats(p Program, location string, v []float32) {
	l := gl.GetUniformLocation(mgl.Program(p), location)
	switch len(v) {
	case 4:
		gl.Uniform4fv(l, v)
	case 16:
		gl.UniformMatrix4fv(l, v)
	default:
		panic("not reach")
	}
}

func (c *Context) GetAttribLocation(p Program, location string) AttribLocation {
	a := AttribLocation(gl.GetAttribLocation(mgl.Program(p), location))
	if a.Value == ^uint(0) {
		panic("invalid attrib location: " + location)
	}
	return a
}

func (c *Context) VertexAttribPointer(p Program, location string, normalize bool, stride int, size int, v int) {
	l := c.GetAttribLocation(p, location)
	gl.VertexAttribPointer(mgl.Attrib(l), size, mgl.SHORT, normalize, stride, v)
}

func (c *Context) EnableVertexAttribArray(p Program, location string) {
	l := c.GetAttribLocation(p, location)
	gl.EnableVertexAttribArray(mgl.Attrib(l))
}

func (c *Context) DisableVertexAttribArray(p Program, location string) {
	l := c.GetAttribLocation(p, location)
	gl.DisableVertexAttribArray(mgl.Attrib(l))
}

func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
	b := gl.CreateBuffer()
	gl.BindBuffer(mgl.Enum(bufferType), b)
	//var bb []byte
	switch v := v.(type) {
	case int:
		gl.BufferInit(mgl.Enum(bufferType), v, mgl.Enum(bufferUsage))
		return Buffer(b)
	case []uint16:
	case []float32:
	default:
		panic("not reach")
	}
	bt := &bytes.Buffer{}
	if err := binary.Write(bt, binary.LittleEndian, v); err != nil {
		panic(fmt.Sprintf("opengl: Binary error: %v", err))
	}
	gl.BufferData(mgl.Enum(bufferType), bt.Bytes(), mgl.Enum(bufferUsage))
	return Buffer(b)
}

func (c *Context) BindElementArrayBuffer(b Buffer) {
	gl.BindBuffer(mgl.ELEMENT_ARRAY_BUFFER, mgl.Buffer(b))
}

func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
	bt := &bytes.Buffer{}
	if err := binary.Write(bt, binary.LittleEndian, data); err != nil {
		panic(fmt.Sprintf("opengl: Binary error: %v", err))
	}
	gl.BufferSubData(mgl.Enum(bufferType), 0, bt.Bytes())
}

func (c *Context) DrawElements(mode Mode, len int) {
	gl.DrawElements(mgl.Enum(mode), len, mgl.UNSIGNED_SHORT, 0)
}
<commit_msg>opengl: Avoid using encoding/binary for speed<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package opengl

import (
	"errors"
	"fmt"

	mgl "golang.org/x/mobile/gl"
)

type Texture mgl.Texture
type Framebuffer mgl.Framebuffer
type Shader mgl.Shader
type Program mgl.Program
type Buffer mgl.Texture

var ZeroFramebuffer Framebuffer

// TODO: Remove this after the GopherJS bug was fixed (#159)
func (p Program) Equals(other Program) bool {
	return p == other
}

type UniformLocation mgl.Uniform
type AttribLocation mgl.Attrib

type ProgramID uint32

func GetProgramID(p Program) ProgramID {
	return ProgramID(p.Value)
}

type context struct {
	worker mgl.Worker
	funcs  chan func()
}

// TODO: This variable can be in the context struct.
var (
	gl mgl.Context
)

func NewContext() *Context {
	c := &Context{
		Nearest:            mgl.NEAREST,
		Linear:             mgl.LINEAR,
		VertexShader:       mgl.VERTEX_SHADER,
		FragmentShader:     mgl.FRAGMENT_SHADER,
		ArrayBuffer:        mgl.ARRAY_BUFFER,
		ElementArrayBuffer: mgl.ELEMENT_ARRAY_BUFFER,
		DynamicDraw:        mgl.DYNAMIC_DRAW,
		StaticDraw:         mgl.STATIC_DRAW,
		Triangles:          mgl.TRIANGLES,
		Lines:              mgl.LINES,
	}
	c.funcs = make(chan func())
	gl, c.worker = mgl.NewContext()
	return c
}

func (c *Context) Loop() {
	for {
		select {
		case <-c.worker.WorkAvailable():
			c.worker.DoWork()
		case f := <-c.funcs:
			f()
		}
	}
}

func (c *Context) Init() {
	// This initialization must be done after Loop is called.
	// This is why Init is separated from NewContext.

	// Textures' pixel formats are alpha premultiplied.
	gl.Enable(mgl.BLEND)
	gl.BlendFunc(mgl.ONE, mgl.ONE_MINUS_SRC_ALPHA)
}

func (c *Context) RunOnContextThread(f func()) {
	ch := make(chan struct{})
	c.funcs <- func() {
		f()
		close(ch)
	}
	<-ch
	return
}

func (c *Context) Check() {
	if e := gl.GetError(); e != mgl.NO_ERROR {
		panic(fmt.Sprintf("check failed: %d", e))
	}
}

func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
	t := gl.CreateTexture()
	if t.Value < 0 {
		return Texture{}, errors.New("opengl: glGenTexture failed")
	}
	gl.PixelStorei(mgl.UNPACK_ALIGNMENT, 4)
	gl.BindTexture(mgl.TEXTURE_2D, t)

	gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MAG_FILTER, int(filter))
	gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MIN_FILTER, int(filter))

	var p []uint8
	if pixels != nil {
		p = pixels
	}
	gl.TexImage2D(mgl.TEXTURE_2D, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)

	return Texture(t), nil
}

func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
	gl.Flush()

	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f))

	pixels := make([]uint8, 4*width*height)
	gl.ReadPixels(pixels, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE)
	if e := gl.GetError(); e != mgl.NO_ERROR {
		return nil, errors.New(fmt.Sprintf("opengl: glReadPixels: %d", e))
	}
	return pixels, nil
}

func (c *Context) BindTexture(t Texture) {
	gl.BindTexture(mgl.TEXTURE_2D, mgl.Texture(t))
}

func (c *Context) DeleteTexture(t Texture) {
	gl.DeleteTexture(mgl.Texture(t))
}

func (c *Context) TexSubImage2D(p []uint8, width, height int) {
	gl.TexSubImage2D(mgl.TEXTURE_2D, 0, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)
}

func (c *Context) BindZeroFramebuffer() {
	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(ZeroFramebuffer))
}

func (c *Context) NewFramebuffer(texture Texture) (Framebuffer, error) {
	f := gl.CreateFramebuffer()
	gl.BindFramebuffer(mgl.FRAMEBUFFER, f)

	gl.FramebufferTexture2D(mgl.FRAMEBUFFER, mgl.COLOR_ATTACHMENT0, mgl.TEXTURE_2D, mgl.Texture(texture), 0)
	s := gl.CheckFramebufferStatus(mgl.FRAMEBUFFER)
	if s != mgl.FRAMEBUFFER_COMPLETE {
		if s != 0 {
			return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %v", s))
		}
		if e := gl.GetError(); e != mgl.NO_ERROR {
			return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: (glGetError) %d", e))
		}
		return Framebuffer{}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: unknown error"))
	}

	return Framebuffer(f), nil
}

func (c *Context) SetViewport(f Framebuffer, width, height int) error {
	gl.Flush()
	gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f))
	if err := gl.CheckFramebufferStatus(mgl.FRAMEBUFFER); err != mgl.FRAMEBUFFER_COMPLETE {
		if e := gl.GetError(); e != 0 {
			return errors.New(fmt.Sprintf("opengl: glBindFramebuffer failed: %d", e))
		}
		return errors.New("opengl: glBindFramebuffer failed: the context is different?")
	}
	gl.Viewport(0, 0, width, height)
	return nil
}

func (c *Context) FillFramebuffer(r, g, b, a float64) error {
	gl.ClearColor(float32(r), float32(g), float32(b), float32(a))
	gl.Clear(mgl.COLOR_BUFFER_BIT)
	return nil
}

func (c *Context) DeleteFramebuffer(f Framebuffer) {
	gl.DeleteFramebuffer(mgl.Framebuffer(f))
}

func (c *Context) NewShader(shaderType ShaderType, source string) (Shader, error) {
	s := gl.CreateShader(mgl.Enum(shaderType))
	if s.Value == 0 {
		return Shader{}, errors.New("opengl: glCreateShader failed")
	}
	gl.ShaderSource(s, source)
	gl.CompileShader(s)

	v := gl.GetShaderi(s, mgl.COMPILE_STATUS)
	if v == mgl.FALSE {
		log := gl.GetShaderInfoLog(s)
		return Shader{}, errors.New(fmt.Sprintf("opengl: shader compile failed: %s", log))
	}
	return Shader(s), nil
}

func (c *Context) DeleteShader(s Shader) {
	gl.DeleteShader(mgl.Shader(s))
}

func (c *Context) GlslHighpSupported() bool {
	return false
}

func (c *Context) NewProgram(shaders []Shader) (Program, error) {
	p := gl.CreateProgram()
	if p.Value == 0 {
		return Program{}, errors.New("opengl: glCreateProgram failed")
	}

	for _, shader := range shaders {
		gl.AttachShader(p, mgl.Shader(shader))
	}
	gl.LinkProgram(p)
	v := gl.GetProgrami(p, mgl.LINK_STATUS)
	if v == mgl.FALSE {
		return Program{}, errors.New("opengl: program error")
	}
	return Program(p), nil
}

func (c *Context) UseProgram(p Program) {
	gl.UseProgram(mgl.Program(p))
}

func (c *Context) GetUniformLocation(p Program, location string) UniformLocation {
	u := UniformLocation(gl.GetUniformLocation(mgl.Program(p), location))
	if u.Value == -1 {
		panic("invalid uniform location: " + location)
	}
	return u
}

func (c *Context) UniformInt(p Program, location string, v int) {
	gl.Uniform1i(gl.GetUniformLocation(mgl.Program(p), location), v)
}

func (c *Context) UniformFloats(p Program, location string, v []float32) {
	l := gl.GetUniformLocation(mgl.Program(p), location)
	switch len(v) {
	case 4:
		gl.Uniform4fv(l, v)
	case 16:
		gl.UniformMatrix4fv(l, v)
	default:
		panic("not reach")
	}
}

func (c *Context) GetAttribLocation(p Program, location string) AttribLocation {
	a := AttribLocation(gl.GetAttribLocation(mgl.Program(p), location))
	if a.Value == ^uint(0) {
		panic("invalid attrib location: " + location)
	}
	return a
}

func (c *Context) VertexAttribPointer(p Program, location string, normalize bool, stride int, size int, v int) {
	l := c.GetAttribLocation(p, location)
	gl.VertexAttribPointer(mgl.Attrib(l), size, mgl.SHORT, normalize, stride, v)
}

func (c *Context) EnableVertexAttribArray(p Program, location string) {
	l := c.GetAttribLocation(p, location)
	gl.EnableVertexAttribArray(mgl.Attrib(l))
}

func (c *Context) DisableVertexAttribArray(p Program, location string) {
	l := c.GetAttribLocation(p, location)
	gl.DisableVertexAttribArray(mgl.Attrib(l))
}

func uint16ToBytes(v []uint16) []byte {
	b := make([]byte, len(v) * 2)
	for i, x := range v {
		b[2*i] = byte(x)
		b[2*i+1] = byte(x >> 8)
	}
	return b
}

func int16ToBytes(v []int16) []byte {
	b := make([]byte, len(v) * 2)
	for i, x := range v {
		b[2*i] = byte(uint16(x))
		b[2*i+1] = byte(uint16(x) >> 8)
	}
	return b
}

func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
	b := gl.CreateBuffer()
	gl.BindBuffer(mgl.Enum(bufferType), b)
	switch v := v.(type) {
	case int:
		gl.BufferInit(mgl.Enum(bufferType), v, mgl.Enum(bufferUsage))
		return Buffer(b)
	case []uint16:
		gl.BufferData(mgl.Enum(bufferType), uint16ToBytes(v), mgl.Enum(bufferUsage))
	default:
		panic("not reach")
	}
	return Buffer(b)
}

func (c *Context) BindElementArrayBuffer(b Buffer) {
	gl.BindBuffer(mgl.ELEMENT_ARRAY_BUFFER, mgl.Buffer(b))
}

func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
	gl.BufferSubData(mgl.Enum(bufferType), 0, int16ToBytes(data))
}

func (c *Context) DrawElements(mode Mode, len int) {
	gl.DrawElements(mgl.Enum(mode), len, mgl.UNSIGNED_SHORT, 0)
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/base64"
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/authelia/authelia/internal/authentication"
	"github.com/authelia/authelia/internal/authorization"
	"github.com/authelia/authelia/internal/middlewares"
	"github.com/valyala/fasthttp"
)

func isURLUnderProtectedDomain(url *url.URL, domain string) bool {
	return strings.HasSuffix(url.Hostname(), domain)
}

func isSchemeHTTPS(url *url.URL) bool {
	return url.Scheme == "https"
}

func isSchemeWSS(url *url.URL) bool {
	return url.Scheme == "wss"
}

// getOriginalURL extract the URL from the request headers (X-Original-URI or X-Forwarded-* headers).
func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
	originalURL := ctx.XOriginalURL()
	if originalURL != nil {
		url, err := url.ParseRequestURI(string(originalURL))
		if err != nil {
			return nil, fmt.Errorf("Unable to parse URL extracted from X-Original-URL header: %v", err)
		}
		return url, nil
	} else {
		ctx.Logger.Debug("No X-Original-URL header detected, fallback to combination of " +
			"X-Fowarded-Proto, X-Forwarded-Host and X-Forwarded-URI headers")
	}

	forwardedProto := ctx.XForwardedProto()
	forwardedHost := ctx.XForwardedHost()
	forwardedURI := ctx.XForwardedURI()

	if forwardedProto == nil {
		return nil, errMissingXForwardedProto
	}

	if forwardedHost == nil {
		return nil, errMissingXForwardedHost
	}

	var requestURI string
	scheme := append(forwardedProto, protoHostSeparator...)
	requestURI = string(append(scheme,
		append(forwardedHost, forwardedURI...)...))

	url, err := url.ParseRequestURI(requestURI)
	if err != nil {
		return nil, fmt.Errorf("Unable to parse URL %s: %v", requestURI, err)
	}
	return url, nil
}

// parseBasicAuth parses an HTTP Basic Authentication string.
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
func parseBasicAuth(auth string) (username, password string, err error) {
	if !strings.HasPrefix(auth, authPrefix) {
		return "", "", fmt.Errorf("%s prefix not found in %s header", strings.Trim(authPrefix, " "), AuthorizationHeader)
	}
	c, err := base64.StdEncoding.DecodeString(auth[len(authPrefix):])
	if err != nil {
		return "", "", err
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return "", "", fmt.Errorf("Format of %s header must be user:password", AuthorizationHeader)
	}
	return cs[:s], cs[s+1:], nil
}

// isTargetURLAuthorized check whether the given user is authorized to access the resource.
func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.URL,
	username string, userGroups []string, clientIP net.IP, authLevel authentication.Level) authorizationMatching {

	level := authorizer.GetRequiredLevel(authorization.Subject{
		Username: username,
		Groups:   userGroups,
		IP:       clientIP,
	}, targetURL)

	if level == authorization.Bypass {
		return Authorized
	} else if username != "" && level == authorization.Denied {
		// If the user is not anonymous, it means that we went through
		// all the rules related to that user and knowing who he is we can
		// deduce the access is forbidden.
		// For anonymous users though, we cannot be sure that she
		// could not be granted the rights to access the resource. Consequently
		// for anonymous users we send Unauthorized instead of Forbidden.
		return Forbidden
	} else {
		if level == authorization.OneFactor &&
			authLevel >= authentication.OneFactor {
			return Authorized
		} else if level == authorization.TwoFactor &&
			authLevel >= authentication.TwoFactor {
			return Authorized
		}
	}
	return NotAuthorized
}

// verifyBasicAuth verify that the provided username and password are correct and
// that the user is authorized to target the resource.
func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) {
	username, password, err := parseBasicAuth(string(auth))

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to parse content of %s header: %s", AuthorizationHeader, err)
	}

	authenticated, err := ctx.Providers.UserProvider.CheckUserPassword(username, password)

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check credentials extracted from %s header: %s", AuthorizationHeader, err)
	}

	// If the user is not correctly authenticated, send a 401.
	if !authenticated {
		// Request Basic Authentication otherwise
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s is not authenticated", username)
	}

	details, err := ctx.Providers.UserProvider.GetDetails(username)

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to retrieve details of user %s: %s", username, err)
	}

	return username, details.Groups, authentication.OneFactor, nil
}

// setForwardedHeaders set the forwarded User and Groups headers.
func setForwardedHeaders(headers *fasthttp.ResponseHeader, username string, groups []string) {
	if username != "" {
		headers.Set(remoteUserHeader, username)
		headers.Set(remoteGroupsHeader, strings.Join(groups, ","))
	}
}

// hasUserBeenInactiveLongEnough check whether the user has been inactive for too long.
func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) {
	maxInactivityPeriod := ctx.Configuration.Session.Inactivity
	if maxInactivityPeriod == 0 {
		return false, nil
	}

	lastActivity := ctx.GetSession().LastActivity
	inactivityPeriod := ctx.Clock.Now().Unix() - lastActivity

	ctx.Logger.Tracef("Inactivity report: Inactivity=%d, MaxInactivity=%d",
		inactivityPeriod, maxInactivityPeriod)

	if inactivityPeriod > maxInactivityPeriod {
		return true, nil
	}

	return false, nil
}

// verifyFromSessionCookie verify if a user identified by a cookie is allowed to access target URL.
func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) {
	userSession := ctx.GetSession()
	// No username in the session means the user is anonymous.
	isUserAnonymous := userSession.Username == ""

	if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("An anonymous user cannot be authenticated. That might be the sign of a compromise")
	}

	if !userSession.KeepMeLoggedIn && !isUserAnonymous {
		inactiveLongEnough, err := hasUserBeenInactiveLongEnough(ctx)
		if err != nil {
			return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check if user has been inactive for a long time: %s", err)
		}

		if inactiveLongEnough {
			// Destroy the session a new one will be regenerated on next request.
			err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
			if err != nil {
				return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to destroy user session after long inactivity: %s", err)
			}

			return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s has been inactive for too long", userSession.Username)
		}
	}
	return userSession.Username, userSession.Groups, userSession.AuthenticationLevel, nil
}

// VerifyGet is the handler verifying if a request is allowed to go through.
func VerifyGet(ctx *middlewares.AutheliaCtx) {
	ctx.Logger.Tracef("Headers=%s", ctx.Request.Header.String())
	targetURL, err := getOriginalURL(ctx)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to parse target URL: %s", err), operationFailedMessage)
		return
	}

	if !isSchemeHTTPS(targetURL) && !isSchemeWSS(targetURL) {
		ctx.Logger.Error(fmt.Errorf("Scheme of target URL %s must be secure since cookies are "+
			"only transported over a secure connection for security reasons", targetURL.String()))
		ctx.ReplyUnauthorized()
		return
	}

	if !isURLUnderProtectedDomain(targetURL, ctx.Configuration.Session.Domain) {
		ctx.Logger.Error(fmt.Errorf("The target URL %s is not under the protected domain %s",
			targetURL.String(), ctx.Configuration.Session.Domain))
		ctx.ReplyUnauthorized()
		return
	}

	var username string
	var groups []string
	var authLevel authentication.Level

	proxyAuthorization := ctx.Request.Header.Peek(AuthorizationHeader)
	hasBasicAuth := proxyAuthorization != nil

	if hasBasicAuth {
		username, groups, authLevel, err = verifyBasicAuth(proxyAuthorization, *targetURL, ctx)
	} else {
		username, groups, authLevel, err = verifyFromSessionCookie(*targetURL, ctx)
	}

	if err != nil {
		ctx.Logger.Error(fmt.Sprintf("Error caught when verifying user authorization: %s", err))
		ctx.ReplyUnauthorized()
		return
	}

	authorization := isTargetURLAuthorized(ctx.Providers.Authorizer, *targetURL, username,
		groups, ctx.RemoteIP(), authLevel)

	if authorization == Forbidden {
		ctx.ReplyForbidden()
		ctx.Logger.Errorf("Access to %s is forbidden to user %s", targetURL.String(), username)
		return
	} else if authorization == NotAuthorized {
		// Kubernetes ingress controller and Traefik use the rd parameter of the verify
		// endpoint to provide the URL of the login portal. The target URL of the user
		// is computed from X-Fowarded-* headers or X-Original-URL.
		rd := string(ctx.QueryArgs().Peek("rd"))
		if rd != "" {
			redirectionURL := fmt.Sprintf("%s?rd=%s", rd, url.QueryEscape(targetURL.String()))
			if strings.Contains(redirectionURL, "/%23/") {
				ctx.Logger.Warn("Characters /%23/ have been detected in redirection URL. This is not needed anymore, please strip it")
			}
			ctx.Redirect(redirectionURL, 302)
			ctx.SetBodyString(fmt.Sprintf("Found. Redirecting to %s", redirectionURL))
		} else {
			ctx.ReplyUnauthorized()
			ctx.Logger.Errorf("Access to %s is not authorized to user %s", targetURL.String(), username)
		}
	} else if authorization == Authorized {
		setForwardedHeaders(&ctx.Response.Header, username, groups)
	}

	// We mark activity of the current user if he comes with a session cookie.
	if !hasBasicAuth && username != "" {
		// Mark current activity
		userSession := ctx.GetSession()
		userSession.LastActivity = time.Now().Unix()
		err = ctx.SaveSession(userSession)

		if err != nil {
			ctx.Error(fmt.Errorf("Unable to update last activity: %s", err), operationFailedMessage)
			return
		}
	}
}
<commit_msg>[MISC] Debug message shows used headers instead of fallback warning. (#772)<commit_after>package handlers

import (
	"encoding/base64"
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/authelia/authelia/internal/authentication"
	"github.com/authelia/authelia/internal/authorization"
	"github.com/authelia/authelia/internal/middlewares"
	"github.com/valyala/fasthttp"
)

func isURLUnderProtectedDomain(url *url.URL, domain string) bool {
	return strings.HasSuffix(url.Hostname(), domain)
}

func isSchemeHTTPS(url *url.URL) bool {
	return url.Scheme == "https"
}

func isSchemeWSS(url *url.URL) bool {
	return url.Scheme == "wss"
}

// getOriginalURL extract the URL from the request headers (X-Original-URI or X-Forwarded-* headers).
func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
	originalURL := ctx.XOriginalURL()
	if originalURL != nil {
		url, err := url.ParseRequestURI(string(originalURL))
		if err != nil {
			return nil, fmt.Errorf("Unable to parse URL extracted from X-Original-URL header: %v", err)
		}
		ctx.Logger.Debug("Using X-Original-URL header content as targeted site URL")
		return url, nil
	}

	forwardedProto := ctx.XForwardedProto()
	forwardedHost := ctx.XForwardedHost()
	forwardedURI := ctx.XForwardedURI()

	if forwardedProto == nil {
		return nil, errMissingXForwardedProto
	}

	if forwardedHost == nil {
		return nil, errMissingXForwardedHost
	}

	var requestURI string
	scheme := append(forwardedProto, protoHostSeparator...)
	requestURI = string(append(scheme,
		append(forwardedHost, forwardedURI...)...))

	url, err := url.ParseRequestURI(requestURI)
	if err != nil {
		return nil, fmt.Errorf("Unable to parse URL %s: %v", requestURI, err)
	}
	ctx.Logger.Debugf("Using X-Fowarded-Proto, X-Forwarded-Host and X-Forwarded-URI headers " +
		"to construct targeted site URL")
	return url, nil
}

// parseBasicAuth parses an HTTP Basic Authentication string.
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
func parseBasicAuth(auth string) (username, password string, err error) {
	if !strings.HasPrefix(auth, authPrefix) {
		return "", "", fmt.Errorf("%s prefix not found in %s header", strings.Trim(authPrefix, " "), AuthorizationHeader)
	}
	c, err := base64.StdEncoding.DecodeString(auth[len(authPrefix):])
	if err != nil {
		return "", "", err
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return "", "", fmt.Errorf("Format of %s header must be user:password", AuthorizationHeader)
	}
	return cs[:s], cs[s+1:], nil
}

// isTargetURLAuthorized check whether the given user is authorized to access the resource.
func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.URL,
	username string, userGroups []string, clientIP net.IP, authLevel authentication.Level) authorizationMatching {

	level := authorizer.GetRequiredLevel(authorization.Subject{
		Username: username,
		Groups:   userGroups,
		IP:       clientIP,
	}, targetURL)

	if level == authorization.Bypass {
		return Authorized
	} else if username != "" && level == authorization.Denied {
		// If the user is not anonymous, it means that we went through
		// all the rules related to that user and knowing who he is we can
		// deduce the access is forbidden.
		// For anonymous users though, we cannot be sure that she
		// could not be granted the rights to access the resource. Consequently
		// for anonymous users we send Unauthorized instead of Forbidden.
		return Forbidden
	} else {
		if level == authorization.OneFactor &&
			authLevel >= authentication.OneFactor {
			return Authorized
		} else if level == authorization.TwoFactor &&
			authLevel >= authentication.TwoFactor {
			return Authorized
		}
	}
	return NotAuthorized
}

// verifyBasicAuth verify that the provided username and password are correct and
// that the user is authorized to target the resource.
func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) {
	username, password, err := parseBasicAuth(string(auth))

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to parse content of %s header: %s", AuthorizationHeader, err)
	}

	authenticated, err := ctx.Providers.UserProvider.CheckUserPassword(username, password)

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check credentials extracted from %s header: %s", AuthorizationHeader, err)
	}

	// If the user is not correctly authenticated, send a 401.
	if !authenticated {
		// Request Basic Authentication otherwise
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s is not authenticated", username)
	}

	details, err := ctx.Providers.UserProvider.GetDetails(username)

	if err != nil {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to retrieve details of user %s: %s", username, err)
	}

	return username, details.Groups, authentication.OneFactor, nil
}

// setForwardedHeaders set the forwarded User and Groups headers.
func setForwardedHeaders(headers *fasthttp.ResponseHeader, username string, groups []string) {
	if username != "" {
		headers.Set(remoteUserHeader, username)
		headers.Set(remoteGroupsHeader, strings.Join(groups, ","))
	}
}

// hasUserBeenInactiveLongEnough check whether the user has been inactive for too long.
func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) {
	maxInactivityPeriod := ctx.Configuration.Session.Inactivity
	if maxInactivityPeriod == 0 {
		return false, nil
	}

	lastActivity := ctx.GetSession().LastActivity
	inactivityPeriod := ctx.Clock.Now().Unix() - lastActivity

	ctx.Logger.Tracef("Inactivity report: Inactivity=%d, MaxInactivity=%d",
		inactivityPeriod, maxInactivityPeriod)

	if inactivityPeriod > maxInactivityPeriod {
		return true, nil
	}

	return false, nil
}

// verifyFromSessionCookie verify if a user identified by a cookie is allowed to access target URL.
func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) {
	userSession := ctx.GetSession()
	// No username in the session means the user is anonymous.
	isUserAnonymous := userSession.Username == ""

	if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
		return "", nil, authentication.NotAuthenticated, fmt.Errorf("An anonymous user cannot be authenticated. That might be the sign of a compromise")
	}

	if !userSession.KeepMeLoggedIn && !isUserAnonymous {
		inactiveLongEnough, err := hasUserBeenInactiveLongEnough(ctx)
		if err != nil {
			return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check if user has been inactive for a long time: %s", err)
		}

		if inactiveLongEnough {
			// Destroy the session a new one will be regenerated on next request.
			err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
			if err != nil {
				return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to destroy user session after long inactivity: %s", err)
			}

			return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s has been inactive for too long", userSession.Username)
		}
	}
	return userSession.Username, userSession.Groups, userSession.AuthenticationLevel, nil
}

// VerifyGet is the handler verifying if a request is allowed to go through.
func VerifyGet(ctx *middlewares.AutheliaCtx) {
	ctx.Logger.Tracef("Headers=%s", ctx.Request.Header.String())
	targetURL, err := getOriginalURL(ctx)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to parse target URL: %s", err), operationFailedMessage)
		return
	}

	if !isSchemeHTTPS(targetURL) && !isSchemeWSS(targetURL) {
		ctx.Logger.Error(fmt.Errorf("Scheme of target URL %s must be secure since cookies are "+
			"only transported over a secure connection for security reasons", targetURL.String()))
		ctx.ReplyUnauthorized()
		return
	}

	if !isURLUnderProtectedDomain(targetURL, ctx.Configuration.Session.Domain) {
		ctx.Logger.Error(fmt.Errorf("The target URL %s is not under the protected domain %s",
			targetURL.String(), ctx.Configuration.Session.Domain))
		ctx.ReplyUnauthorized()
		return
	}

	var username string
	var groups []string
	var authLevel authentication.Level

	proxyAuthorization := ctx.Request.Header.Peek(AuthorizationHeader)
	hasBasicAuth := proxyAuthorization != nil

	if hasBasicAuth {
		username, groups, authLevel, err = verifyBasicAuth(proxyAuthorization, *targetURL, ctx)
	} else {
		username, groups, authLevel, err = verifyFromSessionCookie(*targetURL, ctx)
	}

	if err != nil {
		ctx.Logger.Error(fmt.Sprintf("Error caught when verifying user authorization: %s", err))
		ctx.ReplyUnauthorized()
		return
	}

	authorization := isTargetURLAuthorized(ctx.Providers.Authorizer, *targetURL, username,
		groups, ctx.RemoteIP(), authLevel)

	if authorization == Forbidden {
		ctx.ReplyForbidden()
		ctx.Logger.Errorf("Access to %s is forbidden to user %s", targetURL.String(), username)
		return
	} else if authorization == NotAuthorized {
		// Kubernetes ingress controller and Traefik use the rd parameter of the verify
		// endpoint to provide the URL of the login portal. The target URL of the user
		// is computed from X-Fowarded-* headers or X-Original-URL.
		rd := string(ctx.QueryArgs().Peek("rd"))
		if rd != "" {
			redirectionURL := fmt.Sprintf("%s?rd=%s", rd, url.QueryEscape(targetURL.String()))
			if strings.Contains(redirectionURL, "/%23/") {
				ctx.Logger.Warn("Characters /%23/ have been detected in redirection URL. This is not needed anymore, please strip it")
			}
			ctx.Redirect(redirectionURL, 302)
			ctx.SetBodyString(fmt.Sprintf("Found. Redirecting to %s", redirectionURL))
		} else {
			ctx.ReplyUnauthorized()
			ctx.Logger.Errorf("Access to %s is not authorized to user %s", targetURL.String(), username)
		}
	} else if authorization == Authorized {
		setForwardedHeaders(&ctx.Response.Header, username, groups)
	}

	// We mark activity of the current user if he comes with a session cookie.
	if !hasBasicAuth && username != "" {
		// Mark current activity
		userSession := ctx.GetSession()
		userSession.LastActivity = time.Now().Unix()
		err = ctx.SaveSession(userSession)

		if err != nil {
			ctx.Error(fmt.Errorf("Unable to update last activity: %s", err), operationFailedMessage)
			return
		}
	}
}
<|endoftext|>


<commit_before>package peermanager

import (
	"net"
	"sync"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/mse"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/peerlist"
	"github.com/cenkalti/rain/internal/torrentdata"
)

// Do not connect more than maxPeerPerTorrent peers.
const maxPeerPerTorrent = 60

type PeerManager struct {
	peers            map[[20]byte]*peer.Peer // connected peers
	peerDisconnected chan *peer.Peer
	port             int
	listener         *net.TCPListener
	peerList         *peerlist.PeerList
	peerID           [20]byte
	infoHash         [20]byte
	sKeyHash         [20]byte
	data             *torrentdata.Data
	limiter          chan struct{}
	peerMessages     *peer.Messages
	log              logger.Logger
	m                sync.Mutex
	wg               sync.WaitGroup
}

func New(port int, pl *peerlist.PeerList, peerID, infoHash [20]byte, d *torrentdata.Data, l logger.Logger) *PeerManager {
	return &PeerManager{
		peers:            make(map[[20]byte]*peer.Peer),
		peerDisconnected: make(chan *peer.Peer),
		port:             port,
		peerList:         pl,
		peerID:           peerID,
		infoHash:         infoHash,
		sKeyHash:         mse.HashSKey(infoHash[:]),
		data:             d,
		limiter:          make(chan struct{}, maxPeerPerTorrent),
		peerMessages:     peer.NewMessages(),
		log:              l,
	}
}

func (m *PeerManager) PeerMessages() *peer.Messages {
	return m.peerMessages
}

func (m *PeerManager) close() {
	m.m.Lock()
	defer m.m.Unlock()
	if m.listener != nil {
		err := m.listener.Close()
		if err != nil {
			m.log.Errorln("cannot close listener:", err)
		}
	}
	for _, p := range m.peers {
		err := p.Close()
		if err != nil {
			m.log.Errorln("cannot close peer:", err)
		}
	}
}

func (m *PeerManager) Run(stopC chan struct{}) {
	m.startAcceptor(stopC)
	m.startDialer(stopC)
	for {
		select {
		case p := <-m.peerDisconnected:
			m.handleDisconnect(p)
		case <-stopC:
			m.close()
			m.wg.Done()
			return
		}
	}
}

func (m *PeerManager) startAcceptor(stopC chan struct{}) {
	m.m.Lock()
	defer m.m.Unlock()

	var err error
	m.listener, err = net.ListenTCP("tcp4", &net.TCPAddr{Port: m.port})
	if err != nil {
		m.log.Errorf("cannot listen port %d: %s", m.port, err)
		return
	}

	m.log.Notice("Listening peers on tcp://" + m.listener.Addr().String())
	m.wg.Add(1)
	go func() {
		defer m.wg.Done()
		m.acceptor(stopC)
	}()
}

func (m *PeerManager) startDialer(stopC chan struct{}) {
	m.wg.Add(1)
	go func() {
		defer m.wg.Done()
		m.dialer(stopC)
	}()
}

func (m *PeerManager) handleConnect(p *peer.Peer, stopC chan struct{}) {
	m.m.Lock()
	defer m.m.Unlock()
	if _, ok := m.peers[p.ID()]; ok {
		m.log.Warningln("peer already connected, dropping connection:", p.String())
		go m.closePeer(p)
		return
	}
	go m.waitDisconnect(p, stopC)
	m.peers[p.ID()] = p
	p.Run(stopC)
}

func (m *PeerManager) closePeer(p *peer.Peer) {
	err := p.Close()
	if err != nil {
		m.log.Error(err)
	}
}

func (m *PeerManager) handleDisconnect(p *peer.Peer) {
	m.m.Lock()
	defer m.m.Unlock()
	delete(m.peers, p.ID())
}

func (m *PeerManager) waitDisconnect(p *peer.Peer, stopC chan struct{}) {
	<-p.NotifyDisconnect()
	select {
	case m.peerDisconnected <- p:
	case <-stopC:
		return
	}
}

func (m *PeerManager) Peers() []*peer.Peer {
	m.m.Lock()
	defer m.m.Unlock()
	peers := make([]*peer.Peer, 0, len(m.peers))
	for _, v := range m.peers {
		peers = append(peers, v)
	}
	return peers
}
<commit_msg>fix peer manager wait<commit_after>package peermanager

import (
	"net"
	"sync"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/mse"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/peerlist"
	"github.com/cenkalti/rain/internal/torrentdata"
)

// Do not connect more than maxPeerPerTorrent peers.
const maxPeerPerTorrent = 60

type PeerManager struct {
	peers            map[[20]byte]*peer.Peer // connected peers
	peerDisconnected chan *peer.Peer
	port             int
	listener         *net.TCPListener
	peerList         *peerlist.PeerList
	peerID           [20]byte
	infoHash         [20]byte
	sKeyHash         [20]byte
	data             *torrentdata.Data
	limiter          chan struct{}
	peerMessages     *peer.Messages
	log              logger.Logger
	m                sync.Mutex
	wg               sync.WaitGroup
}

func New(port int, pl *peerlist.PeerList, peerID, infoHash [20]byte, d *torrentdata.Data, l logger.Logger) *PeerManager {
	return &PeerManager{
		peers:            make(map[[20]byte]*peer.Peer),
		peerDisconnected: make(chan *peer.Peer),
		port:             port,
		peerList:         pl,
		peerID:           peerID,
		infoHash:         infoHash,
		sKeyHash:         mse.HashSKey(infoHash[:]),
		data:             d,
		limiter:          make(chan struct{}, maxPeerPerTorrent),
		peerMessages:     peer.NewMessages(),
		log:              l,
	}
}

func (m *PeerManager) PeerMessages() *peer.Messages {
	return m.peerMessages
}

func (m *PeerManager) close() {
	m.m.Lock()
	defer m.m.Unlock()
	if m.listener != nil {
		err := m.listener.Close()
		if err != nil {
			m.log.Errorln("cannot close listener:", err)
		}
	}
	for _, p := range m.peers {
		err := p.Close()
		if err != nil {
			m.log.Errorln("cannot close peer:", err)
		}
	}
}

func (m *PeerManager) Run(stopC chan struct{}) {
	m.startAcceptor(stopC)
	m.startDialer(stopC)
	for {
		select {
		case p := <-m.peerDisconnected:
			m.handleDisconnect(p)
		case <-stopC:
			m.close()
			m.wg.Wait()
			return
		}
	}
}

func (m *PeerManager) startAcceptor(stopC chan struct{}) {
	m.m.Lock()
	defer m.m.Unlock()

	var err error
	m.listener, err = net.ListenTCP("tcp4", &net.TCPAddr{Port: m.port})
	if err != nil {
		m.log.Errorf("cannot listen port %d: %s", m.port, err)
		return
	}

	m.log.Notice("Listening peers on tcp://" + m.listener.Addr().String())
	m.wg.Add(1)
	go func() {
		defer m.wg.Done()
		m.acceptor(stopC)
	}()
}

func (m *PeerManager) startDialer(stopC chan struct{}) {
	m.wg.Add(1)
	go func() {
		defer m.wg.Done()
		m.dialer(stopC)
	}()
}

func (m *PeerManager) handleConnect(p *peer.Peer, stopC chan struct{}) {
	m.m.Lock()
	defer m.m.Unlock()
	if _, ok := m.peers[p.ID()]; ok {
		m.log.Warningln("peer already connected, dropping connection:", p.String())
		go m.closePeer(p)
		return
	}
	go m.waitDisconnect(p, stopC)
	m.peers[p.ID()] = p
	p.Run(stopC)
}

func (m *PeerManager) closePeer(p *peer.Peer) {
	err := p.Close()
	if err != nil {
		m.log.Error(err)
	}
}

func (m *PeerManager) handleDisconnect(p *peer.Peer) {
	m.m.Lock()
	defer m.m.Unlock()
	delete(m.peers, p.ID())
}

func (m *PeerManager) waitDisconnect(p *peer.Peer, stopC chan struct{}) {
	<-p.NotifyDisconnect()
	select {
	case m.peerDisconnected <- p:
	case <-stopC:
		return
	}
}

func (m *PeerManager) Peers() []*peer.Peer {
	m.m.Lock()
	defer m.m.Unlock()
	peers := make([]*peer.Peer, 0, len(m.peers))
	for _, v := range m.peers {
		peers = append(peers, v)
	}
	return peers
}
<|endoftext|>