<commit_before>package login

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/Bplotka/oidc"
)

const callbackPath = "/callback"

func callbackURL(u *url.URL) string {
	return u.Path + callbackPath
}

// CachedOIDCTokenSource implements `oidc.TokenSource` interface to perform oidc-browser-dance.
// It caches fetched tokens in provided TokenCache e.g on disk.
// No mutex since it is implemented to be used with oidc.ReuseTokenSource which already guards it.
type OIDCTokenSource struct {
	ctx    context.Context
	logger *log.Logger

	oidcClient *oidc.Client
	oidcConfig oidc.Config

	// These two are guarded by mutex.
	tokenCache TokenCache
	nonce      string

	bindURL *url.URL
	cfg     Config
}

func NewOIDCTokenSource(ctx context.Context, logger *log.Logger, cfg Config, tokenCache TokenCache) (oidc.TokenSource, error) {
	bindURL, err := url.Parse(cfg.BindAddress)
	if err != nil {
		return nil, fmt.Errorf("BindAddress or Issuer are not in a form of URL. Err: %v", err)
	}

	oidcConfig := oidc.Config{
		ClientID:     cfg.ClientID,
		ClientSecret: cfg.ClientSecret,
		RedirectURL:  callbackURL(bindURL),
		Scopes:       cfg.Scopes,
	}

	oidcClient, err := oidc.NewClient(ctx, cfg.Provider)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize OIDC client. Err: %v", err)
	}

	s := &OIDCTokenSource{
		ctx:    ctx,
		logger: logger,

		oidcClient: oidcClient,
		oidcConfig: oidcConfig,

		tokenCache: tokenCache,
		cfg:        cfg,
		bindURL:    bindURL,
	}

	return oidc.NewReuseTokenSource(nil, s), nil
}

// OIDCToken is used to obtain new OIDC Token (which include e.g access token, refresh token and id token). It does that by
// using a Refresh Token to obtain new Tokens. If the cached one is still valid it returns it immediately.
func (s *OIDCTokenSource) OIDCToken() (*oidc.Token, error) {
	cachedToken, err := s.tokenCache.Token()
	if err != nil {
		s.logger.Printf("Error: Failed to get cached token. Caching might be broken. Err: %v", err)

	} else if cachedToken != nil {
		if cachedToken.Valid(s.ctx, s.oidcClient.Verifier(
			oidc.VerificationConfig{
				ClientID:   s.cfg.ClientID,
				ClaimNonce: s.nonce,
			},
		)) {
			// Successfully retrieved a non-expired cached token and only if we have ID token as well.
			return cachedToken, nil
		}

		if cachedToken.RefreshToken != "" {
			// Only if we have refresh token, we can refresh IDToken.
			oidcToken, err := s.refreshToken(cachedToken)
			if err == nil {
				return oidcToken, nil
			}

			// Our refresh token expired.
			s.logger.Print("Warn: Refresh token expired.")
		}
	}

	// Our request for access token was denied, either we had no RefreshToken, it was invalid or expired.
	newToken, err := s.newToken()
	if err != nil {
		return nil, fmt.Errorf("Failed to obtain new refresh token. Err: %v", err)
	}

	return newToken, nil
}

func (s *OIDCTokenSource) Verifier() oidc.Verifier {
	return s.oidcClient.Verifier(oidc.VerificationConfig{
		ClientID: s.oidcConfig.ClientID,
	})
}

func (s *OIDCTokenSource) refreshToken(invalidToken *oidc.Token) (*oidc.Token, error) {
	ctx := oidcContextWithTimeout(s.ctx)

	s.logger.Printf("Debug: Cached token has none or expired ID token or access token. " +
		"Try to refresh access token using refresh token.")
	token, err := s.oidcClient.TokenSource(
		ctx,
		s.oidcConfig,
		oidc.VerificationConfig{ClientID: s.cfg.ClientID},
		invalidToken,
	).OIDCToken()
	if err != nil {
		return nil, err
	}

	if !token.Valid(s.ctx, s.oidcClient.Verifier(oidc.VerificationConfig{ClientID: s.cfg.ClientID})) {
		return nil, fmt.Errorf("got invalid idToken from provider.")
	}

	err = s.tokenCache.SetToken(token)
	if err != nil {
		s.logger.Printf("Warn: Cannot cache token. Err: %v", err)
	}

	return token, nil
}

func oidcContextWithTimeout(ctx context.Context) context.Context {
	if existing := ctx.Value(oidc.HTTPClientCtxKey); existing != nil {
		return ctx
	}
	timeout := 10 * time.Second
	if deadline, exists := ctx.Deadline(); exists {
		timeout = deadline.Sub(time.Now())
	}
	return context.WithValue(ctx, oidc.HTTPClientCtxKey, http.Client{
		Timeout: timeout,
	})
}

func (s *OIDCTokenSource) prefixPath() string {
	if s.bindURL.Path != "" {
		return s.bindURL.Path
	}
	return "/"
}

func (s *OIDCTokenSource) newToken() (*oidc.Token, error) {
	s.logger.Print("Debug: Perfoming auth Code flow to obtain entirely new OIDC token.")

	callbackChan := make(chan *callbackMsg, 100)

	state := rand128Bits()
	nonce := ""
	extra := url.Values{}
	if s.cfg.NonceCheck {
		nonce = rand128Bits()
		extra.Set("nonce", nonce)
	}

	ctx, cancel := context.WithTimeout(s.ctx, 1*time.Minute)
	defer cancel()

	handler := http.NewServeMux()
	handler.HandleFunc(callbackURL(s.bindURL), CallbackHandler(
		s.oidcClient,
		s.oidcConfig,
		state,
		callbackChan,
	))

	server := &http.Server{Addr: s.bindURL.Host, Handler: handler}
	go func() {
		err := server.ListenAndServe()
		if err != nil {
			s.logger.Printf("Warn: Callback server fail: %v", err)
		}
		cancel()
	}()
	defer func() {
		server.Close()
		close(callbackChan)
	}()

	authURL := s.oidcClient.AuthCodeURL(s.oidcConfig, state, extra)

	err := openBrowser(authURL)
	if err != nil {
		return nil, fmt.Errorf("oidc: Failed to open browser. Err: %v", err)
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	go func() {
		<-quit
		cancel()
	}()

	select {
	// TODO(bplotka): What if someone will scan our callback endpoint?
	case msg := <-callbackChan:
		if msg.err != nil {
			return nil, fmt.Errorf("oidc: Callback error: %v", msg.err)
		}

		s.nonce = nonce
		err = s.tokenCache.SetToken(msg.token)
		if err != nil {
			s.logger.Printf("Warn: Cannot cache token. Err: %v", err)
		}

		return msg.token, nil
	case <-ctx.Done():
		return nil, fmt.Errorf("oidc Deadline Exceeded: Timed out waiting for token. Please retry the command and open the URL printed above in a browser if it doesn't open automatically.")
	}
}
<commit_msg>Moved to full callback path for redirectURL.<commit_after>package login

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/Bplotka/oidc"
)

const callbackPath = "/callback"

func callbackURL(u *url.URL) string {
	return u.Path + callbackPath
}

// CachedOIDCTokenSource implements `oidc.TokenSource` interface to perform oidc-browser-dance.
// It caches fetched tokens in provided TokenCache e.g on disk.
// No mutex since it is implemented to be used with oidc.ReuseTokenSource which already guards it.
type OIDCTokenSource struct {
	ctx    context.Context
	logger *log.Logger

	oidcClient *oidc.Client
	oidcConfig oidc.Config

	// These two are guarded by mutex.
	tokenCache TokenCache
	nonce      string

	bindURL *url.URL
	cfg     Config
}

func NewOIDCTokenSource(ctx context.Context, logger *log.Logger, cfg Config, tokenCache TokenCache) (oidc.TokenSource, error) {
	bindURL, err := url.Parse(cfg.BindAddress)
	if err != nil {
		return nil, fmt.Errorf("BindAddress or Issuer are not in a form of URL. Err: %v", err)
	}

	oidcConfig := oidc.Config{
		ClientID:     cfg.ClientID,
		ClientSecret: cfg.ClientSecret,
		RedirectURL:  bindURL.String() + callbackPath,
		Scopes:       cfg.Scopes,
	}

	oidcClient, err := oidc.NewClient(ctx, cfg.Provider)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize OIDC client. Err: %v", err)
	}

	s := &OIDCTokenSource{
		ctx:    ctx,
		logger: logger,

		oidcClient: oidcClient,
		oidcConfig: oidcConfig,

		tokenCache: tokenCache,
		cfg:        cfg,
		bindURL:    bindURL,
	}

	return oidc.NewReuseTokenSource(nil, s), nil
}

// OIDCToken is used to obtain new OIDC Token (which include e.g access token, refresh token and id token). It does that by
// using a Refresh Token to obtain new Tokens. If the cached one is still valid it returns it immediately.
func (s *OIDCTokenSource) OIDCToken() (*oidc.Token, error) {
	cachedToken, err := s.tokenCache.Token()
	if err != nil {
		s.logger.Printf("Error: Failed to get cached token. Caching might be broken. Err: %v", err)

	} else if cachedToken != nil {
		if cachedToken.Valid(s.ctx, s.oidcClient.Verifier(
			oidc.VerificationConfig{
				ClientID:   s.cfg.ClientID,
				ClaimNonce: s.nonce,
			},
		)) {
			// Successfully retrieved a non-expired cached token and only if we have ID token as well.
			return cachedToken, nil
		}

		if cachedToken.RefreshToken != "" {
			// Only if we have refresh token, we can refresh IDToken.
			oidcToken, err := s.refreshToken(cachedToken)
			if err == nil {
				return oidcToken, nil
			}

			// Our refresh token expired.
			s.logger.Print("Warn: Refresh token expired.")
		}
	}

	// Our request for access token was denied, either we had no RefreshToken, it was invalid or expired.
	newToken, err := s.newToken()
	if err != nil {
		return nil, fmt.Errorf("Failed to obtain new refresh token. Err: %v", err)
	}

	return newToken, nil
}

func (s *OIDCTokenSource) Verifier() oidc.Verifier {
	return s.oidcClient.Verifier(oidc.VerificationConfig{
		ClientID: s.oidcConfig.ClientID,
	})
}

func (s *OIDCTokenSource) refreshToken(invalidToken *oidc.Token) (*oidc.Token, error) {
	ctx := oidcContextWithTimeout(s.ctx)

	s.logger.Printf("Debug: Cached token has none or expired ID token or access token. " +
		"Try to refresh access token using refresh token.")
	token, err := s.oidcClient.TokenSource(
		ctx,
		s.oidcConfig,
		oidc.VerificationConfig{ClientID: s.cfg.ClientID},
		invalidToken,
	).OIDCToken()
	if err != nil {
		return nil, err
	}

	if !token.Valid(s.ctx, s.oidcClient.Verifier(oidc.VerificationConfig{ClientID: s.cfg.ClientID})) {
		return nil, fmt.Errorf("got invalid idToken from provider.")
	}

	err = s.tokenCache.SetToken(token)
	if err != nil {
		s.logger.Printf("Warn: Cannot cache token. Err: %v", err)
	}

	return token, nil
}

func oidcContextWithTimeout(ctx context.Context) context.Context {
	if existing := ctx.Value(oidc.HTTPClientCtxKey); existing != nil {
		return ctx
	}
	timeout := 10 * time.Second
	if deadline, exists := ctx.Deadline(); exists {
		timeout = deadline.Sub(time.Now())
	}
	return context.WithValue(ctx, oidc.HTTPClientCtxKey, http.Client{
		Timeout: timeout,
	})
}

func (s *OIDCTokenSource) prefixPath() string {
	if s.bindURL.Path != "" {
		return s.bindURL.Path
	}
	return "/"
}

func (s *OIDCTokenSource) newToken() (*oidc.Token, error) {
	s.logger.Print("Debug: Perfoming auth Code flow to obtain entirely new OIDC token.")

	callbackChan := make(chan *callbackMsg, 100)

	state := rand128Bits()
	nonce := ""
	extra := url.Values{}
	if s.cfg.NonceCheck {
		nonce = rand128Bits()
		extra.Set("nonce", nonce)
	}

	ctx, cancel := context.WithTimeout(s.ctx, 1*time.Minute)
	defer cancel()

	handler := http.NewServeMux()
	handler.HandleFunc(callbackURL(s.bindURL), CallbackHandler(
		s.oidcClient,
		s.oidcConfig,
		state,
		callbackChan,
	))

	server := &http.Server{Addr: s.bindURL.Host, Handler: handler}
	go func() {
		err := server.ListenAndServe()
		if err != nil {
			s.logger.Printf("Warn: Callback server fail: %v", err)
		}
		cancel()
	}()
	defer func() {
		server.Close()
		close(callbackChan)
	}()

	authURL := s.oidcClient.AuthCodeURL(s.oidcConfig, state, extra)

	err := openBrowser(authURL)
	if err != nil {
		return nil, fmt.Errorf("oidc: Failed to open browser. Err: %v", err)
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	go func() {
		<-quit
		cancel()
	}()

	select {
	// TODO(bplotka): What if someone will scan our callback endpoint?
	case msg := <-callbackChan:
		if msg.err != nil {
			return nil, fmt.Errorf("oidc: Callback error: %v", msg.err)
		}

		s.nonce = nonce
		err = s.tokenCache.SetToken(msg.token)
		if err != nil {
			s.logger.Printf("Warn: Cannot cache token. Err: %v", err)
		}

		return msg.token, nil
	case <-ctx.Done():
		return nil, fmt.Errorf("oidc Deadline Exceeded: Timed out waiting for token. Please retry the command and open the URL printed above in a browser if it doesn't open automatically.")
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"time"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/core/lease"
	coretesting "github.com/juju/juju/testing"
)

type SingularSuite struct {
	ConnSuite
}

var _ = gc.Suite(&SingularSuite{})

func (s *SingularSuite) SetUpTest(c *gc.C) {
	s.ConnSuite.SetUpTest(c)
	err := s.State.SetClockForTesting(s.Clock)
	c.Assert(err, jc.ErrorIsNil)
}

func (s *SingularSuite) TestClaimBadLease(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim("xxx", "machine-123", time.Minute)
	c.Check(err, gc.ErrorMatches, `cannot claim lease "xxx": expected environ UUID`)
}

func (s *SingularSuite) TestClaimBadHolder(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "unit-foo-1", time.Minute)
	c.Check(err, gc.ErrorMatches, `cannot claim lease for holder "unit-foo-1": expected machine tag`)
}

func (s *SingularSuite) TestClaimBadDuration(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", 0)
	c.Check(err, gc.ErrorMatches, `cannot claim lease for 0s?: non-positive`)
}

func (s *SingularSuite) TestClaim(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)

	err = claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)

	err = claimer.Claim(s.modelTag.Id(), "machine-456", time.Minute)
	c.Assert(err, gc.Equals, lease.ErrClaimDenied)
}

func (s *SingularSuite) TestExpire(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)
	wait := make(chan error)
	go func() {
		s.Clock.Advance(coretesting.ShortWait)
		wait <- claimer.WaitUntilExpired(s.modelTag.Id())
	}()
	select {
	case err := <-wait:
		c.Fatalf("expired early with %v", err)
	case <-s.Clock.After(coretesting.ShortWait):
	}

	s.Clock.Advance(time.Hour)
	select {
	case err := <-wait:
		c.Check(err, jc.ErrorIsNil)
	case <-s.Clock.After(coretesting.LongWait):
		c.Fatalf("never expired")
	}

	err = claimer.Claim(s.modelTag.Id(), "machine-456", time.Minute)
	c.Assert(err, jc.ErrorIsNil)
}
<commit_msg>state: fix SingularSuite.TestExpire<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"time"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/core/lease"
	coretesting "github.com/juju/juju/testing"
)

type SingularSuite struct {
	ConnSuite
}

var _ = gc.Suite(&SingularSuite{})

func (s *SingularSuite) SetUpTest(c *gc.C) {
	s.ConnSuite.SetUpTest(c)
	err := s.State.SetClockForTesting(s.Clock)
	c.Assert(err, jc.ErrorIsNil)
}

func (s *SingularSuite) TestClaimBadLease(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim("xxx", "machine-123", time.Minute)
	c.Check(err, gc.ErrorMatches, `cannot claim lease "xxx": expected environ UUID`)
}

func (s *SingularSuite) TestClaimBadHolder(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "unit-foo-1", time.Minute)
	c.Check(err, gc.ErrorMatches, `cannot claim lease for holder "unit-foo-1": expected machine tag`)
}

func (s *SingularSuite) TestClaimBadDuration(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", 0)
	c.Check(err, gc.ErrorMatches, `cannot claim lease for 0s?: non-positive`)
}

func (s *SingularSuite) TestClaim(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)

	err = claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)

	err = claimer.Claim(s.modelTag.Id(), "machine-456", time.Minute)
	c.Assert(err, gc.Equals, lease.ErrClaimDenied)
}

func (s *SingularSuite) TestExpire(c *gc.C) {
	claimer := s.State.SingularClaimer()
	err := claimer.Claim(s.modelTag.Id(), "machine-123", time.Minute)
	c.Assert(err, jc.ErrorIsNil)
	wait := make(chan error)
	go func() {
		wait <- claimer.WaitUntilExpired(s.modelTag.Id())
	}()

	s.Clock.Advance(coretesting.ShortWait)
	select {
	case err := <-wait:
		c.Fatalf("expired early with %v", err)
	case <-time.After(coretesting.ShortWait):
	}

	s.Clock.Advance(time.Hour)
	select {
	case err := <-wait:
		c.Check(err, jc.ErrorIsNil)
	case <-time.After(coretesting.LongWait):
		c.Fatalf("never expired")
	}

	err = claimer.Claim(s.modelTag.Id(), "machine-456", time.Minute)
	c.Assert(err, jc.ErrorIsNil)
}
<|endoftext|>


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

import (
	"fmt"
	"os"
	"strconv"

	"github.com/davecgh/go-spew/spew"
	"github.com/olekukonko/tablewriter"
)

func PrintL2Switch(name string) {
	if sw, ok := switches[name]; ok {
		table := tablewriter.NewWriter(os.Stdout)
		if sw.swIomodule != nil {
			// table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"})
			table.Append([]string{sw.Name, sw.swIomodule.ModuleId, strconv.Itoa(sw.swIomodule.PortsCount)})
			table.Render()
		} else {
			// table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"})
			table.Append([]string{sw.Name, sw.swIomodule.ModuleId, " "})
			table.Render()
		}

		table = tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"SWITCH", "NAME", "IFACE"})
		for _, swp := range sw.ports {
			table.Append([]string{sw.Name, swp.Name, swp.IfaceName})
		}
		table.Render()

		if sw.swIomodule != nil {
			table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "NAME", "LINK", "FD", "BCASTID", "REDIRECT"})
			for _, iface := range sw.swIomodule.Interfaces {
				table.Append([]string{sw.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceFd), strconv.Itoa(iface.IfaceIdArrayBroadcast), strconv.Itoa(iface.IfaceIdRedirectHover)})
			}
			table.Render()
		}
		//TODO We need to print ports array?
		//spew.Dump(sw.swIomodule.PortsArray)
	}
}

func PrintL2Switches(verbose bool) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"SWITCHES", "MODULE-ID", "PORTS#"})
	for swname, sw := range switches {
		table.Append([]string{swname, sw.swIomodule.ModuleId, strconv.Itoa(sw.swIomodule.PortsCount)})
	}
	table.Render()

	if verbose {
		for swname, _ := range switches {
			fmt.Printf("\n")
			PrintL2Switch(swname)
		}
	}
}

func PrintRouter(name string) {
	if router, ok := routers[name]; ok {
		//TODO Improve. First implementation
		spew.Dump(router)

		if r, ok := routers[name]; ok {
			table := tablewriter.NewWriter(os.Stdout)
			if r.rIoModule != nil {
				// table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
				table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)})
				table.Render()
			} else {
				// table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
				table.Append([]string{r.Name, r.rIoModule.ModuleId, " "})
				table.Render()
			}

			table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"ROUTER", "NAME", "IP", "NETMASK", "MAC"})
			for _, rp := range r.ports {
				table.Append([]string{r.Name, rp.Name, rp.IP, rp.Mask, rp.Mac})
			}
			table.Render()

			if r.rIoModule != nil {
				table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "NAME", "LINK", "FD", "REDIRECT", "IP", "NETMASK", "MAC"})
				for _, iface := range r.rIoModule.Interfaces {
					table.Append([]string{r.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceFd), strconv.Itoa(iface.IfaceIdRedirectHover), iface.IP, iface.Netmask, iface.IP})
				}
				table.Render()
			}
		}
	}
}

func PrintRouters(verbose bool) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
	for _, r := range routers {
		table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)})
		table.Render()
	}
	table.Render()

	if verbose {
		for routername, _ := range routers {
			fmt.Printf("\n")
			PrintRouter(routername)
		}
	}
}

func PrintMainLogic(verbose bool) {
	fmt.Printf("\nSwitches\n\n")
	PrintL2Switches(verbose)
	fmt.Printf("\nRouters\n\n")
	PrintRouters(verbose)
}
<commit_msg>cli: remove debug print router<commit_after>// Copyright 2016 Politecnico di Torino
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mainlogic

import (
	"fmt"
	"os"
	"strconv"

	"github.com/olekukonko/tablewriter"
)

func PrintL2Switch(name string) {
	if sw, ok := switches[name]; ok {
		table := tablewriter.NewWriter(os.Stdout)
		if sw.swIomodule != nil {
			// table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"})
			table.Append([]string{sw.Name, sw.swIomodule.ModuleId, strconv.Itoa(sw.swIomodule.PortsCount)})
			table.Render()
		} else {
			// table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"})
			table.Append([]string{sw.Name, sw.swIomodule.ModuleId, " "})
			table.Render()
		}

		table = tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"SWITCH", "NAME", "IFACE"})
		for _, swp := range sw.ports {
			table.Append([]string{sw.Name, swp.Name, swp.IfaceName})
		}
		table.Render()

		if sw.swIomodule != nil {
			table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"SWITCH", "NAME", "LINK", "FD", "BCASTID", "REDIRECT"})
			for _, iface := range sw.swIomodule.Interfaces {
				table.Append([]string{sw.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceFd), strconv.Itoa(iface.IfaceIdArrayBroadcast), strconv.Itoa(iface.IfaceIdRedirectHover)})
			}
			table.Render()
		}
		//TODO We need to print ports array?
		//spew.Dump(sw.swIomodule.PortsArray)
	}
}

func PrintL2Switches(verbose bool) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"SWITCHES", "MODULE-ID", "PORTS#"})
	for swname, sw := range switches {
		table.Append([]string{swname, sw.swIomodule.ModuleId, strconv.Itoa(sw.swIomodule.PortsCount)})
	}
	table.Render()

	if verbose {
		for swname, _ := range switches {
			fmt.Printf("\n")
			PrintL2Switch(swname)
		}
	}
}

func PrintRouter(name string) {
	if _, ok := routers[name]; ok {

		if r, ok := routers[name]; ok {
			table := tablewriter.NewWriter(os.Stdout)
			if r.rIoModule != nil {
				// table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
				table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)})
				table.Render()
			} else {
				// table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
				table.Append([]string{r.Name, r.rIoModule.ModuleId, " "})
				table.Render()
			}

			table = tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"ROUTER", "NAME", "IP", "NETMASK", "MAC"})
			for _, rp := range r.ports {
				table.Append([]string{r.Name, rp.Name, rp.IP, rp.Mask, rp.Mac})
			}
			table.Render()

			if r.rIoModule != nil {
				table = tablewriter.NewWriter(os.Stdout)
				table.SetHeader([]string{"ROUTER", "NAME", "LINK", "FD", "REDIRECT", "IP", "NETMASK", "MAC"})
				for _, iface := range r.rIoModule.Interfaces {
					table.Append([]string{r.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceFd), strconv.Itoa(iface.IfaceIdRedirectHover), iface.IP, iface.Netmask, iface.IP})
				}
				table.Render()
			}
		}
	}
}

func PrintRouters(verbose bool) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"})
	for _, r := range routers {
		table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)})
		table.Render()
	}
	table.Render()

	if verbose {
		for routername, _ := range routers {
			fmt.Printf("\n")
			PrintRouter(routername)
		}
	}
}

func PrintMainLogic(verbose bool) {
	fmt.Printf("\nSwitches\n\n")
	PrintL2Switches(verbose)
	fmt.Printf("\nRouters\n\n")
	PrintRouters(verbose)
}
<|endoftext|>


<commit_before><commit_msg>added input sanitation and markdown support<commit_after><|endoftext|>


<commit_before><commit_msg>Fix tests<commit_after><|endoftext|>


<commit_before><commit_msg>fix nil<commit_after><|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/ovn-org/libovsdb/client"
	"github.com/ovn-org/libovsdb/example/vswitchd"
	"github.com/ovn-org/libovsdb/model"
	"github.com/ovn-org/libovsdb/ovsdb"
	"github.com/ovn-org/libovsdb/server"
)

var (
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
	memprofile = flag.String("memoryprofile", "", "write memory profile to this file")
	port       = flag.Int("port", 56640, "tcp port to listen on")
)

func main() {
	flag.Parse()
	var err error
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}

	dbModel, err := vswitchd.FullDatabaseModel()
	if err != nil {
		log.Fatal(err)
	}
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(wd, "vswitchd", "vswitchd.ovsschema")
	f, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	schema, err := ovsdb.SchemaFromFile(f)
	if err != nil {
		log.Fatal(err)
	}

	ovsDB := server.NewInMemoryDatabase(map[string]*model.DBModel{
		schema.Name: dbModel,
	})

	s, err := server.NewOvsdbServer(ovsDB, server.DatabaseModel{
		Model:  dbModel,
		Schema: schema,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)

	go func(o *server.OvsdbServer) {
		if err := o.Serve("tcp", fmt.Sprintf(":%d", *port)); err != nil {
			log.Fatal(err)
		}
	}(s)

	time.Sleep(1 * time.Second)
	c, err := client.NewOVSDBClient(dbModel, client.WithEndpoint(fmt.Sprintf("tcp::%d", *port)))
	if err != nil {
		log.Fatal(err)
	}

	err = c.Connect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	ovsRow := &vswitchd.OpenvSwitch{
		UUID: "ovs",
	}
	ovsOps, err := c.Create(ovsRow)
	if err != nil {
		log.Fatal(err)
	}
	reply, err := c.Transact(context.Background(), ovsOps...)
	if err != nil {
		log.Fatal(err)
	}
	_, err = ovsdb.CheckOperationResults(reply, ovsOps)
	if err != nil {
		log.Fatal(err)
	}
	c.Close()
	log.Printf("listening on tcp::%d", *port)
	<-sig

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		runtime.GC()
		if err := pprof.WriteHeapProfile(f); err != nil {
			log.Fatal("could not write memory profile: ", err)
		}
	}
}
<commit_msg>example: fix ovsdb-server schema file<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/ovn-org/libovsdb/client"
	"github.com/ovn-org/libovsdb/example/vswitchd"
	"github.com/ovn-org/libovsdb/model"
	"github.com/ovn-org/libovsdb/ovsdb"
	"github.com/ovn-org/libovsdb/server"
)

var (
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
	memprofile = flag.String("memoryprofile", "", "write memory profile to this file")
	port       = flag.Int("port", 56640, "tcp port to listen on")
)

func main() {
	flag.Parse()
	var err error
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}

	dbModel, err := vswitchd.FullDatabaseModel()
	if err != nil {
		log.Fatal(err)
	}
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(wd, "vswitchd", "ovs.ovsschema")
	f, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	schema, err := ovsdb.SchemaFromFile(f)
	if err != nil {
		log.Fatal(err)
	}

	ovsDB := server.NewInMemoryDatabase(map[string]*model.DBModel{
		schema.Name: dbModel,
	})

	s, err := server.NewOvsdbServer(ovsDB, server.DatabaseModel{
		Model:  dbModel,
		Schema: schema,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)

	go func(o *server.OvsdbServer) {
		if err := o.Serve("tcp", fmt.Sprintf(":%d", *port)); err != nil {
			log.Fatal(err)
		}
	}(s)

	time.Sleep(1 * time.Second)
	c, err := client.NewOVSDBClient(dbModel, client.WithEndpoint(fmt.Sprintf("tcp::%d", *port)))
	if err != nil {
		log.Fatal(err)
	}

	err = c.Connect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	ovsRow := &vswitchd.OpenvSwitch{
		UUID: "ovs",
	}
	ovsOps, err := c.Create(ovsRow)
	if err != nil {
		log.Fatal(err)
	}
	reply, err := c.Transact(context.Background(), ovsOps...)
	if err != nil {
		log.Fatal(err)
	}
	_, err = ovsdb.CheckOperationResults(reply, ovsOps)
	if err != nil {
		log.Fatal(err)
	}
	c.Close()
	log.Printf("listening on tcp::%d", *port)
	<-sig

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		runtime.GC()
		if err := pprof.WriteHeapProfile(f); err != nil {
			log.Fatal("could not write memory profile: ", err)
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/ncarlier/webhookd/pkg/hook"
	"github.com/ncarlier/webhookd/pkg/tools"
	"github.com/ncarlier/webhookd/pkg/worker"
)

// WebhookHandler is the main handler of the API.
func WebhookHandler(w http.ResponseWriter, r *http.Request) {
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
		return
	}

	if r.Method != "POST" {
		http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	// Get script location
	p := strings.TrimPrefix(r.URL.Path, "/")
	script, err := hook.ResolveScript(p)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		http.Error(w, "can't read body", http.StatusBadRequest)
		return
	}

	params := tools.QueryParamsToShellVars(r.URL.Query())
	log.Printf("Calling hook script \"%s\" with params %s...\n", script, params)

	// Create work
	work := new(worker.WorkRequest)
	work.Name = p
	work.Script = script
	work.Payload = string(body)
	work.Args = params
	work.MessageChan = make(chan []byte)

	// Put work in queue
	worker.WorkQueue <- *work

	r.Header.Set("Content-Type", "text/event-stream")
	r.Header.Set("Cache-Control", "no-cache")
	r.Header.Set("Connection", "keep-alive")
	r.Header.Set("Access-Control-Allow-Origin", "*")

	log.Println("Work request queued:", script)
	fmt.Fprintf(w, "data: Hook work request \"%s\" queued...\n\n", work.Name)

	for {
		msg, open := <-work.MessageChan

		if !open {
			break
		}

		fmt.Fprintf(w, "data: %s\n\n", msg)

		// Flush the data immediatly instead of buffering it for later.
		flusher.Flush()
	}
}
<commit_msg>fix(api): set SSE headers correctly<commit_after>package api

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/ncarlier/webhookd/pkg/hook"
	"github.com/ncarlier/webhookd/pkg/tools"
	"github.com/ncarlier/webhookd/pkg/worker"
)

// WebhookHandler is the main handler of the API.
func WebhookHandler(w http.ResponseWriter, r *http.Request) {
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
		return
	}

	if r.Method != "POST" {
		http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	// Get script location
	p := strings.TrimPrefix(r.URL.Path, "/")
	script, err := hook.ResolveScript(p)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		http.Error(w, "can't read body", http.StatusBadRequest)
		return
	}

	params := tools.QueryParamsToShellVars(r.URL.Query())
	log.Printf("Calling hook script \"%s\" with params %s...\n", script, params)

	// Create work
	work := new(worker.WorkRequest)
	work.Name = p
	work.Script = script
	work.Payload = string(body)
	work.Args = params
	work.MessageChan = make(chan []byte)

	// Put work in queue
	worker.WorkQueue <- *work

	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	log.Println("Work request queued:", script)
	fmt.Fprintf(w, "data: Hook work request \"%s\" queued...\n\n", work.Name)

	for {
		msg, open := <-work.MessageChan

		if !open {
			break
		}

		fmt.Fprintf(w, "data: %s\n\n", msg)

		// Flush the data immediatly instead of buffering it for later.
		flusher.Flush()
	}
}
<|endoftext|>


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

package repo

import (
	"context"
	"regexp"
	"strings"
	"time"

	"github.com/google/go-github/v33/github"
	"github.com/sirupsen/logrus"

	"github.com/google/pullsheet/pkg/client"
	"github.com/google/pullsheet/pkg/ghcache"
)

const dateForm = "2006-01-02"

var (
	ignorePathRe = regexp.MustCompile(`go\.mod|go\.sum|vendor/|third_party|ignore|schemas/v\d|schema/v\d|Gopkg.lock|.DS_Store`)
	truncRe      = regexp.MustCompile(`changelog|CHANGELOG|Gopkg.toml`)
	commentRe    = regexp.MustCompile(`<!--.*?>`)
)

// MergedPulls returns a list of pull requests in a project
func MergedPulls(ctx context.Context, c *client.Client, org string, project string, since time.Time, until time.Time, users []string) ([]*github.PullRequest, error) {
	var result []*github.PullRequest

	opts := &github.PullRequestListOptions{
		State:     "closed",
		Sort:      "updated",
		Direction: "desc",
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}

	matchUser := map[string]bool{}
	for _, u := range users {
		matchUser[strings.ToLower(u)] = true
	}

	logrus.Infof("Gathering pull requests for %s/%s, users=%q: %+v", org, project, users, opts)
	for page := 1; page != 0; {
		opts.ListOptions.Page = page
		prs, resp, err := c.GitHubClient.PullRequests.List(ctx, org, project, opts)
		if err != nil {
			return result, err
		}

		logrus.Infof("Processing page %d of %s/%s pull request results (looking for %s)...", page, org, project, since)

		page = resp.NextPage
		logrus.Infof("Current PR updated at %s", prs[0].GetUpdatedAt())
		for _, pr := range prs {
			if pr.GetClosedAt().After(until) {
				logrus.Infof("PR#%d closed at %s", pr.GetNumber(), pr.GetUpdatedAt())
				continue
			}

			if pr.GetUpdatedAt().Before(since) {
				logrus.Infof("Hit PR#%d updated at %s", pr.GetNumber(), pr.GetUpdatedAt())
				page = 0
				break
			}

			if !pr.GetClosedAt().IsZero() && pr.GetClosedAt().Before(since) {
				continue
			}

			uname := strings.ToLower(pr.GetUser().GetLogin())
			if len(matchUser) > 0 && !matchUser[uname] {
				continue
			}

			if isBot(pr.GetUser()) {
				continue
			}

			if pr.GetState() != "closed" {
				logrus.Infof("Skipping PR#%d by %s (state=%q)", pr.GetNumber(), pr.GetUser().GetLogin(), pr.GetState())
				continue
			}

			logrus.Infof("Fetching PR #%d by %s (updated %s): %q", pr.GetNumber(), pr.GetUser().GetLogin(), pr.GetUpdatedAt(), pr.GetTitle())
			fullPR, err := ghcache.PullRequestsGet(ctx, c.Cache, c.GitHubClient, pr.GetMergedAt(), org, project, pr.GetNumber())
			if err != nil {
				time.Sleep(1 * time.Second)
				fullPR, err = ghcache.PullRequestsGet(ctx, c.Cache, c.GitHubClient, pr.GetMergedAt(), org, project, pr.GetNumber())
				if err != nil {
					logrus.Errorf("failed PullRequestsGet: %v", err)
					break
				}
			}

			if !fullPR.GetMerged() || fullPR.GetMergeCommitSHA() == "" {
				logrus.Infof("#%d was not merged, skipping", pr.GetNumber())
				continue
			}

			if pr.GetMergedAt().Before(since) {
				logrus.Infof("#%d was merged earlier than %s, skipping", pr.GetNumber(), since)
				continue
			}

			result = append(result, fullPR)
		}
	}
	logrus.Infof("Returning %d pull request results", len(result))
	return result, nil
}

// PRSummary is a summary of a single PR
type PRSummary struct {
	URL         string
	Date        string
	User        string
	Project     string
	Type        string
	Title       string
	Delta       int
	Added       int
	Deleted     int
	FilesTotal  int
	Files       string // newline delimited
	Description string
}

// PullSummary converts GitHub PR data into a summarized view
func PullSummary(prs map[*github.PullRequest][]github.CommitFile, since time.Time, until time.Time) ([]*PRSummary, error) {
	sum := []*PRSummary{}
	seen := map[string]bool{}

	for pr, files := range prs {
		if seen[pr.GetHTMLURL()] {
			logrus.Infof("skipping seen issue: %s", pr.GetHTMLURL())
			continue
		}
		seen[pr.GetHTMLURL()] = true

		_, project := ParseURL(pr.GetHTMLURL())
		body := pr.GetBody()
		body = commentRe.ReplaceAllString(body, "")

		if len(body) > 240 {
			body = body[0:240] + "..."
		}
		t := pr.GetMergedAt()
		// Often the merge timestamp is empty :(
		if t.IsZero() {
			t = pr.GetClosedAt()
		}

		if t.After(until) {
			logrus.Infof("skipping %s - closed at %s, after %s", pr.GetHTMLURL(), t, until)
			continue
		}

		if t.Before(since) {
			logrus.Infof("skipping %s - closed at %s, before %s", pr.GetHTMLURL(), t, since)
			continue
		}

		added := 0
		paths := []string{}
		deleted := 0

		for _, f := range files {
			// These files are mostly auto-generated
			if truncRe.MatchString(f.GetFilename()) && f.GetAdditions() > 10 {
				logrus.Infof("truncating %s from %d to %d lines added", f.GetFilename(), f.GetAdditions(), 10)
				added += 10
			} else {
				added += f.GetAdditions()
			}
			deleted += f.GetDeletions()
			paths = append(paths, f.GetFilename())
		}

		sum = append(sum, &PRSummary{
			URL:         pr.GetHTMLURL(),
			Date:        t.Format(dateForm),
			Project:     project,
			Type:        prType(files),
			Title:       pr.GetTitle(),
			User:        pr.GetUser().GetLogin(),
			Delta:       added + deleted,
			Added:       added,
			Deleted:     deleted,
			FilesTotal:  pr.GetChangedFiles(),
			Files:       strings.Join(paths, "\n"),
			Description: body,
		})
	}

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

package repo

import (
	"context"
	"regexp"
	"strings"
	"time"

	"github.com/google/go-github/v33/github"
	"github.com/sirupsen/logrus"

	"github.com/google/pullsheet/pkg/client"
	"github.com/google/pullsheet/pkg/ghcache"
)

const dateForm = "2006-01-02"

var (
	ignorePathRe = regexp.MustCompile(`go\.mod|go\.sum|vendor/|third_party|ignore|schemas/v\d|schema/v\d|Gopkg.lock|.DS_Store|swagger.json|\.pb\.go|references/api/grpc`)
	truncRe      = regexp.MustCompile(`changelog|CHANGELOG|Gopkg.toml`)
	commentRe    = regexp.MustCompile(`<!--.*?>`)
)

// MergedPulls returns a list of pull requests in a project
func MergedPulls(ctx context.Context, c *client.Client, org string, project string, since time.Time, until time.Time, users []string) ([]*github.PullRequest, error) {
	var result []*github.PullRequest

	opts := &github.PullRequestListOptions{
		State:     "closed",
		Sort:      "updated",
		Direction: "desc",
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}

	matchUser := map[string]bool{}
	for _, u := range users {
		matchUser[strings.ToLower(u)] = true
	}

	logrus.Infof("Gathering pull requests for %s/%s, users=%q: %+v", org, project, users, opts)
	for page := 1; page != 0; {
		opts.ListOptions.Page = page
		prs, resp, err := c.GitHubClient.PullRequests.List(ctx, org, project, opts)
		if err != nil {
			return result, err
		}

		logrus.Infof("Processing page %d of %s/%s pull request results (looking for %s)...", page, org, project, since)

		page = resp.NextPage
		logrus.Infof("Current PR updated at %s", prs[0].GetUpdatedAt())
		for _, pr := range prs {
			if pr.GetClosedAt().After(until) {
				logrus.Infof("PR#%d closed at %s", pr.GetNumber(), pr.GetUpdatedAt())
				continue
			}

			if pr.GetUpdatedAt().Before(since) {
				logrus.Infof("Hit PR#%d updated at %s", pr.GetNumber(), pr.GetUpdatedAt())
				page = 0
				break
			}

			if !pr.GetClosedAt().IsZero() && pr.GetClosedAt().Before(since) {
				continue
			}

			uname := strings.ToLower(pr.GetUser().GetLogin())
			if len(matchUser) > 0 && !matchUser[uname] {
				continue
			}

			if isBot(pr.GetUser()) {
				continue
			}

			if pr.GetState() != "closed" {
				logrus.Infof("Skipping PR#%d by %s (state=%q)", pr.GetNumber(), pr.GetUser().GetLogin(), pr.GetState())
				continue
			}

			logrus.Infof("Fetching PR #%d by %s (updated %s): %q", pr.GetNumber(), pr.GetUser().GetLogin(), pr.GetUpdatedAt(), pr.GetTitle())
			fullPR, err := ghcache.PullRequestsGet(ctx, c.Cache, c.GitHubClient, pr.GetMergedAt(), org, project, pr.GetNumber())
			if err != nil {
				time.Sleep(1 * time.Second)
				fullPR, err = ghcache.PullRequestsGet(ctx, c.Cache, c.GitHubClient, pr.GetMergedAt(), org, project, pr.GetNumber())
				if err != nil {
					logrus.Errorf("failed PullRequestsGet: %v", err)
					break
				}
			}

			if !fullPR.GetMerged() || fullPR.GetMergeCommitSHA() == "" {
				logrus.Infof("#%d was not merged, skipping", pr.GetNumber())
				continue
			}

			if pr.GetMergedAt().Before(since) {
				logrus.Infof("#%d was merged earlier than %s, skipping", pr.GetNumber(), since)
				continue
			}

			result = append(result, fullPR)
		}
	}
	logrus.Infof("Returning %d pull request results", len(result))
	return result, nil
}

// PRSummary is a summary of a single PR
type PRSummary struct {
	URL         string
	Date        string
	User        string
	Project     string
	Type        string
	Title       string
	Delta       int
	Added       int
	Deleted     int
	FilesTotal  int
	Files       string // newline delimited
	Description string
}

// PullSummary converts GitHub PR data into a summarized view
func PullSummary(prs map[*github.PullRequest][]github.CommitFile, since time.Time, until time.Time) ([]*PRSummary, error) {
	sum := []*PRSummary{}
	seen := map[string]bool{}

	for pr, files := range prs {
		if seen[pr.GetHTMLURL()] {
			logrus.Infof("skipping seen issue: %s", pr.GetHTMLURL())
			continue
		}
		seen[pr.GetHTMLURL()] = true

		_, project := ParseURL(pr.GetHTMLURL())
		body := pr.GetBody()
		body = commentRe.ReplaceAllString(body, "")

		if len(body) > 240 {
			body = body[0:240] + "..."
		}
		t := pr.GetMergedAt()
		// Often the merge timestamp is empty :(
		if t.IsZero() {
			t = pr.GetClosedAt()
		}

		if t.After(until) {
			logrus.Infof("skipping %s - closed at %s, after %s", pr.GetHTMLURL(), t, until)
			continue
		}

		if t.Before(since) {
			logrus.Infof("skipping %s - closed at %s, before %s", pr.GetHTMLURL(), t, since)
			continue
		}

		added := 0
		paths := []string{}
		deleted := 0

		for _, f := range files {
			// These files are mostly auto-generated
			if truncRe.MatchString(f.GetFilename()) && f.GetAdditions() > 10 {
				logrus.Infof("truncating %s from %d to %d lines added", f.GetFilename(), f.GetAdditions(), 10)
				added += 10
			} else {
				added += f.GetAdditions()
			}
			deleted += f.GetDeletions()
			paths = append(paths, f.GetFilename())
		}

		sum = append(sum, &PRSummary{
			URL:         pr.GetHTMLURL(),
			Date:        t.Format(dateForm),
			Project:     project,
			Type:        prType(files),
			Title:       pr.GetTitle(),
			User:        pr.GetUser().GetLogin(),
			Delta:       added + deleted,
			Added:       added,
			Deleted:     deleted,
			FilesTotal:  pr.GetChangedFiles(),
			Files:       strings.Join(paths, "\n"),
			Description: body,
		})
	}

	return sum, nil
}
<|endoftext|>


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

package srv

import (
	"fmt"
	"net"
	"net/url"
	"strings"

	"github.com/coreos/etcd/pkg/types"
)

var (
	// indirection for testing
	lookupSRV      = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict
	resolveTCPAddr = net.ResolveTCPAddr
)

// GetCluster gets the cluster information via DNS discovery.
// Also sees each entry as a separate instance.
func GetCluster(service, name, dns string, apurls types.URLs) ([]string, error) {
	tempName := int(0)
	tcp2ap := make(map[string]url.URL)

	// First, resolve the apurls
	for _, url := range apurls {
		tcpAddr, err := resolveTCPAddr("tcp", url.Host)
		if err != nil {
			return nil, err
		}
		tcp2ap[tcpAddr.String()] = url
	}

	stringParts := []string{}
	updateNodeMap := func(service, scheme string) error {
		_, addrs, err := lookupSRV(service, "tcp", dns)
		if err != nil {
			return err
		}
		for _, srv := range addrs {
			port := fmt.Sprintf("%d", srv.Port)
			host := net.JoinHostPort(srv.Target, port)
			tcpAddr, terr := resolveTCPAddr("tcp", host)
			if terr != nil {
				terr = err
				continue
			}
			n := ""
			url, ok := tcp2ap[tcpAddr.String()]
			if ok {
				n = name
			}
			if n == "" {
				n = fmt.Sprintf("%d", tempName)
				tempName++
			}
			// SRV records have a trailing dot but URL shouldn't.
			shortHost := strings.TrimSuffix(srv.Target, ".")
			urlHost := net.JoinHostPort(shortHost, port)
			stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost))
			if ok && url.Scheme != scheme {
				err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String())
			}
		}
		if len(stringParts) == 0 {
			return err
		}
		return nil
	}

	failCount := 0
	err := updateNodeMap(service+"-ssl", "https")
	srvErr := make([]string, 2)
	if err != nil {
		srvErr[0] = fmt.Sprintf("error querying DNS SRV records for _%s-ssl %s", service, err)
		failCount++
	}
	err = updateNodeMap(service, "http")
	if err != nil {
		srvErr[1] = fmt.Sprintf("error querying DNS SRV records for _%s %s", service, err)
		failCount++
	}
	if failCount == 2 {
		return nil, fmt.Errorf("srv: too many errors querying DNS SRV records (%q, %q)", srvErr[0], srvErr[1])
	}
	return stringParts, nil
}

type SRVClients struct {
	Endpoints []string
	SRVs      []*net.SRV
}

// GetClient looks up the client endpoints for a service and domain.
func GetClient(service, domain string) (*SRVClients, error) {
	var urls []*url.URL
	var srvs []*net.SRV

	updateURLs := func(service, scheme string) error {
		_, addrs, err := lookupSRV(service, "tcp", domain)
		if err != nil {
			return err
		}
		for _, srv := range addrs {
			urls = append(urls, &url.URL{
				Scheme: scheme,
				Host:   net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
			})
		}
		srvs = append(srvs, addrs...)
		return nil
	}

	errHTTPS := updateURLs(service+"-ssl", "https")
	errHTTP := updateURLs(service, "http")

	if errHTTPS != nil && errHTTP != nil {
		return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
	}

	endpoints := make([]string, len(urls))
	for i := range urls {
		endpoints[i] = urls[i].String()
	}
	return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil
}
<commit_msg>pkg/srv: fix error checks from resolveTCPAddr<commit_after>// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package srv

import (
	"fmt"
	"net"
	"net/url"
	"strings"

	"github.com/coreos/etcd/pkg/types"
)

var (
	// indirection for testing
	lookupSRV      = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict
	resolveTCPAddr = net.ResolveTCPAddr
)

// GetCluster gets the cluster information via DNS discovery.
// Also sees each entry as a separate instance.
func GetCluster(service, name, dns string, apurls types.URLs) ([]string, error) {
	tempName := int(0)
	tcp2ap := make(map[string]url.URL)

	// First, resolve the apurls
	for _, url := range apurls {
		tcpAddr, err := resolveTCPAddr("tcp", url.Host)
		if err != nil {
			return nil, err
		}
		tcp2ap[tcpAddr.String()] = url
	}

	stringParts := []string{}
	updateNodeMap := func(service, scheme string) error {
		_, addrs, err := lookupSRV(service, "tcp", dns)
		if err != nil {
			return err
		}
		for _, srv := range addrs {
			port := fmt.Sprintf("%d", srv.Port)
			host := net.JoinHostPort(srv.Target, port)
			tcpAddr, terr := resolveTCPAddr("tcp", host)
			if terr != nil {
				err = terr
				continue
			}
			n := ""
			url, ok := tcp2ap[tcpAddr.String()]
			if ok {
				n = name
			}
			if n == "" {
				n = fmt.Sprintf("%d", tempName)
				tempName++
			}
			// SRV records have a trailing dot but URL shouldn't.
			shortHost := strings.TrimSuffix(srv.Target, ".")
			urlHost := net.JoinHostPort(shortHost, port)
			stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost))
			if ok && url.Scheme != scheme {
				err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String())
			}
		}
		if len(stringParts) == 0 {
			return err
		}
		return nil
	}

	failCount := 0
	err := updateNodeMap(service+"-ssl", "https")
	srvErr := make([]string, 2)
	if err != nil {
		srvErr[0] = fmt.Sprintf("error querying DNS SRV records for _%s-ssl %s", service, err)
		failCount++
	}
	err = updateNodeMap(service, "http")
	if err != nil {
		srvErr[1] = fmt.Sprintf("error querying DNS SRV records for _%s %s", service, err)
		failCount++
	}
	if failCount == 2 {
		return nil, fmt.Errorf("srv: too many errors querying DNS SRV records (%q, %q)", srvErr[0], srvErr[1])
	}
	return stringParts, nil
}

type SRVClients struct {
	Endpoints []string
	SRVs      []*net.SRV
}

// GetClient looks up the client endpoints for a service and domain.
func GetClient(service, domain string) (*SRVClients, error) {
	var urls []*url.URL
	var srvs []*net.SRV

	updateURLs := func(service, scheme string) error {
		_, addrs, err := lookupSRV(service, "tcp", domain)
		if err != nil {
			return err
		}
		for _, srv := range addrs {
			urls = append(urls, &url.URL{
				Scheme: scheme,
				Host:   net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
			})
		}
		srvs = append(srvs, addrs...)
		return nil
	}

	errHTTPS := updateURLs(service+"-ssl", "https")
	errHTTP := updateURLs(service, "http")

	if errHTTPS != nil && errHTTP != nil {
		return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
	}

	endpoints := make([]string, len(urls))
	for i := range urls {
		endpoints[i] = urls[i].String()
	}
	return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil
}
<|endoftext|>


<commit_before>package main

import(
	"testing"
	"net/http"
)

func Test_newManagerWeb(t *testing.T) {
	mw, err := newManagerWeb()
	if err != nil {
		t.Error("Типовая ошибка при создании нового объекта.")
	}
	if mw == nil {
		t.Error("метод newManagerWeb() не должен возвращать nil.")
	}
}

func Test_Start(t *testing.T) {
	// создать менеджер веб-интерфейса
	mw, err := newManagerWeb()
	if err != nil || mw == nil {
		t.Error("Невозможно проверить создание сервера, т.к. невозможно создать объект Менеджер веб-интерфейса")
	}
	mw.Start()	// запустить сервер

	// запросить главную страницу
	request, err := http.NewRequest("GET", "http://localhost:8080", nil)
	res, err := http.DefaultClient.Do(request)
	if err != nil {
		t.Error(err)	// произошла ошибка пока отправлялся запрос
	}

	// статус ответа должен быть 200
	if res.StatusCode != 200 {
		t.Errorf("Получен код: %d", res.StatusCode)
	}
}
<commit_msg>В тестах добавлена дополнительная информация при возникновении ошибок.<commit_after>package main

import(
	"testing"
	"net/http"
)

func Test_newManagerWeb(t *testing.T) {
	mw, err := newManagerWeb()
	if err != nil {
		t.Errorf("Типовая ошибка при создании нового объекта. err = %v\n", err)
	}
	if mw == nil {
		t.Error("метод newManagerWeb() не должен возвращать nil.")
	}
}

func Test_Start(t *testing.T) {
	// создать менеджер веб-интерфейса
	mw, err := newManagerWeb()
	if err != nil || mw == nil {
		t.Errorf("Невозможно проверить создание сервера, т.к. невозможно создать объект Менеджер веб-интерфейса. err = %v\n", err)
	}
	mw.Start()	// запустить сервер

	// запросить главную страницу
	request, err := http.NewRequest("GET", "http://localhost:8080", nil)
	res, err := http.DefaultClient.Do(request)
	if err != nil {
		t.Error(err)	// произошла ошибка пока отправлялся запрос
	}

	// статус ответа должен быть 200
	if res.StatusCode != 200 {
		t.Errorf("Получен код: %d. Ожидается: 200.\n", res.StatusCode)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"log"
	"path"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
	"github.com/flynn/flynn/router/types"
)

type EtcdClient interface {
	Create(key string, value string, ttl uint64) (*etcd.Response, error)
	Set(key string, value string, ttl uint64) (*etcd.Response, error)
	Get(key string, sort, recursive bool) (*etcd.Response, error)
	Delete(key string, recursive bool) (*etcd.Response, error)
	Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error)
}

type DataStore interface {
	Add(route *strowger.Route) error
	Set(route *strowger.Route) error
	Get(id string) (*strowger.Route, error)
	List() ([]*strowger.Route, error)
	Remove(id string) error
	Sync(h SyncHandler, started chan<- error)
	StopSync()
}

type DataStoreReader interface {
	Get(id string) (*strowger.Route, error)
	List() ([]*strowger.Route, error)
}

type SyncHandler interface {
	Set(route *strowger.Route) error
	Remove(id string) error
}

func NewEtcdDataStore(etcd EtcdClient, prefix string) DataStore {
	return &etcdDataStore{
		prefix:   prefix,
		etcd:     etcd,
		stopSync: make(chan bool),
	}
}

type etcdDataStore struct {
	prefix   string
	etcd     EtcdClient
	stopSync chan bool
}

var ErrExists = errors.New("strowger: route already exists")
var ErrNotFound = errors.New("strowger: route not found")

func (s *etcdDataStore) Add(r *strowger.Route) error {
	data, err := json.Marshal(r)
	if err != nil {
		return err
	}
	_, err = s.etcd.Create(s.path(r.ID), string(data), 0)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 105 {
		err = ErrExists
	}
	return err
}

func (s *etcdDataStore) Set(r *strowger.Route) error {
	data, err := json.Marshal(r)
	if err != nil {
		return err
	}
	_, err = s.etcd.Set(s.path(r.ID), string(data), 0)
	return err
}

func (s *etcdDataStore) Remove(id string) error {
	_, err := s.etcd.Delete(s.path(id), true)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		return ErrNotFound
	}
	return err
}

func (s *etcdDataStore) Get(id string) (*strowger.Route, error) {
	res, err := s.etcd.Get(s.path(id), false, false)
	if err != nil {
		if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
			err = ErrNotFound
		}
		return nil, err
	}
	r := &strowger.Route{}
	err = json.Unmarshal([]byte(res.Node.Value), r)
	return r, err
}

func (s *etcdDataStore) List() ([]*strowger.Route, error) {
	res, err := s.etcd.Get(s.prefix, false, true)
	if err != nil {
		if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
			err = nil
		}
		return nil, err
	}

	routes := make([]*strowger.Route, len(res.Node.Nodes))
	for i, node := range res.Node.Nodes {
		r := &strowger.Route{}
		if err := json.Unmarshal([]byte(node.Value), r); err != nil {
			return nil, err
		}
		routes[i] = r
	}
	return routes, nil
}

func (s *etcdDataStore) path(id string) string {
	return s.prefix + path.Base(id)
}

func (s *etcdDataStore) Sync(h SyncHandler, started chan<- error) {
	var since uint64
	data, err := s.etcd.Get(s.prefix, false, true)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// key not found, ignore
		goto watch
	}
	if err != nil {
		started <- err
		return
	}
	since = data.EtcdIndex
	for _, node := range data.Node.Nodes {
		route := &strowger.Route{}
		if err := json.Unmarshal([]byte(node.Value), route); err != nil {
			started <- err
			return
		}
		if err := h.Set(route); err != nil {
			started <- err
			return
		}
	}

watch:
	started <- nil
	stream := make(chan *etcd.Response)
	go s.etcd.Watch(s.prefix, since, true, stream, s.stopSync)
	for res := range stream {
		id := path.Base(res.Node.Key)
		var err error
		if res.Action == "delete" {
			err = h.Remove(id)
		} else {
			route := &strowger.Route{}
			if err = json.Unmarshal([]byte(res.Node.Value), route); err != nil {
				goto fail
			}
			err = h.Set(route)
		}
	fail:
		if err != nil {
			log.Printf("Error while processing update from etcd: %s, %#v", err, res.Node)
		}
	}
}

func (s *etcdDataStore) StopSync() {
	close(s.stopSync)
}
<commit_msg>router: Fix off-by-one in etcd watch<commit_after>package main

import (
	"encoding/json"
	"errors"
	"log"
	"path"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
	"github.com/flynn/flynn/router/types"
)

type EtcdClient interface {
	Create(key string, value string, ttl uint64) (*etcd.Response, error)
	Set(key string, value string, ttl uint64) (*etcd.Response, error)
	Get(key string, sort, recursive bool) (*etcd.Response, error)
	Delete(key string, recursive bool) (*etcd.Response, error)
	Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error)
}

type DataStore interface {
	Add(route *strowger.Route) error
	Set(route *strowger.Route) error
	Get(id string) (*strowger.Route, error)
	List() ([]*strowger.Route, error)
	Remove(id string) error
	Sync(h SyncHandler, started chan<- error)
	StopSync()
}

type DataStoreReader interface {
	Get(id string) (*strowger.Route, error)
	List() ([]*strowger.Route, error)
}

type SyncHandler interface {
	Set(route *strowger.Route) error
	Remove(id string) error
}

func NewEtcdDataStore(etcd EtcdClient, prefix string) DataStore {
	return &etcdDataStore{
		prefix:   prefix,
		etcd:     etcd,
		stopSync: make(chan bool),
	}
}

type etcdDataStore struct {
	prefix   string
	etcd     EtcdClient
	stopSync chan bool
}

var ErrExists = errors.New("strowger: route already exists")
var ErrNotFound = errors.New("strowger: route not found")

func (s *etcdDataStore) Add(r *strowger.Route) error {
	data, err := json.Marshal(r)
	if err != nil {
		return err
	}
	_, err = s.etcd.Create(s.path(r.ID), string(data), 0)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 105 {
		err = ErrExists
	}
	return err
}

func (s *etcdDataStore) Set(r *strowger.Route) error {
	data, err := json.Marshal(r)
	if err != nil {
		return err
	}
	_, err = s.etcd.Set(s.path(r.ID), string(data), 0)
	return err
}

func (s *etcdDataStore) Remove(id string) error {
	_, err := s.etcd.Delete(s.path(id), true)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		return ErrNotFound
	}
	return err
}

func (s *etcdDataStore) Get(id string) (*strowger.Route, error) {
	res, err := s.etcd.Get(s.path(id), false, false)
	if err != nil {
		if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
			err = ErrNotFound
		}
		return nil, err
	}
	r := &strowger.Route{}
	err = json.Unmarshal([]byte(res.Node.Value), r)
	return r, err
}

func (s *etcdDataStore) List() ([]*strowger.Route, error) {
	res, err := s.etcd.Get(s.prefix, false, true)
	if err != nil {
		if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
			err = nil
		}
		return nil, err
	}

	routes := make([]*strowger.Route, len(res.Node.Nodes))
	for i, node := range res.Node.Nodes {
		r := &strowger.Route{}
		if err := json.Unmarshal([]byte(node.Value), r); err != nil {
			return nil, err
		}
		routes[i] = r
	}
	return routes, nil
}

func (s *etcdDataStore) path(id string) string {
	return s.prefix + path.Base(id)
}

func (s *etcdDataStore) Sync(h SyncHandler, started chan<- error) {
	var since uint64
	data, err := s.etcd.Get(s.prefix, false, true)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// key not found, ignore
		goto watch
	}
	if err != nil {
		started <- err
		return
	}
	since = data.EtcdIndex + 1
	for _, node := range data.Node.Nodes {
		route := &strowger.Route{}
		if err := json.Unmarshal([]byte(node.Value), route); err != nil {
			started <- err
			return
		}
		if err := h.Set(route); err != nil {
			started <- err
			return
		}
	}

watch:
	started <- nil
	stream := make(chan *etcd.Response)
	go s.etcd.Watch(s.prefix, since, true, stream, s.stopSync)
	for res := range stream {
		id := path.Base(res.Node.Key)
		var err error
		if res.Action == "delete" {
			err = h.Remove(id)
		} else {
			route := &strowger.Route{}
			if err = json.Unmarshal([]byte(res.Node.Value), route); err != nil {
				goto fail
			}
			err = h.Set(route)
		}
	fail:
		if err != nil {
			log.Printf("Error while processing update from etcd: %s, %#v", err, res.Node)
		}
	}
}

func (s *etcdDataStore) StopSync() {
	close(s.stopSync)
}
<|endoftext|>


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

package repo

import (
	"bytes"
	"fmt"
	gotemplate "html/template"
	"io/ioutil"
	"path"
	"strings"

	"github.com/Unknwon/paginater"
	"github.com/go-gitea/gitea/models"
	"github.com/go-gitea/gitea/modules/base"
	"github.com/go-gitea/gitea/modules/context"
	"github.com/go-gitea/gitea/modules/log"
	"github.com/go-gitea/gitea/modules/markdown"
	"github.com/go-gitea/gitea/modules/setting"
	"github.com/go-gitea/gitea/modules/template"
	"github.com/go-gitea/gitea/modules/template/highlight"
	"github.com/go-gitea/git"
)

const (
	HOME     base.TplName = "repo/home"
	WATCHERS base.TplName = "repo/watchers"
	FORKS    base.TplName = "repo/forks"
)

func renderDirectory(ctx *context.Context, treeLink string) {
	tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath)
	if err != nil {
		ctx.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err)
		return
	}

	entries, err := tree.ListEntries()
	if err != nil {
		ctx.Handle(500, "ListEntries", err)
		return
	}
	entries.Sort()

	ctx.Data["Files"], err = entries.GetCommitsInfo(ctx.Repo.Commit, ctx.Repo.TreePath)
	if err != nil {
		ctx.Handle(500, "GetCommitsInfo", err)
		return
	}

	var readmeFile *git.Blob
	for _, entry := range entries {
		if entry.IsDir() || !markdown.IsReadmeFile(entry.Name()) {
			continue
		}

		// TODO: collect all possible README files and show with priority.
		readmeFile = entry.Blob()
		break
	}

	if readmeFile != nil {
		ctx.Data["RawFileLink"] = ""
		ctx.Data["ReadmeInList"] = true
		ctx.Data["ReadmeExist"] = true

		dataRc, err := readmeFile.Data()
		if err != nil {
			ctx.Handle(500, "Data", err)
			return
		}

		buf := make([]byte, 1024)
		n, _ := dataRc.Read(buf)
		buf = buf[:n]

		isTextFile := base.IsTextFile(buf)
		ctx.Data["FileIsText"] = isTextFile
		ctx.Data["FileName"] = readmeFile.Name()
		// FIXME: what happens when README file is an image?
		if isTextFile {
			d, _ := ioutil.ReadAll(dataRc)
			buf = append(buf, d...)
			switch {
			case markdown.IsMarkdownFile(readmeFile.Name()):
				ctx.Data["IsMarkdown"] = true
				buf = markdown.Render(buf, treeLink, ctx.Repo.Repository.ComposeMetas())
			default:
				buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1)
			}
			ctx.Data["FileContent"] = string(buf)
		}
	}

	// Show latest commit info of repository in table header,
	// or of directory if not in root directory.
	latestCommit := ctx.Repo.Commit
	if len(ctx.Repo.TreePath) > 0 {
		latestCommit, err = ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath)
		if err != nil {
			ctx.Handle(500, "GetCommitByPath", err)
			return
		}
	}
	ctx.Data["LatestCommit"] = latestCommit
	ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)

	// Check permission to add or upload new file.
	if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch {
		ctx.Data["CanAddFile"] = true
		ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled
	}
}

func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) {
	ctx.Data["IsViewFile"] = true

	blob := entry.Blob()
	dataRc, err := blob.Data()
	if err != nil {
		ctx.Handle(500, "Data", err)
		return
	}

	ctx.Data["FileSize"] = blob.Size()
	ctx.Data["FileName"] = blob.Name()
	ctx.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name())
	ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath

	buf := make([]byte, 1024)
	n, _ := dataRc.Read(buf)
	buf = buf[:n]

	isTextFile := base.IsTextFile(buf)
	ctx.Data["IsTextFile"] = isTextFile

	// Assume file is not editable first.
	if !isTextFile {
		ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
	}

	switch {
	case isTextFile:
		if blob.Size() >= setting.UI.MaxDisplayFileSize {
			ctx.Data["IsFileTooLarge"] = true
			break
		}

		d, _ := ioutil.ReadAll(dataRc)
		buf = append(buf, d...)

		isMarkdown := markdown.IsMarkdownFile(blob.Name())
		ctx.Data["IsMarkdown"] = isMarkdown

		readmeExist := isMarkdown || markdown.IsReadmeFile(blob.Name())
		ctx.Data["ReadmeExist"] = readmeExist
		if readmeExist {
			// TODO: don't need to render if it's a README but not Markdown file.
			ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
		} else {
			// Building code view blocks with line number on server side.
			var fileContent string
			if err, content := template.ToUTF8WithErr(buf); err != nil {
				if err != nil {
					log.Error(4, "ToUTF8WithErr: %s", err)
				}
				fileContent = string(buf)
			} else {
				fileContent = content
			}

			var output bytes.Buffer
			lines := strings.Split(fileContent, "\n")
			for index, line := range lines {
				output.WriteString(fmt.Sprintf(`<li class="L%d" rel="L%d">%s</li>`, index+1, index+1, gotemplate.HTMLEscapeString(line)) + "\n")
			}
			ctx.Data["FileContent"] = gotemplate.HTML(output.String())

			output.Reset()
			for i := 0; i < len(lines); i++ {
				output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1))
			}
			ctx.Data["LineNums"] = gotemplate.HTML(output.String())
		}

		if ctx.Repo.CanEnableEditor() {
			ctx.Data["CanEditFile"] = true
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
		} else if !ctx.Repo.IsViewBranch {
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
		} else if !ctx.Repo.IsWriter() {
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
		}

	case base.IsPDFFile(buf):
		ctx.Data["IsPDFFile"] = true
	case base.IsImageFile(buf):
		ctx.Data["IsImageFile"] = true
	}

	if ctx.Repo.CanEnableEditor() {
		ctx.Data["CanDeleteFile"] = true
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file")
	} else if !ctx.Repo.IsViewBranch {
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
	} else if !ctx.Repo.IsWriter() {
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
	}
}

func Home(ctx *context.Context) {
	title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
	if len(ctx.Repo.Repository.Description) > 0 {
		title += ": " + ctx.Repo.Repository.Description
	}
	ctx.Data["Title"] = title
	ctx.Data["PageIsViewCode"] = true
	ctx.Data["RequireHighlightJS"] = true

	branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
	treeLink := branchLink
	rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchName

	if len(ctx.Repo.TreePath) > 0 {
		treeLink += "/" + ctx.Repo.TreePath
	}

	// Get current entry user currently looking at.
	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
	if err != nil {
		ctx.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err)
		return
	}

	if entry.IsDir() {
		renderDirectory(ctx, treeLink)
	} else {
		renderFile(ctx, entry, treeLink, rawLink)
	}
	if ctx.Written() {
		return
	}

	var treeNames []string
	paths := make([]string, 0, 5)
	if len(ctx.Repo.TreePath) > 0 {
		treeNames = strings.Split(ctx.Repo.TreePath, "/")
		for i := range treeNames {
			paths = append(paths, strings.Join(treeNames[:i+1], "/"))
		}

		ctx.Data["HasParentPath"] = true
		if len(paths)-2 >= 0 {
			ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
		}
	}

	ctx.Data["Paths"] = paths
	ctx.Data["TreeLink"] = treeLink
	ctx.Data["TreeNames"] = treeNames
	ctx.Data["BranchLink"] = branchLink
	ctx.HTML(200, HOME)
}

func RenderUserCards(ctx *context.Context, total int, getter func(page int) ([]*models.User, error), tpl base.TplName) {
	page := ctx.QueryInt("page")
	if page <= 0 {
		page = 1
	}
	pager := paginater.New(total, models.ItemsPerPage, page, 5)
	ctx.Data["Page"] = pager

	items, err := getter(pager.Current())
	if err != nil {
		ctx.Handle(500, "getter", err)
		return
	}
	ctx.Data["Cards"] = items

	ctx.HTML(200, tpl)
}

func Watchers(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.watchers")
	ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
	ctx.Data["PageIsWatchers"] = true
	RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, ctx.Repo.Repository.GetWatchers, WATCHERS)
}

func Stars(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.stargazers")
	ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
	ctx.Data["PageIsStargazers"] = true
	RenderUserCards(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers, WATCHERS)
}

func Forks(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repos.forks")

	forks, err := ctx.Repo.Repository.GetForks()
	if err != nil {
		ctx.Handle(500, "GetForks", err)
		return
	}

	for _, fork := range forks {
		if err = fork.GetOwner(); err != nil {
			ctx.Handle(500, "GetOwner", err)
			return
		}
	}
	ctx.Data["Forks"] = forks

	ctx.HTML(200, FORKS)
}
<commit_msg>Fix rendering of non-markdown readme files<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"bytes"
	"fmt"
	gotemplate "html/template"
	"io/ioutil"
	"path"
	"strings"

	"github.com/Unknwon/paginater"
	"github.com/go-gitea/gitea/models"
	"github.com/go-gitea/gitea/modules/base"
	"github.com/go-gitea/gitea/modules/context"
	"github.com/go-gitea/gitea/modules/log"
	"github.com/go-gitea/gitea/modules/markdown"
	"github.com/go-gitea/gitea/modules/setting"
	"github.com/go-gitea/gitea/modules/template"
	"github.com/go-gitea/gitea/modules/template/highlight"
	"github.com/go-gitea/git"
)

const (
	HOME     base.TplName = "repo/home"
	WATCHERS base.TplName = "repo/watchers"
	FORKS    base.TplName = "repo/forks"
)

func renderDirectory(ctx *context.Context, treeLink string) {
	tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath)
	if err != nil {
		ctx.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err)
		return
	}

	entries, err := tree.ListEntries()
	if err != nil {
		ctx.Handle(500, "ListEntries", err)
		return
	}
	entries.Sort()

	ctx.Data["Files"], err = entries.GetCommitsInfo(ctx.Repo.Commit, ctx.Repo.TreePath)
	if err != nil {
		ctx.Handle(500, "GetCommitsInfo", err)
		return
	}

	var readmeFile *git.Blob
	for _, entry := range entries {
		if entry.IsDir() || !markdown.IsReadmeFile(entry.Name()) {
			continue
		}

		// TODO: collect all possible README files and show with priority.
		readmeFile = entry.Blob()
		break
	}

	if readmeFile != nil {
		ctx.Data["RawFileLink"] = ""
		ctx.Data["ReadmeInList"] = true
		ctx.Data["ReadmeExist"] = true

		dataRc, err := readmeFile.Data()
		if err != nil {
			ctx.Handle(500, "Data", err)
			return
		}

		buf := make([]byte, 1024)
		n, _ := dataRc.Read(buf)
		buf = buf[:n]

		isTextFile := base.IsTextFile(buf)
		ctx.Data["FileIsText"] = isTextFile
		ctx.Data["FileName"] = readmeFile.Name()
		// FIXME: what happens when README file is an image?
		if isTextFile {
			d, _ := ioutil.ReadAll(dataRc)
			buf = append(buf, d...)
			switch {
			case markdown.IsMarkdownFile(readmeFile.Name()):
				ctx.Data["IsMarkdown"] = true
				buf = markdown.Render(buf, treeLink, ctx.Repo.Repository.ComposeMetas())
			default:
				// FIXME This is the only way to show non-markdown files
				// instead of a broken "View Raw" link
				ctx.Data["IsMarkdown"] = true
				buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1)
			}
			ctx.Data["FileContent"] = string(buf)
		}
	}

	// Show latest commit info of repository in table header,
	// or of directory if not in root directory.
	latestCommit := ctx.Repo.Commit
	if len(ctx.Repo.TreePath) > 0 {
		latestCommit, err = ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath)
		if err != nil {
			ctx.Handle(500, "GetCommitByPath", err)
			return
		}
	}
	ctx.Data["LatestCommit"] = latestCommit
	ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)

	// Check permission to add or upload new file.
	if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch {
		ctx.Data["CanAddFile"] = true
		ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled
	}
}

func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) {
	ctx.Data["IsViewFile"] = true

	blob := entry.Blob()
	dataRc, err := blob.Data()
	if err != nil {
		ctx.Handle(500, "Data", err)
		return
	}

	ctx.Data["FileSize"] = blob.Size()
	ctx.Data["FileName"] = blob.Name()
	ctx.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name())
	ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath

	buf := make([]byte, 1024)
	n, _ := dataRc.Read(buf)
	buf = buf[:n]

	isTextFile := base.IsTextFile(buf)
	ctx.Data["IsTextFile"] = isTextFile

	// Assume file is not editable first.
	if !isTextFile {
		ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
	}

	switch {
	case isTextFile:
		if blob.Size() >= setting.UI.MaxDisplayFileSize {
			ctx.Data["IsFileTooLarge"] = true
			break
		}

		d, _ := ioutil.ReadAll(dataRc)
		buf = append(buf, d...)

		isMarkdown := markdown.IsMarkdownFile(blob.Name())
		ctx.Data["IsMarkdown"] = isMarkdown

		readmeExist := isMarkdown || markdown.IsReadmeFile(blob.Name())
		ctx.Data["ReadmeExist"] = readmeExist
		if readmeExist && isMarkdown {
			ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
		} else {
			// Building code view blocks with line number on server side.
			var fileContent string
			if err, content := template.ToUTF8WithErr(buf); err != nil {
				if err != nil {
					log.Error(4, "ToUTF8WithErr: %s", err)
				}
				fileContent = string(buf)
			} else {
				fileContent = content
			}

			var output bytes.Buffer
			lines := strings.Split(fileContent, "\n")
			for index, line := range lines {
				output.WriteString(fmt.Sprintf(`<li class="L%d" rel="L%d">%s</li>`, index+1, index+1, gotemplate.HTMLEscapeString(line)) + "\n")
			}
			ctx.Data["FileContent"] = gotemplate.HTML(output.String())

			output.Reset()
			for i := 0; i < len(lines); i++ {
				output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1))
			}
			ctx.Data["LineNums"] = gotemplate.HTML(output.String())
		}

		if ctx.Repo.CanEnableEditor() {
			ctx.Data["CanEditFile"] = true
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
		} else if !ctx.Repo.IsViewBranch {
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
		} else if !ctx.Repo.IsWriter() {
			ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
		}

	case base.IsPDFFile(buf):
		ctx.Data["IsPDFFile"] = true
	case base.IsImageFile(buf):
		ctx.Data["IsImageFile"] = true
	}

	if ctx.Repo.CanEnableEditor() {
		ctx.Data["CanDeleteFile"] = true
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file")
	} else if !ctx.Repo.IsViewBranch {
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
	} else if !ctx.Repo.IsWriter() {
		ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
	}
}

func Home(ctx *context.Context) {
	title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
	if len(ctx.Repo.Repository.Description) > 0 {
		title += ": " + ctx.Repo.Repository.Description
	}
	ctx.Data["Title"] = title
	ctx.Data["PageIsViewCode"] = true
	ctx.Data["RequireHighlightJS"] = true

	branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
	treeLink := branchLink
	rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchName

	if len(ctx.Repo.TreePath) > 0 {
		treeLink += "/" + ctx.Repo.TreePath
	}

	// Get current entry user currently looking at.
	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
	if err != nil {
		ctx.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err)
		return
	}

	if entry.IsDir() {
		renderDirectory(ctx, treeLink)
	} else {
		renderFile(ctx, entry, treeLink, rawLink)
	}
	if ctx.Written() {
		return
	}

	var treeNames []string
	paths := make([]string, 0, 5)
	if len(ctx.Repo.TreePath) > 0 {
		treeNames = strings.Split(ctx.Repo.TreePath, "/")
		for i := range treeNames {
			paths = append(paths, strings.Join(treeNames[:i+1], "/"))
		}

		ctx.Data["HasParentPath"] = true
		if len(paths)-2 >= 0 {
			ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
		}
	}

	ctx.Data["Paths"] = paths
	ctx.Data["TreeLink"] = treeLink
	ctx.Data["TreeNames"] = treeNames
	ctx.Data["BranchLink"] = branchLink
	ctx.HTML(200, HOME)
}

func RenderUserCards(ctx *context.Context, total int, getter func(page int) ([]*models.User, error), tpl base.TplName) {
	page := ctx.QueryInt("page")
	if page <= 0 {
		page = 1
	}
	pager := paginater.New(total, models.ItemsPerPage, page, 5)
	ctx.Data["Page"] = pager

	items, err := getter(pager.Current())
	if err != nil {
		ctx.Handle(500, "getter", err)
		return
	}
	ctx.Data["Cards"] = items

	ctx.HTML(200, tpl)
}

func Watchers(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.watchers")
	ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
	ctx.Data["PageIsWatchers"] = true
	RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, ctx.Repo.Repository.GetWatchers, WATCHERS)
}

func Stars(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.stargazers")
	ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
	ctx.Data["PageIsStargazers"] = true
	RenderUserCards(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers, WATCHERS)
}

func Forks(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repos.forks")

	forks, err := ctx.Repo.Repository.GetForks()
	if err != nil {
		ctx.Handle(500, "GetForks", err)
		return
	}

	for _, fork := range forks {
		if err = fork.GetOwner(); err != nil {
			ctx.Handle(500, "GetOwner", err)
			return
		}
	}
	ctx.Data["Forks"] = forks

	ctx.HTML(200, FORKS)
}
<|endoftext|>


<commit_before>// Copyright 2012 Apcera Inc. All rights reserved.

package test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/apcera/gnatsd/server"
)

const natsServerExe = "../gnatsd"

type natsServer struct {
	args []string
	cmd  *exec.Cmd
}

// So we can pass tests and benchmarks..
type tLogger interface {
	Fatalf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
}

var defaultServerOptions = server.Options{
	Host:   "localhost",
	Port:   4222,
	Trace:  false,
	Debug:  false,
	NoLog:  true,
	NoSigs: true,
}

func runDefaultServer() *server.Server {
	return runServer(&defaultServerOptions)
}

// New Go Routine based server
func runServer(opts *server.Options) *server.Server {
	if opts == nil {
		opts = &defaultServerOptions
	}
	s := server.New(opts)
	if s == nil {
		panic("No nats server object returned.")
	}

	go s.AcceptLoop()

	// Make sure we are running and can bind before returning.
	addr := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	end := time.Now().Add(10 * time.Second)
	for time.Now().Before(end) {
		conn, err := net.Dial("tcp", addr)
		if err != nil {
			time.Sleep(50 * time.Millisecond)
			// Retry
			continue
		}
		conn.Close()
		return s
	}
	panic("Unable to start NATs Server in Go Routine")
	return nil
}

func startServer(t tLogger, port int, other string) *natsServer {
	var s natsServer
	args := fmt.Sprintf("-p %d %s", port, other)
	s.args = strings.Split(args, " ")
	s.cmd = exec.Command(natsServerExe, s.args...)
	err := s.cmd.Start()
	if err != nil {
		s.cmd = nil
		t.Errorf("Could not start <%s> [%s], is NATS installed and in path?", natsServerExe, err)
		return &s
	}
	// Give it time to start up
	start := time.Now()
	for {
		addr := fmt.Sprintf("localhost:%d", port)
		c, err := net.Dial("tcp", addr)
		if err != nil {
			time.Sleep(50 * time.Millisecond)
			if time.Since(start) > (5 * time.Second) {
				t.Fatalf("Timed out trying to connect to %s", natsServerExe)
				return nil
			}
		} else {
			c.Close()
			break
		}
	}
	return &s
}

func (s *natsServer) stopServer() {
	if s.cmd != nil && s.cmd.Process != nil {
		s.cmd.Process.Kill()
		s.cmd.Process.Wait()
	}
}

func createClientConn(t tLogger, host string, port int) net.Conn {
	addr := fmt.Sprintf("%s:%d", host, port)
	c, err := net.DialTimeout("tcp", addr, 1*time.Second)
	if err != nil {
		t.Fatalf("Could not connect to server: %v\n", err)
	}
	return c
}

func doConnect(t tLogger, c net.Conn, verbose, pedantic, ssl bool) {
	buf := expectResult(t, c, infoRe)
	js := infoRe.FindAllSubmatch(buf, 1)[0][1]
	var sinfo server.Info
	err := json.Unmarshal(js, &sinfo)
	if err != nil {
		t.Fatalf("Could not unmarshal INFO json: %v\n", err)
	}
	cs := fmt.Sprintf("CONNECT {\"verbose\":%v,\"pedantic\":%v,\"ssl_required\":%v}\r\n", verbose, pedantic, ssl)
	sendProto(t, c, cs)
}

func doDefaultConnect(t tLogger, c net.Conn) {
	// Basic Connect
	doConnect(t, c, false, false, false)
}

func setupConn(t tLogger, c net.Conn) (sendFun, expectFun) {
	doDefaultConnect(t, c)
	send := sendCommand(t, c)
	expect := expectCommand(t, c)
	return send, expect
}

type sendFun func(string)
type expectFun func(*regexp.Regexp) []byte

// Closure version for easier reading
func sendCommand(t tLogger, c net.Conn) sendFun {
	return func(op string) {
		sendProto(t, c, op)
	}
}

// Closure version for easier reading
func expectCommand(t tLogger, c net.Conn) expectFun {
	return func(re *regexp.Regexp) []byte {
		return expectResult(t, c, re)
	}
}

// Send the protocol command to the server.
func sendProto(t tLogger, c net.Conn, op string) {
	n, err := c.Write([]byte(op))
	if err != nil {
		t.Fatalf("Error writing command to conn: %v\n", err)
	}
	if n != len(op) {
		t.Fatalf("Partial write: %d vs %d\n", n, len(op))
	}
}

var (
	infoRe = regexp.MustCompile(`\AINFO\s+([^\r\n]+)\r\n`)
	pingRe = regexp.MustCompile(`\APING\r\n`)
	pongRe = regexp.MustCompile(`\APONG\r\n`)
	msgRe  = regexp.MustCompile(`(?:(?:MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n([^\\r\\n]*?)\r\n)+?)`)
	okRe   = regexp.MustCompile(`\A\+OK\r\n`)
	errRe  = regexp.MustCompile(`\A\-ERR\s+([^\r\n]+)\r\n`)
)

const (
	SUB_INDEX   = 1
	SID_INDEX   = 2
	REPLY_INDEX = 4
	LEN_INDEX   = 5
	MSG_INDEX   = 6
)

// Reuse expect buffer
var expBuf = make([]byte, 32768)

// Test result from server against regexp
func expectResult(t tLogger, c net.Conn, re *regexp.Regexp) []byte {
	// Wait for commands to be processed and results queued for read
	// time.Sleep(10 * time.Millisecond)
	c.SetReadDeadline(time.Now().Add(1 * time.Second))
	defer c.SetReadDeadline(time.Time{})

	n, err := c.Read(expBuf)
	if n <= 0 && err != nil {
		t.Fatalf("Error reading from conn: %v\n", err)
	}
	buf := expBuf[:n]

	if !re.Match(buf) {
		buf = bytes.Replace(buf, []byte("\r\n"), []byte("\\r\\n"), -1)
		t.Fatalf("Response did not match expected: \n\tReceived:'%s'\n\tExpected:'%s'\n", buf, re)
	}
	return buf
}

// This will check that we got what we expected.
func checkMsg(t tLogger, m [][]byte, subject, sid, reply, len, msg string) {
	if string(m[SUB_INDEX]) != subject {
		t.Fatalf("Did not get correct subject: expected '%s' got '%s'\n", subject, m[SUB_INDEX])
	}
	if string(m[SID_INDEX]) != sid {
		t.Fatalf("Did not get correct sid: exepected '%s' got '%s'\n", sid, m[SID_INDEX])
	}
	if string(m[REPLY_INDEX]) != reply {
		t.Fatalf("Did not get correct reply: exepected '%s' got '%s'\n", reply, m[REPLY_INDEX])
	}
	if string(m[LEN_INDEX]) != len {
		t.Fatalf("Did not get correct msg length: expected '%s' got '%s'\n", len, m[LEN_INDEX])
	}
	if string(m[MSG_INDEX]) != msg {
		t.Fatalf("Did not get correct msg: expected '%s' got '%s'\n", msg, m[MSG_INDEX])
	}
}

// Closure for expectMsgs
func expectMsgsCommand(t tLogger, ef expectFun) func(int) [][][]byte {
	return func(expected int) [][][]byte {
		buf := ef(msgRe)
		matches := msgRe.FindAllSubmatch(buf, -1)
		if len(matches) != expected {
			t.Fatalf("Did not get correct # msgs: %d vs %d\n", len(matches), expected)
		}
		return matches
	}
}
<commit_msg>Export running a test Go routine server<commit_after>// Copyright 2012 Apcera Inc. All rights reserved.

package test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/apcera/gnatsd/server"
)

const natsServerExe = "../gnatsd"

type natsServer struct {
	args []string
	cmd  *exec.Cmd
}

// So we can pass tests and benchmarks..
type tLogger interface {
	Fatalf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
}

var DefaultTestOptions = server.Options{
	Host:   "localhost",
	Port:   4222,
	NoSigs: true,
}

func runDefaultServer() *server.Server {
	return RunServer(&DefaultTestOptions)
}

// New Go Routine based server
func RunServer(opts *server.Options) *server.Server {
	if opts == nil {
		opts = &DefaultTestOptions
	}
	s := server.New(opts)
	if s == nil {
		panic("No nats server object returned.")
	}

	go s.AcceptLoop()

	// Make sure we are running and can bind before returning.
	addr := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	end := time.Now().Add(10 * time.Second)
	for time.Now().Before(end) {
		conn, err := net.Dial("tcp", addr)
		if err != nil {
			time.Sleep(50 * time.Millisecond)
			// Retry
			continue
		}
		conn.Close()
		return s
	}
	panic("Unable to start NATs Server in Go Routine")
}

func startServer(t tLogger, port int, other string) *natsServer {
	var s natsServer
	args := fmt.Sprintf("-p %d %s", port, other)
	s.args = strings.Split(args, " ")
	s.cmd = exec.Command(natsServerExe, s.args...)
	err := s.cmd.Start()
	if err != nil {
		s.cmd = nil
		t.Errorf("Could not start <%s> [%s], is NATS installed and in path?", natsServerExe, err)
		return &s
	}
	// Give it time to start up
	start := time.Now()
	for {
		addr := fmt.Sprintf("localhost:%d", port)
		c, err := net.Dial("tcp", addr)
		if err != nil {
			time.Sleep(50 * time.Millisecond)
			if time.Since(start) > (5 * time.Second) {
				t.Fatalf("Timed out trying to connect to %s", natsServerExe)
				return nil
			}
		} else {
			c.Close()
			break
		}
	}
	return &s
}

func (s *natsServer) stopServer() {
	if s.cmd != nil && s.cmd.Process != nil {
		s.cmd.Process.Kill()
		s.cmd.Process.Wait()
	}
}

func createClientConn(t tLogger, host string, port int) net.Conn {
	addr := fmt.Sprintf("%s:%d", host, port)
	c, err := net.DialTimeout("tcp", addr, 1*time.Second)
	if err != nil {
		t.Fatalf("Could not connect to server: %v\n", err)
	}
	return c
}

func doConnect(t tLogger, c net.Conn, verbose, pedantic, ssl bool) {
	buf := expectResult(t, c, infoRe)
	js := infoRe.FindAllSubmatch(buf, 1)[0][1]
	var sinfo server.Info
	err := json.Unmarshal(js, &sinfo)
	if err != nil {
		t.Fatalf("Could not unmarshal INFO json: %v\n", err)
	}
	cs := fmt.Sprintf("CONNECT {\"verbose\":%v,\"pedantic\":%v,\"ssl_required\":%v}\r\n", verbose, pedantic, ssl)
	sendProto(t, c, cs)
}

func doDefaultConnect(t tLogger, c net.Conn) {
	// Basic Connect
	doConnect(t, c, false, false, false)
}

func setupConn(t tLogger, c net.Conn) (sendFun, expectFun) {
	doDefaultConnect(t, c)
	send := sendCommand(t, c)
	expect := expectCommand(t, c)
	return send, expect
}

type sendFun func(string)
type expectFun func(*regexp.Regexp) []byte

// Closure version for easier reading
func sendCommand(t tLogger, c net.Conn) sendFun {
	return func(op string) {
		sendProto(t, c, op)
	}
}

// Closure version for easier reading
func expectCommand(t tLogger, c net.Conn) expectFun {
	return func(re *regexp.Regexp) []byte {
		return expectResult(t, c, re)
	}
}

// Send the protocol command to the server.
func sendProto(t tLogger, c net.Conn, op string) {
	n, err := c.Write([]byte(op))
	if err != nil {
		t.Fatalf("Error writing command to conn: %v\n", err)
	}
	if n != len(op) {
		t.Fatalf("Partial write: %d vs %d\n", n, len(op))
	}
}

var (
	infoRe = regexp.MustCompile(`\AINFO\s+([^\r\n]+)\r\n`)
	pingRe = regexp.MustCompile(`\APING\r\n`)
	pongRe = regexp.MustCompile(`\APONG\r\n`)
	msgRe  = regexp.MustCompile(`(?:(?:MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n([^\\r\\n]*?)\r\n)+?)`)
	okRe   = regexp.MustCompile(`\A\+OK\r\n`)
	errRe  = regexp.MustCompile(`\A\-ERR\s+([^\r\n]+)\r\n`)
)

const (
	SUB_INDEX   = 1
	SID_INDEX   = 2
	REPLY_INDEX = 4
	LEN_INDEX   = 5
	MSG_INDEX   = 6
)

// Reuse expect buffer
var expBuf = make([]byte, 32768)

// Test result from server against regexp
func expectResult(t tLogger, c net.Conn, re *regexp.Regexp) []byte {
	// Wait for commands to be processed and results queued for read
	// time.Sleep(10 * time.Millisecond)
	c.SetReadDeadline(time.Now().Add(1 * time.Second))
	defer c.SetReadDeadline(time.Time{})

	n, err := c.Read(expBuf)
	if n <= 0 && err != nil {
		t.Fatalf("Error reading from conn: %v\n", err)
	}
	buf := expBuf[:n]

	if !re.Match(buf) {
		buf = bytes.Replace(buf, []byte("\r\n"), []byte("\\r\\n"), -1)
		t.Fatalf("Response did not match expected: \n\tReceived:'%s'\n\tExpected:'%s'\n", buf, re)
	}
	return buf
}

// This will check that we got what we expected.
func checkMsg(t tLogger, m [][]byte, subject, sid, reply, len, msg string) {
	if string(m[SUB_INDEX]) != subject {
		t.Fatalf("Did not get correct subject: expected '%s' got '%s'\n", subject, m[SUB_INDEX])
	}
	if string(m[SID_INDEX]) != sid {
		t.Fatalf("Did not get correct sid: exepected '%s' got '%s'\n", sid, m[SID_INDEX])
	}
	if string(m[REPLY_INDEX]) != reply {
		t.Fatalf("Did not get correct reply: exepected '%s' got '%s'\n", reply, m[REPLY_INDEX])
	}
	if string(m[LEN_INDEX]) != len {
		t.Fatalf("Did not get correct msg length: expected '%s' got '%s'\n", len, m[LEN_INDEX])
	}
	if string(m[MSG_INDEX]) != msg {
		t.Fatalf("Did not get correct msg: expected '%s' got '%s'\n", msg, m[MSG_INDEX])
	}
}

// Closure for expectMsgs
func expectMsgsCommand(t tLogger, ef expectFun) func(int) [][][]byte {
	return func(expected int) [][][]byte {
		buf := ef(msgRe)
		matches := msgRe.FindAllSubmatch(buf, -1)
		if len(matches) != expected {
			t.Fatalf("Did not get correct # msgs: %d vs %d\n", len(matches), expected)
		}
		return matches
	}
}
<|endoftext|>


<commit_before>package test

import (
	"bytes"
)

func Fuzz(data []byte) int {
	if len(data) == 2 {
		if data[0] == '!' || data[0] == '#' {
			panic("bingo 0")
		}
		if data[0] == '0' || data[0] == '9' {
			for {
				c := make(chan bool)
				close(c)
			}
		}
		if data[0] == 'a' || data[0] == 'z' {
			data := make([]byte, 128<<30-1)
			_ = data
		}
		if data[0] == 'b' {
			// new coverage
		}
		if data[0] == 'c' {
			// new coverage
		}
	}
	if len(data) == 15 && bytes.HasPrefix(data, []byte("0123456789")) {
		x := int(data[10]) + int(data[11]) << 8 + int(data[12]) << 16 + int(data[13]) << 24
		if x == 0 || x == -1 {
			panic("bingo 1")
		}
		if x == 255 || x == 256 {
			for {
				c := make(chan bool)
				close(c)
			}
		}
		if x == 1<<16-1 || x == 1<<16 {
			data := make([]byte, 128<<30-1)
			_ = data
		}
		if x == '1' {
			// new coverage
		}
		if x == '2' {
			// new coverage
		}
	}
	return 0
}
<commit_msg>finally working version<commit_after>package test

import (
	"bytes"
)

func Fuzz(data []byte) int {
	if len(data) == 1 {
		if data[0] == '!' || data[0] == '#' {
			panic("bingo 0")
		}
		if data[0] == '0' || data[0] == '9' {
			for {
				c := make(chan bool)
				close(c)
			}
		}
		if data[0] == 'a' || data[0] == 'z' {
			data := make([]byte, 128<<30-1)
			_ = data
		}
		if data[0] == 'b' {
			// new coverage
		}
		if data[0] == 'c' {
			// new coverage
		}
	}
	if len(data) == 14 && bytes.HasPrefix(data, []byte("0123456789")) {
		x := int(data[10]) + int(data[11]) << 8 + int(data[12]) << 16 + int(data[13]) << 24
		if x == 0 || x == -1 {
			panic("bingo 1")
		}
		if x == 255 || x == 256 {
			for {
				c := make(chan bool)
				close(c)
			}
		}
		if x == 1<<16-1 || x == 1<<16 {
			data := make([]byte, 128<<30-1)
			_ = data
		}
		if x == '1' {
			// new coverage
		}
		if x == '2' {
			// new coverage
		}
	}
	return 0
}
<|endoftext|>


<commit_before>package tftp

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strings"

	"github.com/rancher/os/config/cloudinit/datasource"

	"github.com/pin/tftp"
)

type Client interface {
	Receive(filename string, mode string) (io.WriterTo, error)
}

type RemoteFile struct {
	host      string
	path      string
	client    Client
	stream    io.WriterTo
	lastError error
}

func NewDatasource(hostAndPath string) *RemoteFile {
	parts := strings.SplitN(hostAndPath, "/", 2)

	if len(parts) < 2 {
		return &RemoteFile{hostAndPath, "", nil, nil, nil}
	}

	host := parts[0]
	if match, _ := regexp.MatchString(":[0-9]{2,5}$", host); !match {
		// No port, using default port 69
		host += ":69"
	}

	path := parts[1]
	if client, lastError := tftp.NewClient(host); lastError == nil {
		return &RemoteFile{host, path, client, nil, nil}
	}

	return &RemoteFile{host, path, nil, nil, nil}
}

func (f *RemoteFile) IsAvailable() bool {
	f.stream, f.lastError = f.client.Receive(f.path, "octet")
	return f.lastError == nil
}

func (f *RemoteFile) Finish() error {
	return nil
}

func (f *RemoteFile) String() string {
	return fmt.Sprintf("%s: %s%s (lastError: %v)", f.Type(), f.host, f.path, f.lastError)
}

func (f *RemoteFile) AvailabilityChanges() bool {
	return false
}

func (f *RemoteFile) ConfigRoot() string {
	return ""
}

func (f *RemoteFile) FetchMetadata() (datasource.Metadata, error) {
	return datasource.Metadata{}, nil
}

func (f *RemoteFile) FetchUserdata() ([]byte, error) {
	var b bytes.Buffer

	_, err := f.stream.WriteTo(&b)

	return b.Bytes(), err
}

func (f *RemoteFile) Type() string {
	return "tftp"
}
<commit_msg>Fix logs output of the tftp datasource<commit_after>package tftp

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strings"

	"github.com/rancher/os/config/cloudinit/datasource"

	"github.com/pin/tftp"
)

type Client interface {
	Receive(filename string, mode string) (io.WriterTo, error)
}

type RemoteFile struct {
	host      string
	path      string
	client    Client
	stream    io.WriterTo
	lastError error
}

func NewDatasource(hostAndPath string) *RemoteFile {
	parts := strings.SplitN(hostAndPath, "/", 2)

	if len(parts) < 2 {
		return &RemoteFile{hostAndPath, "", nil, nil, nil}
	}

	host := parts[0]
	if match, _ := regexp.MatchString(":[0-9]{2,5}$", host); !match {
		// No port, using default port 69
		host += ":69"
	}

	path := parts[1]
	if client, lastError := tftp.NewClient(host); lastError == nil {
		return &RemoteFile{host, path, client, nil, nil}
	}

	return &RemoteFile{host, path, nil, nil, nil}
}

func (f *RemoteFile) IsAvailable() bool {
	f.stream, f.lastError = f.client.Receive(f.path, "octet")
	return f.lastError == nil
}

func (f *RemoteFile) Finish() error {
	return nil
}

func (f *RemoteFile) String() string {
	return fmt.Sprintf("%s, host:%s, path:%s (lastError: %v)", f.Type(), f.host, f.path, f.lastError)
}

func (f *RemoteFile) AvailabilityChanges() bool {
	return false
}

func (f *RemoteFile) ConfigRoot() string {
	return ""
}

func (f *RemoteFile) FetchMetadata() (datasource.Metadata, error) {
	return datasource.Metadata{}, nil
}

func (f *RemoteFile) FetchUserdata() ([]byte, error) {
	var b bytes.Buffer

	_, err := f.stream.WriteTo(&b)

	return b.Bytes(), err
}

func (f *RemoteFile) Type() string {
	return "tftp"
}
<|endoftext|>