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

package oracle_test

import (
	"errors"
	"fmt"
	"time"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	envtesting "github.com/juju/juju/environs/testing"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/provider/oracle"
	"github.com/juju/juju/testing"
	"github.com/juju/juju/tools"
	gitjujutesting "github.com/juju/testing"
	"github.com/juju/utils/arch"
	//"github.com/juju/utils/clock"
	"github.com/juju/version"
	gc "gopkg.in/check.v1"
)

type environSuite struct{}

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

// shamelessly copied from one of the OpenStack tests
var clk = gitjujutesting.NewClock(time.Time{})
var advancingClock = gitjujutesting.AutoAdvancingClock{clk, clk.Advance}

func (e *environSuite) TestNewOracleEnviron(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)
}

func (e *environSuite) TestAvailabilityZone(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	zones, err := environ.AvailabilityZones()
	c.Assert(err, gc.IsNil)
	c.Assert(zones, gc.NotNil)
}

func (e *environSuite) TestInstanceAvailabilityZoneNames(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	zones, err := environ.InstanceAvailabilityZoneNames([]instance.Id{
		instance.Id("0"),
	})
	c.Assert(err, gc.IsNil)
	c.Assert(zones, gc.NotNil)
}

func (e *environSuite) TestInstanceAvailabilityZoneNamesWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeInstancer: FakeInstancer{
				InstanceErr: errors.New("FakeInstanceErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.InstanceAvailabilityZoneNames([]instance.Id{instance.Id("0")})
	c.Assert(err, gc.NotNil)

	environ, err = oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeInstance: FakeInstance{
				AllErr: errors.New("FakeInstanceErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.InstanceAvailabilityZoneNames([]instance.Id{
		instance.Id("0"),
		instance.Id("1"),
	})
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestPrepareForBootstrap(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	err = environ.PrepareForBootstrap(ctx)
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestPrepareForBootstrapWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeAuthenticater: FakeAuthenticater{
				AuthenticateErr: errors.New("FakeAuthenticateErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	err = environ.PrepareForBootstrap(ctx)
	c.Assert(err, gc.NotNil)
}

func makeToolsList(series string) tools.List {
	var toolsVersion version.Binary
	toolsVersion.Number = version.MustParse("1.26.0")
	toolsVersion.Arch = arch.AMD64
	toolsVersion.Series = series
	return tools.List{{
		Version: toolsVersion,
		URL:     fmt.Sprintf("http://example.com/tools/juju-%s.tgz", toolsVersion),
		SHA256:  "1234567890abcdef",
		Size:    1024,
	}}
}

func (e *environSuite) TestBootstrap(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
        &advancingClock,
		//clock.WallClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	_, err = environ.Bootstrap(ctx,
		environs.BootstrapParams{
			ControllerConfig:     testing.FakeControllerConfig(),
			AvailableTools:       makeToolsList("xenial"),
			BootstrapSeries:      "xenial",
			BootstrapConstraints: constraints.MustParse("mem=3.5G"),
		})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestCreate(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Create(environs.CreateParams{
		ControllerUUID: "dsauhdiuashd",
	})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestCreateWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeAuthenticater: FakeAuthenticater{
				AuthenticateErr: errors.New("FakeAuthenticateErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Create(environs.CreateParams{
		ControllerUUID: "daushdasd",
	})
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestAdoptResources(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.AdoptResources("", version.Number{})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestStopInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ids := []instance.Id{instance.Id("0")}
	err = environ.StopInstances(ids...)
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestAllInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.AllInstances()
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestMaintainInstance(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.MaintainInstance(environs.StartInstanceParams{})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestConfig(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	cfg := environ.Config()
	c.Assert(cfg, gc.NotNil)
}

func (e *environSuite) TestConstraintsValidator(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	validator, err := environ.ConstraintsValidator()
	c.Assert(err, gc.IsNil)
	c.Assert(validator, gc.NotNil)
}

func (e *environSuite) TestSetConfig(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.SetConfig(testing.ModelConfig(c))
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	instances, err := environ.Instances([]instance.Id{instance.Id("0")})
	c.Assert(err, gc.IsNil)
	c.Assert(instances, gc.NotNil)
}

func (e *environSuite) TestConstrollerInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	instances, err := environ.ControllerInstances("23123-3123-12312")
	c.Assert(err, gc.Equals, environs.ErrNoInstances)
	c.Assert(instances, gc.IsNil)
}

func (e *environSuite) TestDestroy(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Destroy()
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestDestroyController(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.DestroyController("231233-312-321-3312")
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestProvider(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	p := environ.Provider()
	c.Assert(p, gc.NotNil)
}

func (e *environSuite) TestPrecheckInstnace(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.PrecheckInstance("", constraints.Value{}, "")
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestInstanceTypes(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	types, err := environ.InstanceTypes(constraints.Value{})
	c.Assert(err, gc.IsNil)
	c.Assert(types, gc.NotNil)
}
<commit_msg>go fmt<commit_after>// Copyright 2017 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package oracle_test

import (
	"errors"
	"fmt"
	"time"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	envtesting "github.com/juju/juju/environs/testing"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/provider/oracle"
	"github.com/juju/juju/testing"
	"github.com/juju/juju/tools"
	gitjujutesting "github.com/juju/testing"
	"github.com/juju/utils/arch"
	//"github.com/juju/utils/clock"
	"github.com/juju/version"
	gc "gopkg.in/check.v1"
)

type environSuite struct{}

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

// shamelessly copied from one of the OpenStack tests
var clk = gitjujutesting.NewClock(time.Time{})
var advancingClock = gitjujutesting.AutoAdvancingClock{clk, clk.Advance}

func (e *environSuite) TestNewOracleEnviron(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)
}

func (e *environSuite) TestAvailabilityZone(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	zones, err := environ.AvailabilityZones()
	c.Assert(err, gc.IsNil)
	c.Assert(zones, gc.NotNil)
}

func (e *environSuite) TestInstanceAvailabilityZoneNames(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	zones, err := environ.InstanceAvailabilityZoneNames([]instance.Id{
		instance.Id("0"),
	})
	c.Assert(err, gc.IsNil)
	c.Assert(zones, gc.NotNil)
}

func (e *environSuite) TestInstanceAvailabilityZoneNamesWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeInstancer: FakeInstancer{
				InstanceErr: errors.New("FakeInstanceErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.InstanceAvailabilityZoneNames([]instance.Id{instance.Id("0")})
	c.Assert(err, gc.NotNil)

	environ, err = oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeInstance: FakeInstance{
				AllErr: errors.New("FakeInstanceErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.InstanceAvailabilityZoneNames([]instance.Id{
		instance.Id("0"),
		instance.Id("1"),
	})
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestPrepareForBootstrap(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	err = environ.PrepareForBootstrap(ctx)
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestPrepareForBootstrapWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeAuthenticater: FakeAuthenticater{
				AuthenticateErr: errors.New("FakeAuthenticateErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	err = environ.PrepareForBootstrap(ctx)
	c.Assert(err, gc.NotNil)
}

func makeToolsList(series string) tools.List {
	var toolsVersion version.Binary
	toolsVersion.Number = version.MustParse("1.26.0")
	toolsVersion.Arch = arch.AMD64
	toolsVersion.Series = series
	return tools.List{{
		Version: toolsVersion,
		URL:     fmt.Sprintf("http://example.com/tools/juju-%s.tgz", toolsVersion),
		SHA256:  "1234567890abcdef",
		Size:    1024,
	}}
}

func (e *environSuite) TestBootstrap(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
		//clock.WallClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ctx := envtesting.BootstrapContext(c)
	_, err = environ.Bootstrap(ctx,
		environs.BootstrapParams{
			ControllerConfig:     testing.FakeControllerConfig(),
			AvailableTools:       makeToolsList("xenial"),
			BootstrapSeries:      "xenial",
			BootstrapConstraints: constraints.MustParse("mem=3.5G"),
		})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestCreate(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Create(environs.CreateParams{
		ControllerUUID: "dsauhdiuashd",
	})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestCreateWithErrors(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		FakeEnvironAPI{
			FakeAuthenticater: FakeAuthenticater{
				AuthenticateErr: errors.New("FakeAuthenticateErr"),
			},
		},
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Create(environs.CreateParams{
		ControllerUUID: "daushdasd",
	})
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestAdoptResources(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.AdoptResources("", version.Number{})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestStopInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	ids := []instance.Id{instance.Id("0")}
	err = environ.StopInstances(ids...)
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestAllInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	_, err = environ.AllInstances()
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestMaintainInstance(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.MaintainInstance(environs.StartInstanceParams{})
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestConfig(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	cfg := environ.Config()
	c.Assert(cfg, gc.NotNil)
}

func (e *environSuite) TestConstraintsValidator(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	validator, err := environ.ConstraintsValidator()
	c.Assert(err, gc.IsNil)
	c.Assert(validator, gc.NotNil)
}

func (e *environSuite) TestSetConfig(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.SetConfig(testing.ModelConfig(c))
	c.Assert(err, gc.NotNil)
}

func (e *environSuite) TestInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	instances, err := environ.Instances([]instance.Id{instance.Id("0")})
	c.Assert(err, gc.IsNil)
	c.Assert(instances, gc.NotNil)
}

func (e *environSuite) TestConstrollerInstances(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	instances, err := environ.ControllerInstances("23123-3123-12312")
	c.Assert(err, gc.Equals, environs.ErrNoInstances)
	c.Assert(instances, gc.IsNil)
}

func (e *environSuite) TestDestroy(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.Destroy()
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestDestroyController(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.DestroyController("231233-312-321-3312")
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestProvider(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	p := environ.Provider()
	c.Assert(p, gc.NotNil)
}

func (e *environSuite) TestPrecheckInstnace(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	err = environ.PrecheckInstance("", constraints.Value{}, "")
	c.Assert(err, gc.IsNil)
}

func (e *environSuite) TestInstanceTypes(c *gc.C) {
	environ, err := oracle.NewOracleEnviron(
		oracle.DefaultProvider,
		environs.OpenParams{
			Config: testing.ModelConfig(c),
		},
		DefaultEnvironAPI,
		&advancingClock,
	)
	c.Assert(err, gc.IsNil)
	c.Assert(environ, gc.NotNil)

	types, err := environ.InstanceTypes(constraints.Value{})
	c.Assert(err, gc.IsNil)
	c.Assert(types, gc.NotNil)
}
<|endoftext|>


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

package docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/tsuru/config"
)

func clientWithTimeout(timeout time.Duration) *http.Client {
	dialTimeout := func(network, addr string) (net.Conn, error) {
		return net.DialTimeout(network, addr, timeout)
	}
	transport := http.Transport{
		Dial: dialTimeout,
	}
	return &http.Client{
		Transport: &transport,
	}
}

var timeoutHttpClient = clientWithTimeout(5 * time.Second)

func runHealthcheck(cont *container, w io.Writer) error {
	yamlData, err := getImageTsuruYamlData(cont.Image)
	if err != nil {
		return err
	}
	path := yamlData.Healthcheck.Path
	method := yamlData.Healthcheck.Method
	match := yamlData.Healthcheck.Match
	status := yamlData.Healthcheck.Status
	allowedFailures := yamlData.Healthcheck.AllowedFailures
	if path == "" {
		return nil
	}
	path = strings.TrimSpace(strings.TrimLeft(path, "/"))
	if method == "" {
		method = "get"
	}
	method = strings.ToUpper(method)
	if status == 0 && match == "" {
		status = 200
	}
	var matchRE *regexp.Regexp
	if match != "" {
		match = "(?s)" + match
		matchRE, err = regexp.Compile(match)
		if err != nil {
			return err
		}
	}
	maxWaitTime, _ := config.GetInt("docker:healthcheck:max-time")
	if maxWaitTime == 0 {
		maxWaitTime = 120
	}
	maxWaitTime = maxWaitTime * int(time.Second)
	sleepTime := 3 * time.Second
	startedTime := time.Now()
	url := fmt.Sprintf("http://%s:%s/%s", cont.HostAddr, cont.HostPort, path)
	for {
		var lastError error = nil
		req, err := http.NewRequest(method, url, nil)
		if err != nil {
			return err
		}
		rsp, err := timeoutHttpClient.Do(req)
		if err != nil {
			lastError = fmt.Errorf("healthcheck fail(%s): %s", cont.shortID(), err.Error())
		} else {
			defer rsp.Body.Close()
			if status != 0 && rsp.StatusCode != status {
				lastError = fmt.Errorf("healthcheck fail(%s): wrong status code, expected %d, got: %d", cont.shortID(), status, rsp.StatusCode)
			} else if matchRE != nil {
				result, err := ioutil.ReadAll(rsp.Body)

				if err != nil {
					lastError = err
				}
				if !matchRE.Match(result) {
					lastError = fmt.Errorf("healthcheck fail(%s): unexpected result, expected %q, got: %s", cont.shortID(), match, string(result))
				}
			}
			if lastError != nil {
				if allowedFailures == 0 {
					return lastError
				}
				allowedFailures--
			}
		}
		if lastError == nil {
			fmt.Fprintf(w, " ---> healthcheck successful(%s)\n", cont.shortID())
			return nil
		}
		if time.Now().Sub(startedTime) > time.Duration(maxWaitTime) {
			return lastError
		}
		fmt.Fprintf(w, " ---> %s. Trying again in %ds\n", lastError.Error(), sleepTime/time.Second)
		time.Sleep(sleepTime)
	}
}
<commit_msg>provision/docker: set maximum timeout for healthcheck requests<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/tsuru/config"
)

func clientWithTimeout(dialTimeout time.Duration) *http.Client {
	transport := http.Transport{
		Dial: (&net.Dialer{
			Timeout:   dialTimeout,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: dialTimeout,
	}
	return &http.Client{
		Transport: &transport,
		Timeout:   1 * time.Minute,
	}
}

var timeoutHttpClient = clientWithTimeout(5 * time.Second)

func runHealthcheck(cont *container, w io.Writer) error {
	yamlData, err := getImageTsuruYamlData(cont.Image)
	if err != nil {
		return err
	}
	path := yamlData.Healthcheck.Path
	method := yamlData.Healthcheck.Method
	match := yamlData.Healthcheck.Match
	status := yamlData.Healthcheck.Status
	allowedFailures := yamlData.Healthcheck.AllowedFailures
	if path == "" {
		return nil
	}
	path = strings.TrimSpace(strings.TrimLeft(path, "/"))
	if method == "" {
		method = "get"
	}
	method = strings.ToUpper(method)
	if status == 0 && match == "" {
		status = 200
	}
	var matchRE *regexp.Regexp
	if match != "" {
		match = "(?s)" + match
		matchRE, err = regexp.Compile(match)
		if err != nil {
			return err
		}
	}
	maxWaitTime, _ := config.GetInt("docker:healthcheck:max-time")
	if maxWaitTime == 0 {
		maxWaitTime = 120
	}
	maxWaitTime = maxWaitTime * int(time.Second)
	sleepTime := 3 * time.Second
	startedTime := time.Now()
	url := fmt.Sprintf("http://%s:%s/%s", cont.HostAddr, cont.HostPort, path)
	for {
		var lastError error = nil
		req, err := http.NewRequest(method, url, nil)
		if err != nil {
			return err
		}
		rsp, err := timeoutHttpClient.Do(req)
		if err != nil {
			lastError = fmt.Errorf("healthcheck fail(%s): %s", cont.shortID(), err.Error())
		} else {
			defer rsp.Body.Close()
			if status != 0 && rsp.StatusCode != status {
				lastError = fmt.Errorf("healthcheck fail(%s): wrong status code, expected %d, got: %d", cont.shortID(), status, rsp.StatusCode)
			} else if matchRE != nil {
				result, err := ioutil.ReadAll(rsp.Body)

				if err != nil {
					lastError = err
				}
				if !matchRE.Match(result) {
					lastError = fmt.Errorf("healthcheck fail(%s): unexpected result, expected %q, got: %s", cont.shortID(), match, string(result))
				}
			}
			if lastError != nil {
				if allowedFailures == 0 {
					return lastError
				}
				allowedFailures--
			}
		}
		if lastError == nil {
			fmt.Fprintf(w, " ---> healthcheck successful(%s)\n", cont.shortID())
			return nil
		}
		if time.Now().Sub(startedTime) > time.Duration(maxWaitTime) {
			return lastError
		}
		fmt.Fprintf(w, " ---> %s. Trying again in %ds\n", lastError.Error(), sleepTime/time.Second)
		time.Sleep(sleepTime)
	}
}
<|endoftext|>


<commit_before>//  Copyright (c) 2018 Rackspace
//
//  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.

// To use with keystone, create s3 style credentials
//   `openstack ec2 credentials create`
//
// To enable, add the following to `/etc/hummingbird/proxy-server.conf`
//
//   [filter:s3api]
//   enabled = true
//
// Example using boto2 and haio with tempauth:
//
//  from boto.s3.connection import S3Connection
//  connection = S3Connection(
//    aws_access_key_id="test:tester",
//    aws_secret_access_key="testing",
//    port=8080,
//    host='127.0.0.1',
//    is_secure=False,
//    calling_format=boto.s3.connection.OrdinaryCallingFormat()
//  )
//  connection.get_all_buckets()
//
//  If you are using keystone auth, substitue the key and access key returned from keystone

package middleware

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/troubling/hummingbird/accountserver"
	"github.com/troubling/hummingbird/common/conf"
	"github.com/troubling/hummingbird/common/srv"
	"github.com/uber-go/tally"
)

const (
	s3Xmlns = "http://s3.amazonaws.com/doc/2006-03-01"
)

type s3Response struct {
	Code    string
	Message string
}

var s3Responses = map[int]s3Response{
	// NOTE: These are meant to be generic responses
	403: {"AccessDenied", "Access Denied"},
	501: {"NotImplemented", "A header you provided implies functionality that is not implemented."},
	500: {"InternalError", "We encountered an internal error. Please try again."},
	503: {"ServiceUnavailable", "Reduce your request rate."},
	411: {"MissingContentLength", "You must provide the Content-Length HTTP header."},
}

type s3Owner struct {
	ID          string `xml:"ID"`
	DisplayName string `xml:"DisplayName"`
}

type s3BucketInfo struct {
	Name         string `xml:"Name"`
	CreationDate string `xml:"CreationDate"`
}

type s3BucketList struct {
	XMLName xml.Name       `xml:"ListAllMyBucketsResult"`
	Xmlns   string         `xml:"xmlns,attr"`
	Owner   s3Owner        `xml:"Owner"`
	Buckets []s3BucketInfo `xml:"Buckets>Bucket"`
}

func NewS3BucketList() *s3BucketList {
	return &s3BucketList{Xmlns: s3Xmlns}
}

type s3Error struct {
	XMLName   xml.Name `xml:"Error"`
	Code      string   `xml:"Code"`
	Message   string   `xml:"Message"`
	Resource  string   `xml:"Resource"`
	RequestId string   `xml:"RequestId"`
}

func NewS3Error() *s3Error {
	return &s3Error{}
}

// This will wrap http.ResponseWriter to support s3 style xml responses on errors
// TODO: This may still need some work for more specific error responses
type s3ResponseWriterWrapper struct {
	writer    http.ResponseWriter
	hijack    bool
	resource  string
	requestId string
	msg       []byte
}

func newS3ResponseWriterWrapper(w http.ResponseWriter, r *http.Request) *s3ResponseWriterWrapper {
	ctx := GetProxyContext(r)
	return &s3ResponseWriterWrapper{
		writer:    w,
		hijack:    false,
		resource:  r.URL.Path,
		requestId: ctx.TxId,
	}
}

func (w *s3ResponseWriterWrapper) Header() http.Header {
	return w.writer.Header()
}

func (w *s3ResponseWriterWrapper) WriteHeader(statusCode int) {
	if statusCode/100 != 2 {
		// We are going to hijack to return an S3 style result
		w.hijack = true
		if statusCode == 401 {
			statusCode = 403 // S3 returns 403 instead of 401
		}
		msg := NewS3Error()
		msg.Code = s3Responses[statusCode].Code
		msg.Message = s3Responses[statusCode].Message
		msg.Resource = w.resource
		msg.RequestId = w.requestId
		output, err := xml.MarshalIndent(msg, "", "  ")
		if err != nil {
			w.hijack = false
			w.WriteHeader(500)
		}
		output = []byte(xml.Header + string(output))
		headers := w.writer.Header()
		headers.Set("Content-Type", "application/xml; charset=utf-8")
		headers.Set("Content-Length", strconv.Itoa(len(output)))
		w.msg = output
	}
	w.writer.WriteHeader(statusCode)
}

func (w *s3ResponseWriterWrapper) Write(buf []byte) (int, error) {
	if !w.hijack {
		return w.writer.Write(buf)
	} else {
		n, err := w.writer.Write(w.msg)
		return n, err
	}
}

type s3ApiHandler struct {
	next           http.Handler
	ctx            *ProxyContext
	account        string
	container      string
	object         string
	path           string
	signature      string
	requestsMetric tally.Counter
}

func (s *s3ApiHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)
	// Check if this is an S3 request
	if ctx.S3Auth == nil || strings.HasPrefix(strings.ToLower(request.URL.Path), "/v1/") {
		// Not an S3 request
		s.next.ServeHTTP(writer, request)
		return
	}

	s.container, s.object, _, _ = getPathSegments(request.URL.Path)
	s.account = ctx.S3Auth.Account

	// TODO: Validate the container
	// Generate the hbird api path
	if s.object != "" {
		s.path = fmt.Sprintf("/v1/AUTH_%s/%s/%s", s.account, s.container, s.object)
	} else if s.container != "" {
		s.path = fmt.Sprintf("/v1/AUTH_%s/%s", s.account, s.container)
	} else {
		s.path = fmt.Sprintf("/v1/AUTH_%s", s.account)
	}
	// TODO: Handle metadata?

	if s.object != "" {
		s.handleObjectRequest(writer, request)
		return
	} else if s.container != "" {
		s.handleContainerRequest(writer, request)
		return
	} else {
		s.handleAccountRequest(writer, request)
		return
	}
}

func (s *s3ApiHandler) handleObjectRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)

	if request.Method == "PUT" {
		newReq, err := ctx.newSubrequest("PUT", s.path, request.Body, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Content-Length", request.Header.Get("Content-Length"))
		newReq.Header.Set("Content-Type", request.Header.Get("Content-Type"))
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.Header().Set("ETag", "\""+cap.Header().Get("ETag")+"\"")
			writer.Header().Set("Content-Length", cap.Header().Get("Content-Length"))
			writer.WriteHeader(200)
			return
		}
	}

	// If we didn't get to anything, then return no implemented
	srv.SimpleErrorResponse(writer, http.StatusNotImplemented, "Not Implemented")
}

func (s *s3ApiHandler) handleContainerRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)

	if request.Method == "DELETE" {
		newReq, err := ctx.newSubrequest("DELETE", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.WriteHeader(204)
			return
		}
	}

	if request.Method == "PUT" {
		newReq, err := ctx.newSubrequest("PUT", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.WriteHeader(200)
			return
		}
	}

	// If we didn't get to do anything, then return not implemented
	srv.SimpleErrorResponse(writer, http.StatusNotImplemented, "Not Implemented")
}

func (s *s3ApiHandler) handleAccountRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)
	if request.Method == "GET" {
		newReq, err := ctx.newSubrequest("GET", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		}
		containerListing := []accountserver.ContainerListingRecord{}
		err = json.Unmarshal(cap.body, &containerListing)
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		bucketList := NewS3BucketList()
		bucketList.Owner.ID = ctx.S3Auth.Account
		bucketList.Owner.DisplayName = ctx.S3Auth.Account
		// NOTE: The container list api doesn't have a creation date for the container, so we use an "arbitrary" date.
		for _, c := range containerListing {
			bucketList.Buckets = append(bucketList.Buckets, s3BucketInfo{
				Name:         c.Name,
				CreationDate: "2009-02-03T16:45:09.000Z",
			})
		}
		output, err := xml.MarshalIndent(bucketList, "", "  ")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		output = []byte(xml.Header + string(output))
		headers := writer.Header()
		headers.Set("Content-Type", "application/xml; charset=utf-8")
		headers.Set("Content-Length", strconv.Itoa(len(output)))
		writer.WriteHeader(200)
		writer.Write(output)
		return

	}

	// No other methods are allowed at the account level
	srv.StandardResponse(writer, http.StatusMethodNotAllowed)
}

func NewS3Api(config conf.Section, metricsScope tally.Scope) (func(http.Handler) http.Handler, error) {
	enabled, ok := config.Section["enabled"]
	if !ok || strings.Compare(strings.ToLower(enabled), "false") == 0 {
		// s3api is disabled, so pass the request on
		return func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
				next.ServeHTTP(writer, request)
			})
		}, nil
	}
	RegisterInfo("s3Api", map[string]interface{}{})
	return s3Api(metricsScope.Counter("s3Api_requests")), nil
}

func s3Api(requestsMetric tally.Counter) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			(&s3ApiHandler{next: next, requestsMetric: requestsMetric}).ServeHTTP(writer, request)
		})
	}
}
<commit_msg>S3 needs its own path split<commit_after>//  Copyright (c) 2018 Rackspace
//
//  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.

// To use with keystone, create s3 style credentials
//   `openstack ec2 credentials create`
//
// To enable, add the following to `/etc/hummingbird/proxy-server.conf`
//
//   [filter:s3api]
//   enabled = true
//
// Example using boto2 and haio with tempauth:
//
//  from boto.s3.connection import S3Connection
//  connection = S3Connection(
//    aws_access_key_id="test:tester",
//    aws_secret_access_key="testing",
//    port=8080,
//    host='127.0.0.1',
//    is_secure=False,
//    calling_format=boto.s3.connection.OrdinaryCallingFormat()
//  )
//  connection.get_all_buckets()
//
//  If you are using keystone auth, substitue the key and access key returned from keystone

package middleware

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/troubling/hummingbird/accountserver"
	"github.com/troubling/hummingbird/common/conf"
	"github.com/troubling/hummingbird/common/srv"
	"github.com/uber-go/tally"
)

const (
	s3Xmlns = "http://s3.amazonaws.com/doc/2006-03-01"
)

type s3Response struct {
	Code    string
	Message string
}

var s3Responses = map[int]s3Response{
	// NOTE: These are meant to be generic responses
	403: {"AccessDenied", "Access Denied"},
	501: {"NotImplemented", "A header you provided implies functionality that is not implemented."},
	500: {"InternalError", "We encountered an internal error. Please try again."},
	503: {"ServiceUnavailable", "Reduce your request rate."},
	411: {"MissingContentLength", "You must provide the Content-Length HTTP header."},
}

type s3Owner struct {
	ID          string `xml:"ID"`
	DisplayName string `xml:"DisplayName"`
}

type s3BucketInfo struct {
	Name         string `xml:"Name"`
	CreationDate string `xml:"CreationDate"`
}

type s3BucketList struct {
	XMLName xml.Name       `xml:"ListAllMyBucketsResult"`
	Xmlns   string         `xml:"xmlns,attr"`
	Owner   s3Owner        `xml:"Owner"`
	Buckets []s3BucketInfo `xml:"Buckets>Bucket"`
}

func NewS3BucketList() *s3BucketList {
	return &s3BucketList{Xmlns: s3Xmlns}
}

type s3Error struct {
	XMLName   xml.Name `xml:"Error"`
	Code      string   `xml:"Code"`
	Message   string   `xml:"Message"`
	Resource  string   `xml:"Resource"`
	RequestId string   `xml:"RequestId"`
}

func NewS3Error() *s3Error {
	return &s3Error{}
}

// This will wrap http.ResponseWriter to support s3 style xml responses on errors
// TODO: This may still need some work for more specific error responses
type s3ResponseWriterWrapper struct {
	writer    http.ResponseWriter
	hijack    bool
	resource  string
	requestId string
	msg       []byte
}

func newS3ResponseWriterWrapper(w http.ResponseWriter, r *http.Request) *s3ResponseWriterWrapper {
	ctx := GetProxyContext(r)
	return &s3ResponseWriterWrapper{
		writer:    w,
		hijack:    false,
		resource:  r.URL.Path,
		requestId: ctx.TxId,
	}
}

func (w *s3ResponseWriterWrapper) Header() http.Header {
	return w.writer.Header()
}

func (w *s3ResponseWriterWrapper) WriteHeader(statusCode int) {
	if statusCode/100 != 2 {
		// We are going to hijack to return an S3 style result
		w.hijack = true
		if statusCode == 401 {
			statusCode = 403 // S3 returns 403 instead of 401
		}
		msg := NewS3Error()
		msg.Code = s3Responses[statusCode].Code
		msg.Message = s3Responses[statusCode].Message
		msg.Resource = w.resource
		msg.RequestId = w.requestId
		output, err := xml.MarshalIndent(msg, "", "  ")
		if err != nil {
			w.hijack = false
			w.WriteHeader(500)
		}
		output = []byte(xml.Header + string(output))
		headers := w.writer.Header()
		headers.Set("Content-Type", "application/xml; charset=utf-8")
		headers.Set("Content-Length", strconv.Itoa(len(output)))
		w.msg = output
	}
	w.writer.WriteHeader(statusCode)
}

func (w *s3ResponseWriterWrapper) Write(buf []byte) (int, error) {
	if !w.hijack {
		return w.writer.Write(buf)
	} else {
		n, err := w.writer.Write(w.msg)
		return n, err
	}
}

type s3ApiHandler struct {
	next           http.Handler
	ctx            *ProxyContext
	account        string
	container      string
	object         string
	path           string
	signature      string
	requestsMetric tally.Counter
}

func s3PathSplit(path string) (string, string) {
	parts := strings.SplitN(path, "/", 3)
	switch len(parts) {
	case 3:
		return parts[1], parts[2]
	case 2:
		return parts[1], ""
	default:
		return "", ""
	}
}

func (s *s3ApiHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)
	// Check if this is an S3 request
	if ctx.S3Auth == nil || strings.HasPrefix(strings.ToLower(request.URL.Path), "/v1/") {
		// Not an S3 request
		s.next.ServeHTTP(writer, request)
		return
	}

	s.container, s.object = s3PathSplit(request.URL.Path)
	s.account = ctx.S3Auth.Account

	// TODO: Validate the container
	// Generate the hbird api path
	if s.object != "" {
		s.path = fmt.Sprintf("/v1/AUTH_%s/%s/%s", s.account, s.container, s.object)
	} else if s.container != "" {
		s.path = fmt.Sprintf("/v1/AUTH_%s/%s", s.account, s.container)
	} else {
		s.path = fmt.Sprintf("/v1/AUTH_%s", s.account)
	}
	// TODO: Handle metadata?

	if s.object != "" {
		s.handleObjectRequest(writer, request)
		return
	} else if s.container != "" {
		s.handleContainerRequest(writer, request)
		return
	} else {
		s.handleAccountRequest(writer, request)
		return
	}
}

func (s *s3ApiHandler) handleObjectRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)

	if request.Method == "PUT" {
		newReq, err := ctx.newSubrequest("PUT", s.path, request.Body, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Content-Length", request.Header.Get("Content-Length"))
		newReq.Header.Set("Content-Type", request.Header.Get("Content-Type"))
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.Header().Set("ETag", "\""+cap.Header().Get("ETag")+"\"")
			writer.Header().Set("Content-Length", cap.Header().Get("Content-Length"))
			writer.WriteHeader(200)
			return
		}
	}

	// If we didn't get to anything, then return no implemented
	srv.SimpleErrorResponse(writer, http.StatusNotImplemented, "Not Implemented")
}

func (s *s3ApiHandler) handleContainerRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)

	if request.Method == "DELETE" {
		newReq, err := ctx.newSubrequest("DELETE", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.WriteHeader(204)
			return
		}
	}

	if request.Method == "PUT" {
		newReq, err := ctx.newSubrequest("PUT", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		} else {
			writer.WriteHeader(200)
			return
		}
	}

	// If we didn't get to do anything, then return not implemented
	srv.SimpleErrorResponse(writer, http.StatusNotImplemented, "Not Implemented")
}

func (s *s3ApiHandler) handleAccountRequest(writer http.ResponseWriter, request *http.Request) {
	ctx := GetProxyContext(request)
	if request.Method == "GET" {
		newReq, err := ctx.newSubrequest("GET", s.path, http.NoBody, request, "s3api")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		newReq.Header.Set("Accept", "application/json")
		cap := NewCaptureWriter()
		ctx.serveHTTPSubrequest(cap, newReq)
		if cap.status/100 != 2 {
			srv.StandardResponse(writer, cap.status)
			return
		}
		containerListing := []accountserver.ContainerListingRecord{}
		err = json.Unmarshal(cap.body, &containerListing)
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		bucketList := NewS3BucketList()
		bucketList.Owner.ID = ctx.S3Auth.Account
		bucketList.Owner.DisplayName = ctx.S3Auth.Account
		// NOTE: The container list api doesn't have a creation date for the container, so we use an "arbitrary" date.
		for _, c := range containerListing {
			bucketList.Buckets = append(bucketList.Buckets, s3BucketInfo{
				Name:         c.Name,
				CreationDate: "2009-02-03T16:45:09.000Z",
			})
		}
		output, err := xml.MarshalIndent(bucketList, "", "  ")
		if err != nil {
			srv.SimpleErrorResponse(writer, http.StatusInternalServerError, err.Error())
			return
		}
		output = []byte(xml.Header + string(output))
		headers := writer.Header()
		headers.Set("Content-Type", "application/xml; charset=utf-8")
		headers.Set("Content-Length", strconv.Itoa(len(output)))
		writer.WriteHeader(200)
		writer.Write(output)
		return

	}

	// No other methods are allowed at the account level
	srv.StandardResponse(writer, http.StatusMethodNotAllowed)
}

func NewS3Api(config conf.Section, metricsScope tally.Scope) (func(http.Handler) http.Handler, error) {
	enabled, ok := config.Section["enabled"]
	if !ok || strings.Compare(strings.ToLower(enabled), "false") == 0 {
		// s3api is disabled, so pass the request on
		return func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
				next.ServeHTTP(writer, request)
			})
		}, nil
	}
	RegisterInfo("s3Api", map[string]interface{}{})
	return s3Api(metricsScope.Counter("s3Api_requests")), nil
}

func s3Api(requestsMetric tally.Counter) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			(&s3ApiHandler{next: next, requestsMetric: requestsMetric}).ServeHTTP(writer, request)
		})
	}
}
<|endoftext|>


<commit_before>/* walter: a deployment pipeline template
 * Copyright (C) 2014 Recruit Technologies Co., Ltd. and contributors
 * (see CONTRIBUTORS.md)
 *
 * 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 messengers

import (
	"encoding/json"
	"github.com/recruit-tech/walter/log"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// Slack is a client which reports the pipeline results to the Slack chennel.
type Slack struct {
	BaseMessenger `config:"suppress"`
	Channel       string `config:"channel" json:"channel"`
	UserName      string `config:"username" json:"username"`
	IconEmoji     string `config:"icon" json:"icon_emoji,omitempty"`
	IconUrl       string `config:"icon_url" json:"icon_url,omitempty"`
	IncomingUrl   string `config:"url" json:"-"` // not map to json
}

// To avoid the infinite recursion
// (see http://stackoverflow.com/questions/23045884/can-i-use-marshaljson-to-add-arbitrary-fields-to-a-json-encoding-in-golang)
type FakeSlack Slack

func (self *Slack) Post(message string) bool {
	if self.Channel[0] != '#' {
		log.Infof("Add # to channel name: %s", self.Channel)
		self.Channel = "#" + self.Channel
	}

	var color string

	if strings.Contains(message, "] Failed") {
		color = "danger"
	} else if strings.Contains(message, "] Skipped") {
		color = "warning"
	} else {
		color = "good"
	}

	attachment := map[string]string{
		"text":  message,
		"color": color,
	}

	attachments := []map[string]string{attachment}

	params, _ := json.Marshal(struct {
		FakeSlack
		Attachments []map[string]string `json:"attachments"`
	}{
		FakeSlack:   FakeSlack(*self),
		Attachments: attachments,
	})

	resp, err := http.PostForm(
		self.IncomingUrl,
		url.Values{"payload": {string(params)}},
	)
	defer resp.Body.Close()

	if err != nil {
		log.Errorf("Failed post message to Slack...: %s", message)
		return false
	}

	if body, err := ioutil.ReadAll(resp.Body); err == nil {
		log.Infof("Slack post result...: %s", body)
		return true
	}
	log.Errorf("Failed to read result from Slack...")
	return false
}
<commit_msg>Make uncolor stdout and stderr messeges<commit_after>/* walter: a deployment pipeline template
 * Copyright (C) 2014 Recruit Technologies Co., Ltd. and contributors
 * (see CONTRIBUTORS.md)
 *
 * 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 messengers

import (
	"encoding/json"
	"github.com/recruit-tech/walter/log"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// Slack is a client which reports the pipeline results to the Slack chennel.
type Slack struct {
	BaseMessenger `config:"suppress"`
	Channel       string `config:"channel" json:"channel"`
	UserName      string `config:"username" json:"username"`
	IconEmoji     string `config:"icon" json:"icon_emoji,omitempty"`
	IconUrl       string `config:"icon_url" json:"icon_url,omitempty"`
	IncomingUrl   string `config:"url" json:"-"` // not map to json
}

// To avoid the infinite recursion
// (see http://stackoverflow.com/questions/23045884/can-i-use-marshaljson-to-add-arbitrary-fields-to-a-json-encoding-in-golang)
type FakeSlack Slack

func (self *Slack) Post(message string) bool {
	if self.Channel[0] != '#' {
		log.Infof("Add # to channel name: %s", self.Channel)
		self.Channel = "#" + self.Channel
	}

	var color string

	if strings.Contains(message, "[RESULT] Failed") {
		color = "danger"
	} else if strings.Contains(message, "[RESULT] Skipped") {
		color = "warning"
	} else if strings.Contains(message, "[RESULT] Succeeded") {
		color = "good"
	}

	attachment := map[string]string{
		"text":  message,
		"color": color,
	}

	attachments := []map[string]string{attachment}

	params, _ := json.Marshal(struct {
		FakeSlack
		Attachments []map[string]string `json:"attachments"`
	}{
		FakeSlack:   FakeSlack(*self),
		Attachments: attachments,
	})

	resp, err := http.PostForm(
		self.IncomingUrl,
		url.Values{"payload": {string(params)}},
	)
	defer resp.Body.Close()

	if err != nil {
		log.Errorf("Failed post message to Slack...: %s", message)
		return false
	}

	if body, err := ioutil.ReadAll(resp.Body); err == nil {
		log.Infof("Slack post result...: %s", body)
		return true
	}
	log.Errorf("Failed to read result from Slack...")
	return false
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/AlekSi/pointer"
	"github.com/julienschmidt/httprouter"
	"github.com/rumyantseva/mif/models"
	"gopkg.in/reform.v1"
)

// SingleBook finds book from database by its id with volumes available for this book.
func (mw *MW) SingleBook(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	bookID, err := strconv.Atoi(p.ByName("id"))

	if err != nil || bookID <= 0 {
		mw.log.Info(bookID)
		mw.makeError(w, http.StatusBadRequest, "Book ID must be positive integer.")
		return
	}

	st, err := mw.db.FindByPrimaryKeyFrom(models.BookTable, bookID)
	if err != nil && err != reform.ErrNoRows {
		mw.log.Errorf("Couldn't get data from DB for book: %s", err.Error())
		mw.errorServer(w)
		return
	}
	if err == reform.ErrNoRows {
		mw.makeError(w, http.StatusNotFound, "Book is not found.")
		return
	}

	book := st.(*models.Book)

	// Additional information: book volumes
	sts, err := mw.db.FindAllFrom(models.VolumeTable, "book_id", book.ID)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for book's volumes: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var volumes []string
	for _, st := range sts {
		volume := st.(*models.Volume)
		volumes = append(volumes, volume.Type)
	}
	book.Volumes = volumes

	mw.makeDataBody(w, http.StatusOK, book, nil)
}

// SearchBooks finds multiple books by the given search criteria.
func (mw *MW) SearchBooks(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	request := struct {
		mifID  *int
		volume *string
		search *string
		author *string
		limit  int
		offset int
	}{
		mifID:  nil,
		volume: nil,
		search: nil,
		author: nil,
		limit:  10,
		offset: 0,
	}

	mifID := r.URL.Query().Get("mif_id")
	if len(mifID) > 0 {
		numID, err := strconv.Atoi(mifID)
		if err != nil {
			mw.makeError(w, http.StatusBadRequest, "MIF ID must be positive integer.")
			return
		}

		request.mifID = pointer.ToInt(numID)
	}

	volume := r.URL.Query().Get("type")
	if len(volume) > 0 {
		possible := models.CheckVolume(volume)
		if !possible {
			msg := "Volume type is wrong. Possible values are: " + strings.Join(models.GetVolumes(), ", ")
			mw.makeError(w, http.StatusBadRequest, msg)
			return
		}
		request.volume = pointer.ToString(volume)
	}

	search := r.URL.Query().Get("search")
	if len(search) > 0 {
		request.search = pointer.ToString("%" + search + "%")
	}

	author := r.URL.Query().Get("author")
	if len(author) > 0 {
		request.author = pointer.ToString("%" + author + "%")
	}

	limit := r.URL.Query().Get("limit")
	if len(limit) > 0 {
		numLim, err := strconv.Atoi(limit)
		if err != nil || numLim <= 0 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return
		}

		if numLim > 100 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return
		}

		request.limit = numLim
	}

	offset := r.URL.Query().Get("offset")
	if len(offset) > 0 {
		numOffset, err := strconv.Atoi(offset)
		if err != nil || numOffset < 0 {
			mw.makeError(w, http.StatusBadRequest, "Offset must be non-negative integer.")
			return
		}

		request.offset = numOffset
	}

	sel := ""
	var conditions []string
	var args []interface{}

	ind := 1

	if request.mifID != nil {
		conditions = append(conditions, "books.mif_id = "+mw.db.Placeholder(ind))
		args = append(args, *request.mifID)
		ind++
	}

	if request.search != nil {
		conditions = append(conditions, "books.title ILIKE "+mw.db.Placeholder(ind))
		args = append(args, *request.search)
		ind++
	}

	if request.author != nil {
		conditions = append(conditions, "books.authors ILIKE "+mw.db.Placeholder(ind))
		args = append(args, *request.author)
		ind++
	}

	join := ""
	if request.volume != nil {
		join = "JOIN volumes v ON v.book_id = books.id "
		conditions = append(conditions, "v.type = "+mw.db.Placeholder(ind))
		args = append(args, *request.volume)
	}

	order := fmt.Sprintf(" ORDER BY books.id DESC LIMIT %d OFFSET %d", request.limit, request.offset)

	where := " WHERE is_visible = true "
	if len(conditions) > 0 {
		where += "AND " + strings.Join(conditions, " AND ")
	}

	query := sel + join + where + order

	sts, err := mw.db.SelectAllFrom(models.BookTable, query, args...)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for books: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var books []*models.Book
	for _, st := range sts {
		book := st.(*models.Book)
		books = append(books, book)
	}

	// Total amount of books for the request
	var total int
	query = "SELECT COUNT(books.*) FROM " + models.BookTable.Name() + join + where
	err = mw.db.QueryRow(query, args...).Scan(&total)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for books total count: %s", err.Error())
		mw.errorServer(w)
		return
	}

	meta := struct {
		Total  int `json:"total"`
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
	}{
		Limit:  request.limit,
		Offset: request.offset,
		Total:  total,
	}
	mw.makeDataBody(w, http.StatusOK, books, meta)
}
<commit_msg>Fix cyclomatic complexities<commit_after>package middleware

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/AlekSi/pointer"
	"github.com/julienschmidt/httprouter"
	"github.com/rumyantseva/mif/models"
	"gopkg.in/reform.v1"
)

// SearchBooksRequest describes parameters of books searching request.
type SearchBooksRequest struct {
	mifID  *int
	volume *string
	search *string
	author *string
	limit  int
	offset int
}

// SingleBook finds book from database by its id with volumes available for this book.
func (mw *MW) SingleBook(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	bookID, err := strconv.Atoi(p.ByName("id"))

	if err != nil || bookID <= 0 {
		mw.log.Info(bookID)
		mw.makeError(w, http.StatusBadRequest, "Book ID must be positive integer.")
		return
	}

	st, err := mw.db.FindByPrimaryKeyFrom(models.BookTable, bookID)
	if err != nil && err != reform.ErrNoRows {
		mw.log.Errorf("Couldn't get data from DB for book: %s", err.Error())
		mw.errorServer(w)
		return
	}
	if err == reform.ErrNoRows {
		mw.makeError(w, http.StatusNotFound, "Book is not found.")
		return
	}

	book := st.(*models.Book)

	// Additional information: book volumes
	sts, err := mw.db.FindAllFrom(models.VolumeTable, "book_id", book.ID)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for book's volumes: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var volumes []string
	for _, st := range sts {
		volume := st.(*models.Volume)
		volumes = append(volumes, volume.Type)
	}
	book.Volumes = volumes

	mw.makeDataBody(w, http.StatusOK, book, nil)
}

// SearchBooks finds multiple books by the given search criteria.
func (mw *MW) SearchBooks(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	request := mw.searchBooksRequest(r, w)
	if request == nil {
		return
	}

	sel := ""
	var conditions []string
	var args []interface{}

	ind := 1

	if request.mifID != nil {
		conditions = append(conditions, "books.mif_id = "+mw.db.Placeholder(ind))
		args = append(args, *request.mifID)
		ind++
	}

	if request.search != nil {
		conditions = append(conditions, "books.title ILIKE "+mw.db.Placeholder(ind))
		args = append(args, *request.search)
		ind++
	}

	if request.author != nil {
		conditions = append(conditions, "books.authors ILIKE "+mw.db.Placeholder(ind))
		args = append(args, *request.author)
		ind++
	}

	join := ""
	if request.volume != nil {
		join = "JOIN volumes v ON v.book_id = books.id "
		conditions = append(conditions, "v.type = "+mw.db.Placeholder(ind))
		args = append(args, *request.volume)
	}

	order := fmt.Sprintf(" ORDER BY books.id DESC LIMIT %d OFFSET %d", request.limit, request.offset)

	where := " WHERE is_visible = true "
	if len(conditions) > 0 {
		where += "AND " + strings.Join(conditions, " AND ")
	}

	query := sel + join + where + order

	sts, err := mw.db.SelectAllFrom(models.BookTable, query, args...)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for books: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var books []*models.Book
	for _, st := range sts {
		book := st.(*models.Book)
		books = append(books, book)
	}

	// Total amount of books for the request
	var total int
	query = "SELECT COUNT(books.*) FROM " + models.BookTable.Name() + join + where
	err = mw.db.QueryRow(query, args...).Scan(&total)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for books total count: %s", err.Error())
		mw.errorServer(w)
		return
	}

	meta := struct {
		Total  int `json:"total"`
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
	}{
		Limit:  request.limit,
		Offset: request.offset,
		Total:  total,
	}
	mw.makeDataBody(w, http.StatusOK, books, meta)
}

// searchBooksRequest checks http request and makes SearchBooksRequest item.
func (mw *MW) searchBooksRequest(r *http.Request, w http.ResponseWriter) *SearchBooksRequest {
	request := &SearchBooksRequest{
		mifID:  nil,
		volume: nil,
		search: nil,
		author: nil,
		limit:  10,
		offset: 0,
	}

	mifID := r.URL.Query().Get("mif_id")
	if len(mifID) > 0 {
		numID, err := strconv.Atoi(mifID)
		if err != nil {
			mw.makeError(w, http.StatusBadRequest, "MIF ID must be positive integer.")
			return nil
		}

		request.mifID = pointer.ToInt(numID)
	}

	volume := r.URL.Query().Get("type")
	if len(volume) > 0 {
		possible := models.CheckVolume(volume)
		if !possible {
			msg := "Volume type is wrong. Possible values are: " + strings.Join(models.GetVolumes(), ", ")
			mw.makeError(w, http.StatusBadRequest, msg)
			return nil
		}
		request.volume = pointer.ToString(volume)
	}

	search := r.URL.Query().Get("search")
	if len(search) > 0 {
		request.search = pointer.ToString("%" + search + "%")
	}

	author := r.URL.Query().Get("author")
	if len(author) > 0 {
		request.author = pointer.ToString("%" + author + "%")
	}

	limit := r.URL.Query().Get("limit")
	if len(limit) > 0 {
		numLim, err := strconv.Atoi(limit)
		if err != nil || numLim <= 0 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		if numLim > 100 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		request.limit = numLim
	}

	offset := r.URL.Query().Get("offset")
	if len(offset) > 0 {
		numOffset, err := strconv.Atoi(offset)
		if err != nil || numOffset < 0 {
			mw.makeError(w, http.StatusBadRequest, "Offset must be non-negative integer.")
			return nil
		}

		request.offset = numOffset
	}

	return request
}
<|endoftext|>


<commit_before>package seven5

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

const (
	prefix = "gopath"
)

//SimpleStaticFileServer is a simple implementation of a file server
//for static files that is sufficient for most simple applications.  It
//defaults to serving "/" from "static" (child of the current directory)
//but this can be changed with the environment variable STATIC_DIR.
type SimpleStaticFilesServer struct {
	testMode  bool
	mountedAt string
	staticDir string
	fs        http.Handler
}

//NewSimpleStaticFilesServer returns a new file server and if isTestMode
//is true and the environment variable GOPATH is set, it will
//also serve up go source files from the GOPATH.  It expects that the
//prefix "/gopath" will be used for gopath requests.  You should supply
//the place this has been "mounted" in the URL space (usually "/"") in
//the first parameter.
func NewSimpleStaticFilesServer(mountedAt string, isTestMode bool) *SimpleStaticFilesServer {
	staticDir := "static"
	env := os.Getenv("STATIC_DIR")
	if env != "" {
		log.Printf("STATIC_DIR is set, using  %s for static files", env)
		staticDir = env
	}
	return &SimpleStaticFilesServer{
		testMode:  isTestMode,
		mountedAt: mountedAt,
		staticDir: staticDir,
		fs:        http.StripPrefix(mountedAt, http.FileServer(http.Dir(staticDir))),
	}
}

func (s *SimpleStaticFilesServer) gopath(w http.ResponseWriter, r *http.Request, desired string) {
	gopathSegments := strings.Split(os.Getenv("GOPATH"), ":")
	for _, gopath := range gopathSegments {
		_, err := os.Stat(filepath.Join(gopath, desired))
		if err != nil {
			continue
		}
		log.Printf("serving gopath content  %s: /%v", gopath, desired)
		http.ServeFile(w, r, filepath.Join(gopath, desired))
		return
	}
	http.NotFound(w, r)
}

//ServeHTTP retuns a static file or a not found error. This function meets
//the requirement of net/http#Handler.
func (s *SimpleStaticFilesServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.testMode && strings.HasPrefix(r.URL.String(), prefix) {
		s.gopath(w, r, r.URL.String()[len(prefix):])
		return
	}
	log.Printf("[STATIC CONTENT (%s)]: %v", s.staticDir, r.URL.String())
	s.fs.ServeHTTP(w, r)
}
<commit_msg>fix gopath prefix<commit_after>package seven5

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

const (
	prefix = "/gopath"
)

//SimpleStaticFileServer is a simple implementation of a file server
//for static files that is sufficient for most simple applications.  It
//defaults to serving "/" from "static" (child of the current directory)
//but this can be changed with the environment variable STATIC_DIR.
type SimpleStaticFilesServer struct {
	testMode  bool
	mountedAt string
	staticDir string
	fs        http.Handler
}

//NewSimpleStaticFilesServer returns a new file server and if isTestMode
//is true and the environment variable GOPATH is set, it will
//also serve up go source files from the GOPATH.  It expects that the
//prefix "/gopath" will be used for gopath requests.  You should supply
//the place this has been "mounted" in the URL space (usually "/"") in
//the first parameter.
func NewSimpleStaticFilesServer(mountedAt string, isTestMode bool) *SimpleStaticFilesServer {
	staticDir := "static"
	env := os.Getenv("STATIC_DIR")
	if env != "" {
		log.Printf("STATIC_DIR is set, using  %s for static files", env)
		staticDir = env
	}
	return &SimpleStaticFilesServer{
		testMode:  isTestMode,
		mountedAt: mountedAt,
		staticDir: staticDir,
		fs:        http.StripPrefix(mountedAt, http.FileServer(http.Dir(staticDir))),
	}
}

func (s *SimpleStaticFilesServer) gopath(w http.ResponseWriter, r *http.Request, desired string) {
	gopathSegments := strings.Split(os.Getenv("GOPATH"), ":")
	for _, gopath := range gopathSegments {
		_, err := os.Stat(filepath.Join(gopath, desired))
		if err != nil {
			continue
		}
		log.Printf("serving gopath content  %s: /%v", gopath, desired)
		http.ServeFile(w, r, filepath.Join(gopath, desired))
		return
	}
	http.NotFound(w, r)
}

//ServeHTTP retuns a static file or a not found error. This function meets
//the requirement of net/http#Handler.
func (s *SimpleStaticFilesServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.testMode && strings.HasPrefix(r.URL.String(), prefix) {
		s.gopath(w, r, strings.TrimPrefix(r.URL.String(), prefix))
		return
	}
	log.Printf("[STATIC CONTENT (%s)]: %v", s.staticDir, r.URL.String())
	s.fs.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	var err error

	if len(args) == 0 {
		return nil, errors.New("Incorrect number of arguments. At least one Meter's name is required.")
	}

	for _,name := range args {
		if len(name) == 0{
			continue
		}
		err = stub.PutState(name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
	}

	return nil, nil
}

// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if function != "report" {
		return nil, errors.New("Unimplemented '" + function + "' invoked")
	}

	var name string    // Entities
	var val int // Asset holdings
	var err error

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	name = args[0]
	val, _ = strconv.Atoi(string(args[1]))

	err = stub.PutState(name, []byte(strconv.Itoa(val)))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Query callback representing the query of a chaincode
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if function != "query" {
		return nil, errors.New("Invalid query function name. Expecting \"query\"")
	}
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState(name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter" + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<commit_msg>return status message<commit_after>package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	var err error

	if len(args) == 0 {
		return nil, errors.New("Incorrect number of arguments. At least one Meter's name is required.")
	}

	for _,name := range args {
		if len(name) == 0{
			continue
		}
		err = stub.PutState(name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
	}

	return nil, nil
}

// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if function != "report" {
		return nil, errors.New("Unimplemented '" + function + "' invoked")
	}

	var name string    // Entities
	var val int // Asset holdings
	var err error

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	name = args[0]
	val, _ = strconv.Atoi(string(args[1]))

	err = stub.PutState(name, []byte(strconv.Itoa(val)))

	if err != nil {
		return nil, err
	}

	if(val > 3){
		return []byte(strconv.Itoa(1)), nil
	}else{
		return []byte(strconv.Itoa(0)), nil

	}

}

// Query callback representing the query of a chaincode
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if function != "query" {
		return nil, errors.New("Invalid query function name. Expecting \"query\"")
	}
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState(name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter" + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<|endoftext|>


<commit_before>package htsdb

import (
	"github.com/Masterminds/squirrel"
	"github.com/biogo/biogo/feat"
)

// Assert that interfaces are satisfied
var (
	_ feat.Orienter = (*Feature)(nil)
	_ feat.Feature  = (*Feature)(nil)
)

// RangeBuilder is a squirrel select builder whose columns match Range fields.
var RangeBuilder = squirrel.Select("start", "stop", "copy_number")

// Range is part of an htsdb record that wraps the alignment coordinates.
type Range struct {
	StartPos   int `db:"start"`
	StopPos    int `db:"stop"`
	CopyNumber int `db:"copy_number"`
}

// Start returns the start position of Range.
func (e *Range) Start() int { return e.StartPos }

// End returns the end position of Range.
func (e *Range) End() int { return e.StopPos + 1 }

// Len returns the length of Range.
func (e *Range) Len() int { return e.End() - e.Start() }

// CopyNum returns the copy number of Range.
func (e *Range) CopyNum() int { return e.CopyNumber }

// Feature is part of an htsdb record that wraps Range, strand and the name of
// the reference sequence.
type Feature struct {
	Strand int    `db:"strand"`
	Rname  string `db:"rname"`
	Range
}

// Name returns an empty string.
func (e *Feature) Name() string { return "" }

// Orientation returns the orientation of Feature.
func (e *Feature) Orientation() feat.Orientation {
	return feat.Orientation(e.Strand)
}

// Description returns an empty string.
func (e *Feature) Description() string { return "" }

// Location returns the location of Feature.
func (e *Feature) Location() feat.Feature {
	return &Reference{Chrom: e.Rname}
}

// SAM corresponds to database record that has all the fields of a SAM record.
type SAM struct {
	Feature
	Qname string
	Flag  uint
	Pos   uint
	Mapq  uint
	Cigar string
	Rnext string
	Pnext uint
	Tlen  uint
	Seq   string
	Qual  string
	Tags  string
}

// Name returns the SAM qname.
func (e *SAM) Name() string { return e.Qname }
<commit_msg>htsdb: add functions to extract head/tail coordinates<commit_after>package htsdb

import (
	"github.com/Masterminds/squirrel"
	"github.com/biogo/biogo/feat"
)

// Assert that interfaces are satisfied
var (
	_ feat.Orienter = (*Feature)(nil)
	_ feat.Feature  = (*Feature)(nil)
)

// RangeBuilder is a squirrel select builder whose columns match Range fields.
var RangeBuilder = squirrel.Select("start", "stop", "copy_number")

// Range is part of an htsdb record that wraps the alignment coordinates.
type Range struct {
	StartPos   int `db:"start"`
	StopPos    int `db:"stop"`
	CopyNumber int `db:"copy_number"`
}

// Start returns the start position of Range.
func (e *Range) Start() int { return e.StartPos }

// End returns the end position of Range.
func (e *Range) End() int { return e.StopPos + 1 }

// Len returns the length of Range.
func (e *Range) Len() int { return e.End() - e.Start() }

// CopyNum returns the copy number of Range.
func (e *Range) CopyNum() int { return e.CopyNumber }

// Feature is part of an htsdb record that wraps Range, strand and the name of
// the reference sequence.
type Feature struct {
	Strand int    `db:"strand"`
	Rname  string `db:"rname"`
	Range
}

// Name returns an empty string.
func (e *Feature) Name() string { return "" }

// Orientation returns the orientation of Feature.
func (e *Feature) Orientation() feat.Orientation {
	return feat.Orientation(e.Strand)
}

// Description returns an empty string.
func (e *Feature) Description() string { return "" }

// Location returns the location of Feature.
func (e *Feature) Location() feat.Feature {
	return &Reference{Chrom: e.Rname}
}

// SAM corresponds to database record that has all the fields of a SAM record.
type SAM struct {
	Feature
	Qname string
	Flag  uint
	Pos   uint
	Mapq  uint
	Cigar string
	Rnext string
	Pnext uint
	Tlen  uint
	Seq   string
	Qual  string
	Tags  string
}

// Name returns the SAM qname.
func (e *SAM) Name() string { return e.Qname }

// Head returns the head coordinate of r depending on orientation.
func Head(r feat.Range, o feat.Orientation) int {
	if o == feat.Forward {
		return r.Start()
	} else if o == feat.Reverse {
		return r.End() - 1
	}
	panic("htsdb: strand is not 1 or -1")
}

// Tail returns the tail coordinate of r depending on orientation.
func Tail(r feat.Range, o feat.Orientation) int {
	if o == feat.Forward {
		return r.End() - 1
	} else if o == feat.Reverse {
		return r.Start()
	}
	panic("htsdb: strand is not 1 or -1")
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * 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 s3git

import (
	"fmt"
	"github.com/s3git/s3git-go/internal/config"
	"strings"
	"errors"
)

type Remote struct {
	Name string
	Resource string
}

type remoteOptions struct {
	endpoint string
}

func RemoteOptionSetEndpoint(endpoint string) func(optns *remoteOptions) {
	return func(optns *remoteOptions) {
		optns.endpoint = endpoint
	}
}

type RemoteOptions func(*remoteOptions)

func (repo Repository) RemoteAdd(name, resource, accessKey, secretKey string, options ...RemoteOptions) error {

	optns := &remoteOptions{}
	for _, op := range options {
		op(optns)
	}

	// TODO: 'Ping' remote to check credentials

	parts := strings.Split(resource, "//")
	if len(parts) != 2 {
		return errors.New(fmt.Sprintf("Bad resource for cloning (missing '//' separator): %s", resource))
	}

	var region, endpoint string
	if optns.endpoint == "" {
		// Just look for region when endpoint not explicitly specified
		var err error
		region, err = config.GetRegionForBucket(parts[1], accessKey, secretKey)
		if err != nil {
			return err
		}
	} else {
		endpoint = optns.endpoint
	}

	if region == "" {
		// TODO: Remove hard coded region
		region = "us-east-1"
	}

	return config.AddRemote(name, parts[1], region, accessKey, secretKey, endpoint)
}

func (repo Repository) RemotesShow() ([]Remote, error) {

	remotes := []Remote{}

	for _, r := range config.Config.Remotes {
		remotes = append(remotes, Remote{Name: r.Name, Resource: r.S3Bucket})
	}

	return remotes, nil
}<commit_msg>Internal API to add config for fake backend<commit_after>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * 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 s3git

import (
	"fmt"
	"github.com/s3git/s3git-go/internal/config"
	"strings"
	"errors"
)

type Remote struct {
	Name string
	Resource string
}

type remoteOptions struct {
	endpoint string
}

func RemoteOptionSetEndpoint(endpoint string) func(optns *remoteOptions) {
	return func(optns *remoteOptions) {
		optns.endpoint = endpoint
	}
}

type RemoteOptions func(*remoteOptions)

func (repo Repository) RemoteAdd(name, resource, accessKey, secretKey string, options ...RemoteOptions) error {

	optns := &remoteOptions{}
	for _, op := range options {
		op(optns)
	}

	// TODO: 'Ping' remote to check credentials

	parts := strings.Split(resource, "//")
	if len(parts) != 2 {
		return errors.New(fmt.Sprintf("Bad resource for cloning (missing '//' separator): %s", resource))
	}

	var region, endpoint string
	if optns.endpoint == "" {
		// Just look for region when endpoint not explicitly specified
		var err error
		region, err = config.GetRegionForBucket(parts[1], accessKey, secretKey)
		if err != nil {
			return err
		}
	} else {
		endpoint = optns.endpoint
	}

	if region == "" {
		// TODO: Remove hard coded region
		region = "us-east-1"
	}

	return config.AddRemote(name, parts[1], region, accessKey, secretKey, endpoint)
}

func (repo Repository) remoteAddFake(name, directory string) error {

	return config.AddFakeRemote(name, directory)
}

func (repo Repository) RemotesShow() ([]Remote, error) {

	remotes := []Remote{}

	for _, r := range config.Config.Remotes {
		remotes = append(remotes, Remote{Name: r.Name, Resource: r.S3Bucket})
	}

	return remotes, nil
}<|endoftext|>


<commit_before>package mysqlproto

const (
	SERVER_STATUS_IN_TRANS byte = iota
	SERVER_STATUS_AUTOCOMMIT
	SERVER_MORE_RESULTS_EXISTS
	SERVER_STATUS_NO_GOOD_INDEX_USED
	SERVER_STATUS_NO_INDEX_USED
	SERVER_STATUS_CURSOR_EXISTS
	SERVER_STATUS_LAST_ROW_SENT
	SERVER_STATUS_DB_DROPPED
	SERVER_STATUS_NO_BACKSLASH_ESCAPES
	SERVER_STATUS_METADATA_CHANGED
	SERVER_QUERY_WAS_SLOW
	SERVER_PS_OUT_PARAMS
	SERVER_STATUS_IN_TRANS_READONLY
	SERVER_SESSION_STATE_CHANGED
)
<commit_msg>Fix SERVER_STATUS type<commit_after>package mysqlproto

// https://dev.mysql.com/doc/internals/en/status-flags.html
const (
	SERVER_STATUS_IN_TRANS uint16 = 1 << iota
	SERVER_STATUS_AUTOCOMMIT
	SERVER_MORE_RESULTS_EXISTS
	SERVER_STATUS_NO_GOOD_INDEX_USED
	SERVER_STATUS_NO_INDEX_USED
	SERVER_STATUS_CURSOR_EXISTS
	SERVER_STATUS_LAST_ROW_SENT
	SERVER_STATUS_DB_DROPPED
	SERVER_STATUS_NO_BACKSLASH_ESCAPES
	SERVER_STATUS_METADATA_CHANGED
	SERVER_QUERY_WAS_SLOW
	SERVER_PS_OUT_PARAMS
	SERVER_STATUS_IN_TRANS_READONLY
	SERVER_SESSION_STATE_CHANGED
)
<|endoftext|>


<commit_before>// 22-24 august 2012
package main

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"html/template"
	"log"
)

var report_text = `

	<title>Sega Retro Scan Information: {{.Console}}</title>

Sega Retro Scan Information: {{.Console}}

{{.Stats.HTML}}

Filter by region: Enter the two-letter region code below. Leave the field blank to remove the filter.

<form action="/scans/?special=filter" method=POST>
				<input type=text name=region>
				<input type=submit value=Apply>
			</form>

			Game
			Region
			Box
			Media

{{$filter := .FilterRegion}}{{range .Scans}}
{{if .Error}}

			{{.Name}}
			Error: {{.Error.String}}

{{else}}{{if .HasNoScans}}

			{{.Name}}
			No scans

{{else}}{{if filterRegion .Region $filter}}

			{{.Name}}
			{{.Region}}
			{{.BoxState}}
			{{.MediaState}}

{{end}}{{end}}{{end}}
{{end}}

`

type ReportPageContents struct {
	Console			string
	Stats				Stats
	FilterRegion		string
	URL_NoSort		string
	URL_SortRegion	string
	URL_SortBox		string
	URL_SortMedia		string
	Scans			ScanSet
}

var report_template *template.Template

func init() {
	var err error

	report_template = template.New("report")
	report_template = report_template.Funcs(template.FuncMap{
		"filterRegion":	func(r, what string) bool {
			if what == "" {		// no filter
				return true
			}
			return strings.HasPrefix(r, what)
		},
	})
	report_template, err = report_template.Parse(report_text)
	if err != nil {
		log.Fatalf("could not prepare template for report page: %v", err)
	}
}

const filterRegionName = "region"
const sortOrderName = "sort"
var sortOrders = map[string]SortOrder{
	"region":		SortByRegion,
	"box":		SortByBoxState,
	"media":		SortByMediaState,
}

func urlSort(url url.URL, order string) string {
	q := url.Query()
	q.Del("sort")
	q.Add("sort", order)
	url.RawQuery = q.Encode()
	return url.String()
}

func urlNoSort(url url.URL) string {
	q := url.Query()
	q.Del("sort")
	url.RawQuery = q.Encode()
	return url.String()
}

func generateConsoleReport(console string, w http.ResponseWriter, url url.URL) error {
	var filterRegion string

//	fmt.Fprintf(w, top, console, console)
	scans, err := GetConsoleScans(console)
	if err != nil {
		return fmt.Errorf("Error getting %s scan info: %v", console, err)
	}
	query := url.Query()
	if x, ok := query[filterRegionName]; ok && len(x) > 0 {	// filter by region if supplied
		filterRegion = x[0]
	}
	if x, ok := query[sortOrderName]; ok && len(x) > 0 {		// sort differently if asked
		if so, ok := sortOrders[x[0]]; ok {				// but only if we passed a valid sort order
			scans.Sort(so)
		}
	}
	stats := scans.GetStats(filterRegion)
	report_template.Execute(w, ReportPageContents{
		Console:			console,
		Stats:			stats,
		FilterRegion:		filterRegion,
		URL_NoSort:		urlNoSort(url),
		URL_SortRegion:	urlSort(url, "region"),
		URL_SortBox:		urlSort(url, "box"),
		URL_SortMedia:	urlSort(url, "media"),
		Scans:			scans,
	})
	return nil
}

func applyFilter(w http.ResponseWriter, r *http.Request) error {
	newURL, err := url.Parse(r.Referer())
	if err != nil {
		return err
	}
	filterRegion := r.FormValue("region")
	query := newURL.Query()
	query.Del("region")
	if filterRegion != "" {		// want a filter
		query.Add("region", filterRegion)
	}
	newURL.RawQuery = query.Encode()
	http.Redirect(w, r, newURL.String(), http.StatusFound)
	return nil
}
<commit_msg>Changed filter to search throughout the region string, not just at the beginning. TODO fix rest of code and documentation to reflect this change.<commit_after>// 22-24 august 2012
package main

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"html/template"
	"log"
)

var report_text = `

	<title>Sega Retro Scan Information: {{.Console}}</title>

Sega Retro Scan Information: {{.Console}}

{{.Stats.HTML}}

Filter by region: Enter the two-letter region code below. Leave the field blank to remove the filter.

<form action="/scans/?special=filter" method=POST>
				<input type=text name=region>
				<input type=submit value=Apply>
			</form>

			Game
			Region
			Box
			Media

{{$filter := .FilterRegion}}{{range .Scans}}
{{if .Error}}

			{{.Name}}
			Error: {{.Error.String}}

{{else}}{{if .HasNoScans}}

			{{.Name}}
			No scans

{{else}}{{if filterRegion .Region $filter}}

			{{.Name}}
			{{.Region}}
			{{.BoxState}}
			{{.MediaState}}

{{end}}{{end}}{{end}}
{{end}}

`

type ReportPageContents struct {
	Console			string
	Stats				Stats
	FilterRegion		string
	URL_NoSort		string
	URL_SortRegion	string
	URL_SortBox		string
	URL_SortMedia		string
	Scans			ScanSet
}

var report_template *template.Template

func init() {
	var err error

	report_template = template.New("report")
	report_template = report_template.Funcs(template.FuncMap{
		"filterRegion":	func(r, what string) bool {
			if what == "" {		// no filter
				return true
			}
			return strings.Contains(r, what)
		},
	})
	report_template, err = report_template.Parse(report_text)
	if err != nil {
		log.Fatalf("could not prepare template for report page: %v", err)
	}
}

const filterRegionName = "region"
const sortOrderName = "sort"
var sortOrders = map[string]SortOrder{
	"region":		SortByRegion,
	"box":		SortByBoxState,
	"media":		SortByMediaState,
}

func urlSort(url url.URL, order string) string {
	q := url.Query()
	q.Del("sort")
	q.Add("sort", order)
	url.RawQuery = q.Encode()
	return url.String()
}

func urlNoSort(url url.URL) string {
	q := url.Query()
	q.Del("sort")
	url.RawQuery = q.Encode()
	return url.String()
}

func generateConsoleReport(console string, w http.ResponseWriter, url url.URL) error {
	var filterRegion string

//	fmt.Fprintf(w, top, console, console)
	scans, err := GetConsoleScans(console)
	if err != nil {
		return fmt.Errorf("Error getting %s scan info: %v", console, err)
	}
	query := url.Query()
	if x, ok := query[filterRegionName]; ok && len(x) > 0 {	// filter by region if supplied
		filterRegion = x[0]
	}
	if x, ok := query[sortOrderName]; ok && len(x) > 0 {		// sort differently if asked
		if so, ok := sortOrders[x[0]]; ok {				// but only if we passed a valid sort order
			scans.Sort(so)
		}
	}
	stats := scans.GetStats(filterRegion)
	report_template.Execute(w, ReportPageContents{
		Console:			console,
		Stats:			stats,
		FilterRegion:		filterRegion,
		URL_NoSort:		urlNoSort(url),
		URL_SortRegion:	urlSort(url, "region"),
		URL_SortBox:		urlSort(url, "box"),
		URL_SortMedia:	urlSort(url, "media"),
		Scans:			scans,
	})
	return nil
}

func applyFilter(w http.ResponseWriter, r *http.Request) error {
	newURL, err := url.Parse(r.Referer())
	if err != nil {
		return err
	}
	filterRegion := r.FormValue("region")
	query := newURL.Query()
	query.Del("region")
	if filterRegion != "" {		// want a filter
		query.Add("region", filterRegion)
	}
	newURL.RawQuery = query.Encode()
	http.Redirect(w, r, newURL.String(), http.StatusFound)
	return nil
}
<|endoftext|>