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

package rsyslog_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	stdtesting "testing"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cert"
	jujutesting "launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils/syslog"
	"launchpad.net/juju-core/worker/rsyslog"
)

func TestPackage(t *stdtesting.T) {
	coretesting.MgoTestPackage(t)
}

type RsyslogSuite struct {
	jujutesting.JujuConnSuite
}

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

func (s *RsyslogSuite) SetUpSuite(c *gc.C) {
	s.JujuConnSuite.SetUpSuite(c)
	// TODO(waigani) 2014-03-19 bug 1294462
	// Add patch for suite functions
	restore := testing.PatchValue(rsyslog.LookupUser, func(username string) (uid, gid int, err error) {
		// worker will not attempt to chown files if uid/gid is 0
		return 0, 0, nil
	})
	s.AddSuiteCleanup(func(*gc.C) { restore() })
}

func (s *RsyslogSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)
	s.PatchValue(rsyslog.RestartRsyslog, func() error { return nil })
	s.PatchValue(rsyslog.LogDir, c.MkDir())
	s.PatchValue(rsyslog.RsyslogConfDir, c.MkDir())
}

func waitForFile(c *gc.C, file string) {
	timeout := time.After(coretesting.LongWait)
	for {
		select {
		case <-timeout:
			c.Fatalf("timed out waiting for %s to be written", file)
		case <-time.After(coretesting.ShortWait):
			if _, err := os.Stat(file); err == nil {
				return
			}
		}
	}
}

func waitForRestart(c *gc.C, restarted chan struct{}) {
	timeout := time.After(coretesting.LongWait)
	for {
		select {
		case <-timeout:
			c.Fatalf("timed out waiting for rsyslog to be restarted")
		case <-restarted:
			return
		}
	}
}

func (s *RsyslogSuite) TestStartStop(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	worker.Kill()
	c.Assert(worker.Wait(), gc.IsNil)
}

func (s *RsyslogSuite) TestTearDown(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")
	// On worker teardown, the rsyslog config file should be removed.
	defer func() {
		_, err := os.Stat(confFile)
		c.Assert(err, jc.Satisfies, os.IsNotExist)
	}()
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()
	waitForFile(c, confFile)
}

func (s *RsyslogSuite) TestModeForwarding(c *gc.C) {
	err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	addrs := []string{"0.1.2.3", "0.2.4.6"}
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", addrs)
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()

	// We should get a ca-cert.pem with the contents introduced into state config.
	waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(caCertPEM), gc.DeepEquals, coretesting.CACert)

	// Verify rsyslog configuration.
	waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	c.Assert(err, gc.IsNil)

	syslogPort := s.Conn.Environ.Config().SyslogPort()
	syslogConfig := syslog.NewForwardConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", addrs)
	syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir
	rendered, err := syslogConfig.Render()
	c.Assert(err, gc.IsNil)
	c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered))
}

func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil)
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()
	waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))

	// We should have ca-cert.pem, rsyslog-cert.pem, and rsyslog-key.pem.
	caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	c.Assert(err, gc.IsNil)
	rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem"))
	c.Assert(err, gc.IsNil)
	rsyslogKeyPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-key.pem"))
	c.Assert(err, gc.IsNil)
	_, _, err = cert.ParseCertAndKey(string(rsyslogCertPEM), string(rsyslogKeyPEM))
	c.Assert(err, gc.IsNil)
	err = cert.Verify(string(rsyslogCertPEM), string(caCertPEM), time.Now().UTC())
	c.Assert(err, gc.IsNil)

	// Verify rsyslog configuration.
	waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	c.Assert(err, gc.IsNil)

	syslogPort := s.Conn.Environ.Config().SyslogPort()
	syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", []string{"foo:80"})
	syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir
	rendered, err := syslogConfig.Render()
	c.Assert(err, gc.IsNil)
	c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered))
}

func (s *RsyslogSuite) TestNamespace(c *gc.C) {
	st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	// namespace only takes effect in filenames
	// for machine-0; all others assume isolation.
	s.testNamespace(c, st, "machine-0", "", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "machine-0", "mynamespace", "25-juju-mynamespace.conf", *rsyslog.LogDir+"-mynamespace")
	s.testNamespace(c, st, "machine-1", "", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "machine-1", "mynamespace", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "unit-myservice-0", "", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "unit-myservice-0", "mynamespace", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir)
}

// testNamespace starts a worker and ensures that
// the rsyslog config file has the expected filename,
// and the appropriate log dir is used.
func (s *RsyslogSuite) testNamespace(c *gc.C, st *api.State, tag, namespace, expectedFilename, expectedLogDir string) {
	restarted := make(chan struct{}, 2) // once for create, once for teardown
	s.PatchValue(rsyslog.RestartRsyslog, func() error {
		restarted <- struct{}{}
		return nil
	})

	err := os.MkdirAll(expectedLogDir, 0755)
	c.Assert(err, gc.IsNil)
	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, tag, namespace, []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()

	// Ensure that ca-cert.pem gets written to the expected log dir.
	waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem"))

	// Wait for rsyslog to be restarted, so we can check to see
	// what the name of the config file is.
	waitForRestart(c, restarted)
	dir, err := os.Open(*rsyslog.RsyslogConfDir)
	c.Assert(err, gc.IsNil)
	names, err := dir.Readdirnames(-1)
	dir.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(names, gc.HasLen, 1)
	c.Assert(names[0], gc.Equals, expectedFilename)
}

func (s *RsyslogSuite) TestConfigChange(c *gc.C) {
	var restarted bool
	s.PatchValue(rsyslog.RestartRsyslog, func() error {
		restarted = true
		return nil
	})

	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	handler, err := rsyslog.NewRsyslogConfigHandler(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)

	assertRestart := func(v bool) {
		err := handler.Handle()
		c.Assert(err, gc.IsNil)
		c.Assert(restarted, gc.Equals, v)
		restarted = false
		// Handling again should not restart, as no changes have been made.
		err = handler.Handle()
		c.Assert(err, gc.IsNil)
		c.Assert(restarted, jc.IsFalse)
	}

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	assertRestart(true)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 1})
	c.Assert(err, gc.IsNil)
	assertRestart(true)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"unrelated": "anything"})
	c.Assert(err, gc.IsNil)
	assertRestart(false)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 2})
	c.Assert(err, gc.IsNil)
	assertRestart(true)
}
<commit_msg>Test fix<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package rsyslog_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	stdtesting "testing"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cert"
	jujutesting "launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils/syslog"
	"launchpad.net/juju-core/worker/rsyslog"
)

func TestPackage(t *stdtesting.T) {
	coretesting.MgoTestPackage(t)
}

type RsyslogSuite struct {
	jujutesting.JujuConnSuite
}

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

func (s *RsyslogSuite) SetUpSuite(c *gc.C) {
	s.JujuConnSuite.SetUpSuite(c)
	// TODO(waigani) 2014-03-19 bug 1294462
	// Add patch for suite functions
	restore := testing.PatchValue(rsyslog.LookupUser, func(username string) (uid, gid int, err error) {
		// worker will not attempt to chown files if uid/gid is 0
		return 0, 0, nil
	})
	s.AddSuiteCleanup(func(*gc.C) { restore() })
}

func (s *RsyslogSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)
	s.PatchValue(rsyslog.RestartRsyslog, func() error { return nil })
	s.PatchValue(rsyslog.LogDir, c.MkDir())
	s.PatchValue(rsyslog.RsyslogConfDir, c.MkDir())
}

func waitForFile(c *gc.C, file string) {
	timeout := time.After(coretesting.LongWait)
	for {
		select {
		case <-timeout:
			c.Fatalf("timed out waiting for %s to be written", file)
		case <-time.After(coretesting.ShortWait):
			if _, err := os.Stat(file); err == nil {
				return
			}
		}
	}
}

func waitForRestart(c *gc.C, restarted chan struct{}) {
	timeout := time.After(coretesting.LongWait)
	for {
		select {
		case <-timeout:
			c.Fatalf("timed out waiting for rsyslog to be restarted")
		case <-restarted:
			return
		}
	}
}

func (s *RsyslogSuite) TestStartStop(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	worker.Kill()
	c.Assert(worker.Wait(), gc.IsNil)
}

func (s *RsyslogSuite) TestTearDown(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")
	// On worker teardown, the rsyslog config file should be removed.
	defer func() {
		_, err := os.Stat(confFile)
		c.Assert(err, jc.Satisfies, os.IsNotExist)
	}()
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()
	waitForFile(c, confFile)
}

func (s *RsyslogSuite) TestModeForwarding(c *gc.C) {
	err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	addrs := []string{"0.1.2.3", "0.2.4.6"}
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", addrs)
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()

	// We should get a ca-cert.pem with the contents introduced into state config.
	waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(caCertPEM), gc.DeepEquals, coretesting.CACert)

	// Verify rsyslog configuration.
	waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	c.Assert(err, gc.IsNil)

	syslogPort := s.Conn.Environ.Config().SyslogPort()
	syslogConfig := syslog.NewForwardConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", addrs)
	syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir
	rendered, err := syslogConfig.Render()
	c.Assert(err, gc.IsNil)
	c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered))
}

func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) {
	st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil)
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()
	waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))

	// We should have ca-cert.pem, rsyslog-cert.pem, and rsyslog-key.pem.
	caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem"))
	c.Assert(err, gc.IsNil)
	rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem"))
	c.Assert(err, gc.IsNil)
	rsyslogKeyPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-key.pem"))
	c.Assert(err, gc.IsNil)
	_, _, err = cert.ParseCertAndKey(string(rsyslogCertPEM), string(rsyslogKeyPEM))
	c.Assert(err, gc.IsNil)
	err = cert.Verify(string(rsyslogCertPEM), string(caCertPEM), time.Now().UTC())
	c.Assert(err, gc.IsNil)

	// Verify rsyslog configuration.
	waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf"))
	c.Assert(err, gc.IsNil)

	syslogPort := s.Conn.Environ.Config().SyslogPort()
	syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", []string{})
	syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir
	rendered, err := syslogConfig.Render()
	c.Assert(err, gc.IsNil)

	c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered))
}

func (s *RsyslogSuite) TestNamespace(c *gc.C) {
	st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
	// namespace only takes effect in filenames
	// for machine-0; all others assume isolation.
	s.testNamespace(c, st, "machine-0", "", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "machine-0", "mynamespace", "25-juju-mynamespace.conf", *rsyslog.LogDir+"-mynamespace")
	s.testNamespace(c, st, "machine-1", "", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "machine-1", "mynamespace", "25-juju.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "unit-myservice-0", "", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir)
	s.testNamespace(c, st, "unit-myservice-0", "mynamespace", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir)
}

// testNamespace starts a worker and ensures that
// the rsyslog config file has the expected filename,
// and the appropriate log dir is used.
func (s *RsyslogSuite) testNamespace(c *gc.C, st *api.State, tag, namespace, expectedFilename, expectedLogDir string) {
	restarted := make(chan struct{}, 2) // once for create, once for teardown
	s.PatchValue(rsyslog.RestartRsyslog, func() error {
		restarted <- struct{}{}
		return nil
	})

	err := os.MkdirAll(expectedLogDir, 0755)
	c.Assert(err, gc.IsNil)
	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, tag, namespace, []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()

	// Ensure that ca-cert.pem gets written to the expected log dir.
	waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem"))

	// Wait for rsyslog to be restarted, so we can check to see
	// what the name of the config file is.
	waitForRestart(c, restarted)
	dir, err := os.Open(*rsyslog.RsyslogConfDir)
	c.Assert(err, gc.IsNil)
	names, err := dir.Readdirnames(-1)
	dir.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(names, gc.HasLen, 1)
	c.Assert(names[0], gc.Equals, expectedFilename)
}

func (s *RsyslogSuite) TestConfigChange(c *gc.C) {
	var restarted bool
	s.PatchValue(rsyslog.RestartRsyslog, func() error {
		restarted = true
		return nil
	})

	st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits)
	handler, err := rsyslog.NewRsyslogConfigHandler(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, gc.IsNil)

	assertRestart := func(v bool) {
		err := handler.Handle()
		c.Assert(err, gc.IsNil)
		c.Assert(restarted, gc.Equals, v)
		restarted = false
		// Handling again should not restart, as no changes have been made.
		err = handler.Handle()
		c.Assert(err, gc.IsNil)
		c.Assert(restarted, jc.IsFalse)
	}

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert})
	c.Assert(err, gc.IsNil)
	assertRestart(true)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 1})
	c.Assert(err, gc.IsNil)
	assertRestart(true)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"unrelated": "anything"})
	c.Assert(err, gc.IsNil)
	assertRestart(false)

	err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 2})
	c.Assert(err, gc.IsNil)
	assertRestart(true)
}
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/rds"
)

func resourceAwsDbParameterGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDbParameterGroupCreate,
		Read:   resourceAwsDbParameterGroupRead,
		Update: resourceAwsDbParameterGroupUpdate,
		Delete: resourceAwsDbParameterGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
				ValidateFunc:  validateDbParamGroupName,
			},
			"name_prefix": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ForceNew:     true,
				ValidateFunc: validateDbParamGroupNamePrefix,
			},
			"family": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "Managed by Terraform",
			},
			"parameter": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: false,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"value": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"apply_method": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "immediate",
						},
					},
				},
				Set: resourceAwsDbParameterHash,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	var groupName string
	if v, ok := d.GetOk("name"); ok {
		groupName = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		groupName = resource.PrefixedUniqueId(v.(string))
	} else {
		groupName = resource.UniqueId()
	}
	d.Set("name", groupName)

	createOpts := rds.CreateDBParameterGroupInput{
		DBParameterGroupName:   aws.String(groupName),
		DBParameterGroupFamily: aws.String(d.Get("family").(string)),
		Description:            aws.String(d.Get("description").(string)),
		Tags:                   tags,
	}

	log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts)
	_, err := rdsconn.CreateDBParameterGroup(&createOpts)
	if err != nil {
		return fmt.Errorf("Error creating DB Parameter Group: %s", err)
	}

	d.Partial(true)
	d.SetPartial("name")
	d.SetPartial("family")
	d.SetPartial("description")
	d.Partial(false)

	d.SetId(*createOpts.DBParameterGroupName)
	log.Printf("[INFO] DB Parameter Group ID: %s", d.Id())

	return resourceAwsDbParameterGroupUpdate(d, meta)
}

func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	describeOpts := rds.DescribeDBParameterGroupsInput{
		DBParameterGroupName: aws.String(d.Id()),
	}

	describeResp, err := rdsconn.DescribeDBParameterGroups(&describeOpts)
	if err != nil {
		if isAWSErr(err, rds.ErrCodeDBParameterGroupNotFoundFault, "") {
			log.Printf("[WARN] DB Parameter Group (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	if len(describeResp.DBParameterGroups) != 1 ||
		*describeResp.DBParameterGroups[0].DBParameterGroupName != d.Id() {
		return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.DBParameterGroups)
	}

	d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName)
	d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily)
	d.Set("description", describeResp.DBParameterGroups[0].Description)

	configParams := d.Get("parameter").(*schema.Set)
	describeParametersOpts := rds.DescribeDBParametersInput{
		DBParameterGroupName: aws.String(d.Id()),
	}
	if configParams.Len() < 1 {
		// if we don't have any params in the ResourceData already, two possibilities
		// first, we don't have a config available to us. Second, we do, but it has
		// no parameters. We're going to assume the first, to be safe. In this case,
		// we're only going to ask for the user-modified values, because any defaults
		// the user may have _also_ set are indistinguishable from the hundreds of
		// defaults AWS sets. If the user hasn't set any parameters, this will return
		// an empty list anyways, so we just make some unnecessary requests. But in
		// the more common case (I assume) of an import, this will make fewer requests
		// and "do the right thing".
		describeParametersOpts.Source = aws.String("user")
	}

	var parameters []*rds.Parameter
	err = rdsconn.DescribeDBParametersPages(&describeParametersOpts,
		func(describeParametersResp *rds.DescribeDBParametersOutput, lastPage bool) bool {
			parameters = append(parameters, describeParametersResp.Parameters...)
			return !lastPage
		})
	if err != nil {
		return err
	}

	var userParams []*rds.Parameter
	if configParams.Len() < 1 {
		// if we have no config/no parameters in config, we've already asked for only
		// user-modified values, so we can just use the entire response.
		userParams = parameters
	} else {
		// if we have a config available to us, we have two possible classes of value
		// in the config. On the one hand, the user could have specified a parameter
		// that _actually_ changed things, in which case its Source would be set to
		// user. On the other, they may have specified a parameter that coincides with
		// the default value. In that case, the Source will be set to "system" or
		// "engine-default". We need to set the union of all "user" Source parameters
		// _and_ the "system"/"engine-default" Source parameters _that appear in the
		// config_ in the state, or the user gets a perpetual diff. See
		// terraform-providers/terraform-provider-aws#593 for more context and details.
		confParams, err := expandParameters(configParams.List())
		if err != nil {
			return err
		}
		for _, param := range parameters {
			if param.Source == nil || param.ParameterName == nil {
				continue
			}
			if *param.Source == "user" {
				userParams = append(userParams, param)
				continue
			}
			var paramFound bool
			for _, cp := range confParams {
				if cp.ParameterName == nil {
					continue
				}
				if *cp.ParameterName == *param.ParameterName {
					userParams = append(userParams, param)
					break
				}
			}
			if !paramFound {
				log.Printf("[DEBUG] Not persisting %s to state, as its source is %q and it isn't in the config", *param.ParameterName, *param.Source)
			}
		}
	}

	err = d.Set("parameter", flattenParameters(userParams))
	if err != nil {
		return fmt.Errorf("error setting 'parameter' in state: %#v", err)
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Service:   "rds",
		Region:    meta.(*AWSClient).region,
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("pg:%s", d.Id()),
	}.String()
	d.Set("arn", arn)
	resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{
		ResourceName: aws.String(arn),
	})

	if err != nil {
		log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn)
	}

	var dt []*rds.Tag
	if len(resp.TagList) > 0 {
		dt = resp.TagList
	}
	d.Set("tags", tagsToMapRDS(dt))

	return nil
}

func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	d.Partial(true)

	if d.HasChange("parameter") {
		o, n := d.GetChange("parameter")
		if o == nil {
			o = new(schema.Set)
		}
		if n == nil {
			n = new(schema.Set)
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
		parameters, err := expandParameters(ns.Difference(os).List())
		if err != nil {
			return err
		}

		if len(parameters) > 0 {
			// We can only modify 20 parameters at a time, so walk them until
			// we've got them all.
			maxParams := 20
			for parameters != nil {
				paramsToModify := make([]*rds.Parameter, 0)
				if len(parameters) <= maxParams {
					paramsToModify, parameters = parameters[:], nil
				} else {
					paramsToModify, parameters = parameters[:maxParams], parameters[maxParams:]
				}
				modifyOpts := rds.ModifyDBParameterGroupInput{
					DBParameterGroupName: aws.String(d.Get("name").(string)),
					Parameters:           paramsToModify,
				}

				log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts)
				_, err = rdsconn.ModifyDBParameterGroup(&modifyOpts)
				if err != nil {
					return fmt.Errorf("Error modifying DB Parameter Group: %s", err)
				}
			}
			d.SetPartial("parameter")
		}
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Service:   "rds",
		Region:    meta.(*AWSClient).region,
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("pg:%s", d.Id()),
	}.String()
	if err := setTagsRDS(rdsconn, d, arn); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	d.Partial(false)

	return resourceAwsDbParameterGroupRead(d, meta)
}

func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	return resource.Retry(3*time.Minute, func() *resource.RetryError {
		deleteOpts := rds.DeleteDBParameterGroupInput{
			DBParameterGroupName: aws.String(d.Id()),
		}

		_, err := conn.DeleteDBParameterGroup(&deleteOpts)
		if err != nil {
			awsErr, ok := err.(awserr.Error)
			if ok && awsErr.Code() == "DBParameterGroupNotFoundFault" {
				return resource.RetryableError(err)
			}
			if ok && awsErr.Code() == "InvalidDBParameterGroupState" {
				return resource.RetryableError(err)
			}
		}
		return resource.NonRetryableError(err)
	})
}

func resourceAwsDbParameterHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	// Store the value as a lower case string, to match how we store them in flattenParameters
	buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string))))

	return hashcode.String(buf.String())
}
<commit_msg>set arn from parameter group response<commit_after>package aws

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/rds"
)

func resourceAwsDbParameterGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDbParameterGroupCreate,
		Read:   resourceAwsDbParameterGroupRead,
		Update: resourceAwsDbParameterGroupUpdate,
		Delete: resourceAwsDbParameterGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
				ValidateFunc:  validateDbParamGroupName,
			},
			"name_prefix": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ForceNew:     true,
				ValidateFunc: validateDbParamGroupNamePrefix,
			},
			"family": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "Managed by Terraform",
			},
			"parameter": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: false,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"value": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"apply_method": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "immediate",
						},
					},
				},
				Set: resourceAwsDbParameterHash,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	var groupName string
	if v, ok := d.GetOk("name"); ok {
		groupName = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		groupName = resource.PrefixedUniqueId(v.(string))
	} else {
		groupName = resource.UniqueId()
	}
	d.Set("name", groupName)

	createOpts := rds.CreateDBParameterGroupInput{
		DBParameterGroupName:   aws.String(groupName),
		DBParameterGroupFamily: aws.String(d.Get("family").(string)),
		Description:            aws.String(d.Get("description").(string)),
		Tags:                   tags,
	}

	log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts)
	resp, err := rdsconn.CreateDBParameterGroup(&createOpts)
	if err != nil {
		return fmt.Errorf("Error creating DB Parameter Group: %s", err)
	}

	d.Partial(true)
	d.SetPartial("name")
	d.SetPartial("family")
	d.SetPartial("description")
	d.Partial(false)

	d.SetId(aws.StringValue(resp.DBParameterGroup.DBParameterGroupName))
	d.Set("arn", resp.DBParameterGroup.DBParameterGroupArn)
	log.Printf("[INFO] DB Parameter Group ID: %s", d.Id())

	return resourceAwsDbParameterGroupUpdate(d, meta)
}

func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	describeOpts := rds.DescribeDBParameterGroupsInput{
		DBParameterGroupName: aws.String(d.Id()),
	}

	describeResp, err := rdsconn.DescribeDBParameterGroups(&describeOpts)
	if err != nil {
		if isAWSErr(err, rds.ErrCodeDBParameterGroupNotFoundFault, "") {
			log.Printf("[WARN] DB Parameter Group (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	if len(describeResp.DBParameterGroups) != 1 ||
		*describeResp.DBParameterGroups[0].DBParameterGroupName != d.Id() {
		return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.DBParameterGroups)
	}

	d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName)
	d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily)
	d.Set("description", describeResp.DBParameterGroups[0].Description)

	configParams := d.Get("parameter").(*schema.Set)
	describeParametersOpts := rds.DescribeDBParametersInput{
		DBParameterGroupName: aws.String(d.Id()),
	}
	if configParams.Len() < 1 {
		// if we don't have any params in the ResourceData already, two possibilities
		// first, we don't have a config available to us. Second, we do, but it has
		// no parameters. We're going to assume the first, to be safe. In this case,
		// we're only going to ask for the user-modified values, because any defaults
		// the user may have _also_ set are indistinguishable from the hundreds of
		// defaults AWS sets. If the user hasn't set any parameters, this will return
		// an empty list anyways, so we just make some unnecessary requests. But in
		// the more common case (I assume) of an import, this will make fewer requests
		// and "do the right thing".
		describeParametersOpts.Source = aws.String("user")
	}

	var parameters []*rds.Parameter
	err = rdsconn.DescribeDBParametersPages(&describeParametersOpts,
		func(describeParametersResp *rds.DescribeDBParametersOutput, lastPage bool) bool {
			parameters = append(parameters, describeParametersResp.Parameters...)
			return !lastPage
		})
	if err != nil {
		return err
	}

	var userParams []*rds.Parameter
	if configParams.Len() < 1 {
		// if we have no config/no parameters in config, we've already asked for only
		// user-modified values, so we can just use the entire response.
		userParams = parameters
	} else {
		// if we have a config available to us, we have two possible classes of value
		// in the config. On the one hand, the user could have specified a parameter
		// that _actually_ changed things, in which case its Source would be set to
		// user. On the other, they may have specified a parameter that coincides with
		// the default value. In that case, the Source will be set to "system" or
		// "engine-default". We need to set the union of all "user" Source parameters
		// _and_ the "system"/"engine-default" Source parameters _that appear in the
		// config_ in the state, or the user gets a perpetual diff. See
		// terraform-providers/terraform-provider-aws#593 for more context and details.
		confParams, err := expandParameters(configParams.List())
		if err != nil {
			return err
		}
		for _, param := range parameters {
			if param.Source == nil || param.ParameterName == nil {
				continue
			}
			if *param.Source == "user" {
				userParams = append(userParams, param)
				continue
			}
			var paramFound bool
			for _, cp := range confParams {
				if cp.ParameterName == nil {
					continue
				}
				if *cp.ParameterName == *param.ParameterName {
					userParams = append(userParams, param)
					break
				}
			}
			if !paramFound {
				log.Printf("[DEBUG] Not persisting %s to state, as its source is %q and it isn't in the config", *param.ParameterName, *param.Source)
			}
		}
	}

	err = d.Set("parameter", flattenParameters(userParams))
	if err != nil {
		return fmt.Errorf("error setting 'parameter' in state: %#v", err)
	}

	arn := aws.StringValue(describeResp.DBParameterGroups[0].DBParameterGroupArn)
	d.Set("arn", arn)

	resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{
		ResourceName: aws.String(arn),
	})

	if err != nil {
		log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn)
	}

	var dt []*rds.Tag
	if len(resp.TagList) > 0 {
		dt = resp.TagList
	}
	d.Set("tags", tagsToMapRDS(dt))

	return nil
}

func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	d.Partial(true)

	if d.HasChange("parameter") {
		o, n := d.GetChange("parameter")
		if o == nil {
			o = new(schema.Set)
		}
		if n == nil {
			n = new(schema.Set)
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
		parameters, err := expandParameters(ns.Difference(os).List())
		if err != nil {
			return err
		}

		if len(parameters) > 0 {
			// We can only modify 20 parameters at a time, so walk them until
			// we've got them all.
			maxParams := 20
			for parameters != nil {
				paramsToModify := make([]*rds.Parameter, 0)
				if len(parameters) <= maxParams {
					paramsToModify, parameters = parameters[:], nil
				} else {
					paramsToModify, parameters = parameters[:maxParams], parameters[maxParams:]
				}
				modifyOpts := rds.ModifyDBParameterGroupInput{
					DBParameterGroupName: aws.String(d.Get("name").(string)),
					Parameters:           paramsToModify,
				}

				log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts)
				_, err = rdsconn.ModifyDBParameterGroup(&modifyOpts)
				if err != nil {
					return fmt.Errorf("Error modifying DB Parameter Group: %s", err)
				}
			}
			d.SetPartial("parameter")
		}
	}

	if err := setTagsRDS(rdsconn, d, d.Get("arn").(string)); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	d.Partial(false)

	return resourceAwsDbParameterGroupRead(d, meta)
}

func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	return resource.Retry(3*time.Minute, func() *resource.RetryError {
		deleteOpts := rds.DeleteDBParameterGroupInput{
			DBParameterGroupName: aws.String(d.Id()),
		}

		_, err := conn.DeleteDBParameterGroup(&deleteOpts)
		if err != nil {
			awsErr, ok := err.(awserr.Error)
			if ok && awsErr.Code() == "DBParameterGroupNotFoundFault" {
				return resource.RetryableError(err)
			}
			if ok && awsErr.Code() == "InvalidDBParameterGroupState" {
				return resource.RetryableError(err)
			}
		}
		return resource.NonRetryableError(err)
	})
}

func resourceAwsDbParameterHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	// Store the value as a lower case string, to match how we store them in flattenParameters
	buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string))))

	return hashcode.String(buf.String())
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2015, UPMC Enterprises
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"k8s.io/client-go/1.4/kubernetes"
	"k8s.io/client-go/1.4/pkg/api/v1"
	"k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/rest"
)

var (
	argListenPort        = flag.Int("listen-port", 9080, "port to have API listen")
	argDockerRegistry    = flag.String("docker-registry", "", "docker registry to use")
	argKubecfgFile       = flag.String("kubecfg-file", "", "Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens")
	argKubeMasterURL     = flag.String("kube-master-url", "", "URL to reach kubernetes master. Env variables in this flag will be expanded.")
	argTemplateNamespace = flag.String("template-namespace", "template", "Namespace to 'clone from when creating new deployments'")
	argPathToTokens      = flag.String("path-to-tokens", "", "Full path including file name to tokens file for authorization, setting to empty string will disable.")
	argSubDomain         = flag.String("subdomain", "k8s.local.com", "Subdomain used to configure external routing to branch (e.g. namespace.ci.k8s.local)")
	client               *kubernetes.Clientset
	defaultReplicaCount  *int32
)

const (
	appVersion = "0.0.3"
)

// Default (GET "/")
func indexRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %s", "welcome to Emmie!")
}

// Version (GET "/version")
func versionRoute(w http.ResponseWriter, r *http.Request) {
	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	fmt.Fprintf(w, "%q", appVersion)
}

// Deploy (POST "/deploy/namespace/branchName")
func deployRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	imageNamespace := vars["namespace"]

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)
	log.Println("[Emmie] is deploying branch:", branchName)

	// create namespace
	err := createNamespace(branchName)

	if err != nil {
		// TODO: Don't use error for logic
		// Existing namespace, do an update
		log.Println("Existing namespace found: ", branchName, " deleting pods.")

		deletePodsByNamespace(branchName)
	} else {
		log.Println("Namespace created, deploying new app...")

		// copy controllers / services based on label query
		rcs, _ := listReplicationControllersByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(rcs.Items), " template replication controllers to copy.")

		deployments, _ := listDeploymentsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(deployments.Items), " template deployments to copy.")

		svcs, _ := listServicesByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(svcs.Items), " template services to copy.")

		secrets, _ := listSecretsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(secrets.Items), " template secrets to copy.")

		configmaps, _ := listConfigMapsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(configmaps.Items), " template configmaps to copy.")

		// create configmaps
		for _, configmap := range configmaps.Items {

			requestConfigMap := &v1.ConfigMap{
				ObjectMeta: v1.ObjectMeta{
					Name:      configmap.Name,
					Namespace: branchName,
				},
				Data: configmap.Data,
			}

			createConfigMap(branchName, requestConfigMap)
		}

		// create secrets
		for _, secret := range secrets.Items {

			// skip service accounts
			if secret.Type != "kubernetes.io/service-account-token" {

				requestSecret := &v1.Secret{
					ObjectMeta: v1.ObjectMeta{
						Name:      secret.Name,
						Namespace: branchName,
					},
					Type: secret.Type,
					Data: secret.Data,
				}

				createSecret(branchName, requestSecret)
			}
		}

		// create services
		for _, svc := range svcs.Items {

			// Create annotations for Deis router
			annotations := make(map[string]string)
			annotations["router.deis.io/domains"] = fmt.Sprintf("%s,www.%s.%s", branchName, branchName, argSubDomain)

			// Add Deis router label
			svc.Labels["router.deis.io/routable"] = "true"

			requestService := &v1.Service{
				ObjectMeta: v1.ObjectMeta{
					Name:        svc.ObjectMeta.Name,
					Namespace:   branchName,
					Annotations: annotations,
				},
			}

			ports := []v1.ServicePort{}
			for _, port := range svc.Spec.Ports {
				newPort := v1.ServicePort{
					Name:       port.Name,
					Protocol:   port.Protocol,
					Port:       port.Port,
					TargetPort: port.TargetPort,
				}

				ports = append(ports, newPort)
			}

			requestService.Spec.Ports = ports
			requestService.Spec.Selector = svc.Spec.Selector
			requestService.Spec.Type = svc.Spec.Type
			requestService.Labels = svc.Labels

			createService(branchName, requestService)
		}

		// now that we have all replicationControllers, update them to have new image name
		for _, rc := range rcs.Items {

			containerNameToUpdate := ""

			// Looks for annotations to know which container to replace
			for key, value := range rc.Annotations {
				if key == "emmie-update" {
					containerNameToUpdate = value
				}
			}

			// Find the container which matches the annotation
			for i, container := range rc.Spec.Template.Spec.Containers {

				imageName := ""

				if containerNameToUpdate == "" {
					//default to current image tag if no annotations found
					imageName = container.Image
				} else {
					imageName = fmt.Sprintf("%s%s/%s:%s", *argDockerRegistry, imageNamespace, rc.ObjectMeta.Labels["name"], branchName)
				}

				rc.Spec.Template.Spec.Containers[i].Image = imageName

				// Set the image pull policy to "Always"
				rc.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
			}

			requestController := &v1.ReplicationController{
				ObjectMeta: v1.ObjectMeta{
					Name:      rc.ObjectMeta.Name,
					Namespace: branchName,
				},
			}

			requestController.Spec = rc.Spec
			requestController.Spec.Replicas = defaultReplicaCount

			// create new replication controller
			createReplicationController(branchName, requestController)
		}

		// now that we have all deployments, update them to have new image name
		for _, dply := range deployments.Items {

			containerNameToUpdate := ""

			// Looks for annotations to know which container to replace
			for key, value := range dply.Annotations {
				if key == "emmie-update" {
					containerNameToUpdate = value
				}
			}

			// Find the container which matches the annotation
			for i, container := range dply.Spec.Template.Spec.Containers {

				imageName := ""

				if containerNameToUpdate == "" {
					//default to current image tag if no annotations found
					imageName = container.Image
				} else {
					imageName = fmt.Sprintf("%s%s/%s:%s", *argDockerRegistry, imageNamespace, dply.ObjectMeta.Labels["name"], branchName)
				}

				dply.Spec.Template.Spec.Containers[i].Image = imageName

				// Set the image pull policy to "Always"
				dply.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
			}

			deployment := &v1beta1.Deployment{
				ObjectMeta: v1.ObjectMeta{
					Name:      dply.ObjectMeta.Name,
					Namespace: branchName,
				},
			}

			deployment.Spec = dply.Spec
			deployment.Spec.Replicas = defaultReplicaCount

			// create new replication controller
			createDeployment(branchName, deployment)
		}
	}
	log.Println("[Emmie] is finished deploying branch!")
}

// Put (PUT "/deploy")
func updateRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	log.Println(w, "[Emmie] is updating branch:", branchName)

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)

	deletePodsByNamespace(branchName)

	log.Println("Finished updating branch!")
}

// Delete (DELETE "/deploy")
func deleteRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	log.Println("[Emmie] is deleting branch:", branchName)

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)

	// get controllers / services / secrets in namespace
	rcs, _ := listReplicationControllersByNamespace(*argTemplateNamespace)
	for _, rc := range rcs.Items {
		deleteReplicationController(branchName, rc.ObjectMeta.Name)
		log.Println("Deleted replicationController:", rc.ObjectMeta.Name)
	}

	deployments, _ := listDeploymentsByNamespace(*argTemplateNamespace)
	for _, dply := range deployments.Items {
		deleteDeployment(branchName, dply.ObjectMeta.Name)
		log.Println("Deleted deployment:", dply.ObjectMeta.Name)
	}

	svcs, _ := listServicesByNamespace(*argTemplateNamespace)
	for _, svc := range svcs.Items {
		deleteService(branchName, svc.ObjectMeta.Name)
		log.Println("Deleted service:", svc.ObjectMeta.Name)
	}

	secrets, _ := listSecretsByNamespace(*argTemplateNamespace)
	for _, secret := range secrets.Items {
		deleteSecret(branchName, secret.ObjectMeta.Name)
		log.Println("Deleted secret:", secret.ObjectMeta.Name)
	}

	configmaps, _ := listConfigMapsByNamespace(*argTemplateNamespace)
	for _, configmap := range configmaps.Items {
		deleteSecret(branchName, configmap.ObjectMeta.Name)
		log.Println("Deleted configmap:", configmap.ObjectMeta.Name)
	}

	deleteNamespace(branchName)
	log.Println("[Emmie] is done deleting branch.")
}

func tokenIsValid(token string) bool {
	// If no path is passed, then auth is disabled
	if *argPathToTokens == "" {
		return true
	}

	file, err := os.Open(*argPathToTokens)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if token == scanner.Text() {
			fmt.Println("Token IS valid!")
			return true
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	fmt.Println("Token is NOT valid! =(")
	return false
}

func main() {
	flag.Parse()
	log.Println("[Emmie] is up and running!", time.Now())

	// Sanitize docker registry
	if *argDockerRegistry != "" {
		*argDockerRegistry = fmt.Sprintf("%s/", *argDockerRegistry)
	}

	// Configure router
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", indexRoute)
	router.HandleFunc("/deploy/{namespace}/{branchName}", deployRoute).Methods("POST")
	router.HandleFunc("/deploy/{branchName}", deleteRoute).Methods("DELETE")
	router.HandleFunc("/deploy/{branchName}", updateRoute).Methods("PUT")
	router.HandleFunc("/deploy", getDeploymentsRoute).Methods("GET")

	// Services
	// router.HandleFunc("/services/{namespace}/{serviceName}", getServiceRoute).Methods("GET")
	// router.HandleFunc("/services/{namespace}/{key}/{value}", getServicesRoute).Methods("GET")

	// ReplicationControllers
	// router.HandleFunc("/replicationControllers/{namespace}/{rcName}", getReplicationControllerRoute).Methods("GET")
	// router.HandleFunc("/replicationControllers/{namespace}/{key}/{value}", getReplicationControllersRoute).Methods("GET")

	// Deployments
	// router.HandleFunc("/deployments/{namespace}/{deploymentName}", getDeploymentRoute).Methods("GET")
	// router.HandleFunc("/deployments/{namespace}/{key}/{value}", getDeploymentsRoute).Methods("GET")

	// Version
	router.HandleFunc("/version", versionRoute)

	// Create k8s client
	config, err := rest.InClusterConfig()
	//config, err := clientcmd.BuildConfigFromFlags("", *argKubecfgFile)
	if err != nil {
		panic(err.Error())
	}

	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	client = clientset

	// Start server
	log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", *argListenPort), "certs/cert.pem", "certs/key.pem", router))
	//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *argListenPort), router))
}
<commit_msg>Bug fix on router subdomain<commit_after>/*
Copyright (c) 2015, UPMC Enterprises
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"k8s.io/client-go/1.4/kubernetes"
	"k8s.io/client-go/1.4/pkg/api/v1"
	"k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/rest"
)

var (
	argListenPort        = flag.Int("listen-port", 9080, "port to have API listen")
	argDockerRegistry    = flag.String("docker-registry", "", "docker registry to use")
	argKubecfgFile       = flag.String("kubecfg-file", "", "Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens")
	argKubeMasterURL     = flag.String("kube-master-url", "", "URL to reach kubernetes master. Env variables in this flag will be expanded.")
	argTemplateNamespace = flag.String("template-namespace", "template", "Namespace to 'clone from when creating new deployments'")
	argPathToTokens      = flag.String("path-to-tokens", "", "Full path including file name to tokens file for authorization, setting to empty string will disable.")
	argSubDomain         = flag.String("subdomain", "k8s.local.com", "Subdomain used to configure external routing to branch (e.g. namespace.ci.k8s.local)")
	client               *kubernetes.Clientset
	defaultReplicaCount  *int32
)

const (
	appVersion = "0.0.3"
)

// Default (GET "/")
func indexRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %s", "welcome to Emmie!")
}

// Version (GET "/version")
func versionRoute(w http.ResponseWriter, r *http.Request) {
	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	fmt.Fprintf(w, "%q", appVersion)
}

// Deploy (POST "/deploy/namespace/branchName")
func deployRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	imageNamespace := vars["namespace"]

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)
	log.Println("[Emmie] is deploying branch:", branchName)

	// create namespace
	err := createNamespace(branchName)

	if err != nil {
		// TODO: Don't use error for logic
		// Existing namespace, do an update
		log.Println("Existing namespace found: ", branchName, " deleting pods.")

		deletePodsByNamespace(branchName)
	} else {
		log.Println("Namespace created, deploying new app...")

		// copy controllers / services based on label query
		rcs, _ := listReplicationControllersByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(rcs.Items), " template replication controllers to copy.")

		deployments, _ := listDeploymentsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(deployments.Items), " template deployments to copy.")

		svcs, _ := listServicesByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(svcs.Items), " template services to copy.")

		secrets, _ := listSecretsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(secrets.Items), " template secrets to copy.")

		configmaps, _ := listConfigMapsByNamespace(*argTemplateNamespace)
		log.Println("Found ", len(configmaps.Items), " template configmaps to copy.")

		// create configmaps
		for _, configmap := range configmaps.Items {

			requestConfigMap := &v1.ConfigMap{
				ObjectMeta: v1.ObjectMeta{
					Name:      configmap.Name,
					Namespace: branchName,
				},
				Data: configmap.Data,
			}

			createConfigMap(branchName, requestConfigMap)
		}

		// create secrets
		for _, secret := range secrets.Items {

			// skip service accounts
			if secret.Type != "kubernetes.io/service-account-token" {

				requestSecret := &v1.Secret{
					ObjectMeta: v1.ObjectMeta{
						Name:      secret.Name,
						Namespace: branchName,
					},
					Type: secret.Type,
					Data: secret.Data,
				}

				createSecret(branchName, requestSecret)
			}
		}

		// create services
		for _, svc := range svcs.Items {

			// Create annotations for Deis router
			annotations := make(map[string]string)
			annotations["router.deis.io/domains"] = fmt.Sprintf("%s,www.%s.%s", branchName, branchName, *argSubDomain)

			// Add Deis router label
			svc.Labels["router.deis.io/routable"] = "true"

			requestService := &v1.Service{
				ObjectMeta: v1.ObjectMeta{
					Name:        svc.ObjectMeta.Name,
					Namespace:   branchName,
					Annotations: annotations,
				},
			}

			ports := []v1.ServicePort{}
			for _, port := range svc.Spec.Ports {
				newPort := v1.ServicePort{
					Name:       port.Name,
					Protocol:   port.Protocol,
					Port:       port.Port,
					TargetPort: port.TargetPort,
				}

				ports = append(ports, newPort)
			}

			requestService.Spec.Ports = ports
			requestService.Spec.Selector = svc.Spec.Selector
			requestService.Spec.Type = svc.Spec.Type
			requestService.Labels = svc.Labels

			createService(branchName, requestService)
		}

		// now that we have all replicationControllers, update them to have new image name
		for _, rc := range rcs.Items {

			containerNameToUpdate := ""

			// Looks for annotations to know which container to replace
			for key, value := range rc.Annotations {
				if key == "emmie-update" {
					containerNameToUpdate = value
				}
			}

			// Find the container which matches the annotation
			for i, container := range rc.Spec.Template.Spec.Containers {

				imageName := ""

				if containerNameToUpdate == "" {
					//default to current image tag if no annotations found
					imageName = container.Image
				} else {
					imageName = fmt.Sprintf("%s%s/%s:%s", *argDockerRegistry, imageNamespace, rc.ObjectMeta.Labels["name"], branchName)
				}

				rc.Spec.Template.Spec.Containers[i].Image = imageName

				// Set the image pull policy to "Always"
				rc.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
			}

			requestController := &v1.ReplicationController{
				ObjectMeta: v1.ObjectMeta{
					Name:      rc.ObjectMeta.Name,
					Namespace: branchName,
				},
			}

			requestController.Spec = rc.Spec
			requestController.Spec.Replicas = defaultReplicaCount

			// create new replication controller
			createReplicationController(branchName, requestController)
		}

		// now that we have all deployments, update them to have new image name
		for _, dply := range deployments.Items {

			containerNameToUpdate := ""

			// Looks for annotations to know which container to replace
			for key, value := range dply.Annotations {
				if key == "emmie-update" {
					containerNameToUpdate = value
				}
			}

			// Find the container which matches the annotation
			for i, container := range dply.Spec.Template.Spec.Containers {

				imageName := ""

				if containerNameToUpdate == "" {
					//default to current image tag if no annotations found
					imageName = container.Image
				} else {
					imageName = fmt.Sprintf("%s%s/%s:%s", *argDockerRegistry, imageNamespace, dply.ObjectMeta.Labels["name"], branchName)
				}

				dply.Spec.Template.Spec.Containers[i].Image = imageName

				// Set the image pull policy to "Always"
				dply.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
			}

			deployment := &v1beta1.Deployment{
				ObjectMeta: v1.ObjectMeta{
					Name:      dply.ObjectMeta.Name,
					Namespace: branchName,
				},
			}

			deployment.Spec = dply.Spec
			deployment.Spec.Replicas = defaultReplicaCount

			// create new replication controller
			createDeployment(branchName, deployment)
		}
	}
	log.Println("[Emmie] is finished deploying branch!")
}

// Put (PUT "/deploy")
func updateRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	log.Println(w, "[Emmie] is updating branch:", branchName)

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)

	deletePodsByNamespace(branchName)

	log.Println("Finished updating branch!")
}

// Delete (DELETE "/deploy")
func deleteRoute(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	branchName := vars["branchName"]
	log.Println("[Emmie] is deleting branch:", branchName)

	if !tokenIsValid(r.FormValue("token")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// sanitize BranchName
	branchName = strings.Replace(branchName, "_", "-", -1)

	// get controllers / services / secrets in namespace
	rcs, _ := listReplicationControllersByNamespace(*argTemplateNamespace)
	for _, rc := range rcs.Items {
		deleteReplicationController(branchName, rc.ObjectMeta.Name)
		log.Println("Deleted replicationController:", rc.ObjectMeta.Name)
	}

	deployments, _ := listDeploymentsByNamespace(*argTemplateNamespace)
	for _, dply := range deployments.Items {
		deleteDeployment(branchName, dply.ObjectMeta.Name)
		log.Println("Deleted deployment:", dply.ObjectMeta.Name)
	}

	svcs, _ := listServicesByNamespace(*argTemplateNamespace)
	for _, svc := range svcs.Items {
		deleteService(branchName, svc.ObjectMeta.Name)
		log.Println("Deleted service:", svc.ObjectMeta.Name)
	}

	secrets, _ := listSecretsByNamespace(*argTemplateNamespace)
	for _, secret := range secrets.Items {
		deleteSecret(branchName, secret.ObjectMeta.Name)
		log.Println("Deleted secret:", secret.ObjectMeta.Name)
	}

	configmaps, _ := listConfigMapsByNamespace(*argTemplateNamespace)
	for _, configmap := range configmaps.Items {
		deleteSecret(branchName, configmap.ObjectMeta.Name)
		log.Println("Deleted configmap:", configmap.ObjectMeta.Name)
	}

	deleteNamespace(branchName)
	log.Println("[Emmie] is done deleting branch.")
}

func tokenIsValid(token string) bool {
	// If no path is passed, then auth is disabled
	if *argPathToTokens == "" {
		return true
	}

	file, err := os.Open(*argPathToTokens)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if token == scanner.Text() {
			fmt.Println("Token IS valid!")
			return true
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	fmt.Println("Token is NOT valid! =(")
	return false
}

func main() {
	flag.Parse()
	log.Println("[Emmie] is up and running!", time.Now())

	// Sanitize docker registry
	if *argDockerRegistry != "" {
		*argDockerRegistry = fmt.Sprintf("%s/", *argDockerRegistry)
	}

	// Configure router
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", indexRoute)
	router.HandleFunc("/deploy/{namespace}/{branchName}", deployRoute).Methods("POST")
	router.HandleFunc("/deploy/{branchName}", deleteRoute).Methods("DELETE")
	router.HandleFunc("/deploy/{branchName}", updateRoute).Methods("PUT")
	router.HandleFunc("/deploy", getDeploymentsRoute).Methods("GET")

	// Services
	// router.HandleFunc("/services/{namespace}/{serviceName}", getServiceRoute).Methods("GET")
	// router.HandleFunc("/services/{namespace}/{key}/{value}", getServicesRoute).Methods("GET")

	// ReplicationControllers
	// router.HandleFunc("/replicationControllers/{namespace}/{rcName}", getReplicationControllerRoute).Methods("GET")
	// router.HandleFunc("/replicationControllers/{namespace}/{key}/{value}", getReplicationControllersRoute).Methods("GET")

	// Deployments
	// router.HandleFunc("/deployments/{namespace}/{deploymentName}", getDeploymentRoute).Methods("GET")
	// router.HandleFunc("/deployments/{namespace}/{key}/{value}", getDeploymentsRoute).Methods("GET")

	// Version
	router.HandleFunc("/version", versionRoute)

	// Create k8s client
	config, err := rest.InClusterConfig()
	//config, err := clientcmd.BuildConfigFromFlags("", *argKubecfgFile)
	if err != nil {
		panic(err.Error())
	}

	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	client = clientset

	// Start server
	log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", *argListenPort), "certs/cert.pem", "certs/key.pem", router))
	//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *argListenPort), router))
}
<|endoftext|>


<commit_before>package writesplitter

import (
	"errors"
	"io"
	"os"
	"time"
)

const (
	Kilobyte  = 1024                // const for specifying ByteLimit
	Megabyte  = Kilobyte * Kilobyte // const for specifying ByteLimit
	formatStr = "2006-01-02T15.04.05.999999999Z0700.log"
)

var (
	ErrNotAFile = errors.New("WriteSplitter: invalid memory address or nil pointer dereference") // a custom error to signal that no file was closed
)

// WriteSplitter represents a disk bound io.WriteCloser that splits the input
// across consecutively named files based on either the number of bytes or the
// number of lines. Splitting does not guarantee true byte/line split
// precision as it does not parse the incoming data. The decision to split is
// before the underlying write operation based on the previous invocation. In
// other words, if a []byte sent to `Write()` contains enough bytes or new
// lines ('\n') to exceed the given limit, a new file won't be generated until
// the *next* invocation of `Write()`. If both LineLimit and ByteLimit are set,
// preference is given to LineLimit. By default, no splitting occurs because
// both LineLimit and ByteLimit are zero (0).
type WriteSplitter struct {
	LineLimit int            // how many write ops (typically one per line) before splitting the file
	ByteLimit int            // how many bytes before splitting the file
	Prefix    string         // files are named: $prefix + $nano-precision-timestamp + '.log'
	numBytes  int            // internal byte count
	numLines  int            // internal line count
	handle    io.WriteCloser // embedded file
}

// LineSplitter returns a WriteSplitter set to split at the given number of lines
func LineSplitter(limit int, prefix string) io.WriteCloser {
	return &WriteSplitter{LineLimit: limit, Prefix: prefix}
}

// ByteSplitter returns a WriteSplitter set to split at the given number of bytes
func ByteSplitter(limit int, prefix string) io.WriteCloser {
	return &WriteSplitter{ByteLimit: limit, Prefix: prefix}
}

// Close is a passthru and satisfies io.Closer. Subsequent writes will return an
// error.
func (ws *WriteSplitter) Close() error {
	if ws.handle != nil { // do not try to close nil
		return ws.handle.Close()
	}
	return ErrNotAFile // do not hide errors, but signal it's a WriteSplit error as opposed to an underlying os.* error
}

// Write satisfies io.Writer and internally manages file io. Write also limits
// each WriteSplitter to only one open file at a time.
func (ws *WriteSplitter) Write(p []byte) (int, error) {

	var n int
	var e error

	if ws.handle == nil {
		ws.handle, e = createFile(ws.Prefix)
	}

	switch {
	case ws.LineLimit > 0 && ws.numLines >= ws.LineLimit:
		fallthrough
	case ws.ByteLimit > 0 && ws.numBytes >= ws.ByteLimit:
		ws.Close()
		ws.handle, e = createFile(ws.Prefix)
		ws.numLines, ws.numBytes = 0, 0
	}

	if e != nil {
		return 0, e
	}

	n, e = ws.handle.Write(p)
	ws.numLines += 1
	ws.numBytes += n
	return n, e
}

// TestFileIO creates and removes a file to ensure that the location is writable.
func TestFileIO(prefix string) error {
	fn := prefix + "test.log"
	// It doesn't use the fs layer because it should be used to test the
	// writability of the actual filesystem. This test is unnecessary for mock filesystems
	if _, err := createFile(fn); err != nil {
		return err
	}
	removeFile(fn)
	return nil
}

/// This is for mocking the file IO. Used exclusively for testing
///-----------------------------------------------------------------------------

// createFile is the file creating function that wraps os.Create
var createFile = func(prefix string) (io.WriteCloser, error) {
	name := prefix + time.Now().Format(formatStr)
	return os.Create(name)
}

// removeFile is the file removing function that wraps os.Remove
var removeFile = func(name string) error {
	return os.Remove(name)
}
<commit_msg>func for naming things<commit_after>package writesplitter

import (
	"errors"
	"io"
	"os"
	"time"
	// "path/filepath"
)

const (
	Kilobyte  = 1024                // const for specifying ByteLimit
	Megabyte  = Kilobyte * Kilobyte // const for specifying ByteLimit
	formatStr = "2006-01-02T15.04.05.999999999Z0700.log"
)

var (
	ErrNotAFile = errors.New("WriteSplitter: invalid memory address or nil pointer dereference") // a custom error to signal that no file was closed
)

// WriteSplitter represents a disk bound io.WriteCloser that splits the input
// across consecutively named files based on either the number of bytes or the
// number of lines. Splitting does not guarantee true byte/line split
// precision as it does not parse the incoming data. The decision to split is
// before the underlying write operation based on the previous invocation. In
// other words, if a []byte sent to `Write()` contains enough bytes or new
// lines ('\n') to exceed the given limit, a new file won't be generated until
// the *next* invocation of `Write()`. If both LineLimit and ByteLimit are set,
// preference is given to LineLimit. By default, no splitting occurs because
// both LineLimit and ByteLimit are zero (0).
type WriteSplitter struct {
	LineLimit int            // how many write ops (typically one per line) before splitting the file
	ByteLimit int            // how many bytes before splitting the file
	Prefix    string         // files are named: $prefix + $nano-precision-timestamp + '.log'
	numBytes  int            // internal byte count
	numLines  int            // internal line count
	handle    io.WriteCloser // embedded file
}

// LineSplitter returns a WriteSplitter set to split at the given number of lines
func LineSplitter(limit int, prefix string) io.WriteCloser {
	return &WriteSplitter{LineLimit: limit, Prefix: prefix}
}

// ByteSplitter returns a WriteSplitter set to split at the given number of bytes
func ByteSplitter(limit int, prefix string) io.WriteCloser {
	return &WriteSplitter{ByteLimit: limit, Prefix: prefix}
}

// Close is a passthru and satisfies io.Closer. Subsequent writes will return an
// error.
func (ws *WriteSplitter) Close() error {
	if ws.handle != nil { // do not try to close nil
		return ws.handle.Close()
	}
	return ErrNotAFile // do not hide errors, but signal it's a WriteSplit error as opposed to an underlying os.* error
}

// Write satisfies io.Writer and internally manages file io. Write also limits
// each WriteSplitter to only one open file at a time.
func (ws *WriteSplitter) Write(p []byte) (int, error) {

	var n int
	var e error

	if ws.handle == nil {
		ws.handle, e = createFile(fileName(ws.Prefix))
	}

	switch {
	case ws.LineLimit > 0 && ws.numLines >= ws.LineLimit:
		fallthrough
	case ws.ByteLimit > 0 && ws.numBytes >= ws.ByteLimit:
		ws.Close()
		ws.handle, e = createFile(fileName(ws.Prefix))
		ws.numLines, ws.numBytes = 0, 0
	}

	if e != nil {
		return 0, e
	}

	n, e = ws.handle.Write(p)
	ws.numLines += 1
	ws.numBytes += n
	return n, e
}

// TestFileIO creates and removes a file to ensure that the location is writable.
func TestFileIO(prefix string) error {
	fn := fileName(prefix + "testlog-")
	// It doesn't use the fs layer because it should be used to test the
	// writability of the actual filesystem. This test is unnecessary for mock filesystems
	if _, err := createFile(fn); err != nil {
		return err
	}
	removeFile(fn)
	return nil
}

// homogenize how filenames are generated
func fileName(prefix string) string {
	return prefix + time.Now().Format(formatStr)
}

/// This is for mocking the file IO. Used exclusively for testing
///-----------------------------------------------------------------------------

// createFile is the file creating function that wraps os.Create
var createFile = func(name string) (io.WriteCloser, error) {
	return os.Create(name)
}

// removeFile is the file removing function that wraps os.Remove
var removeFile = func(name string) error {
	return os.Remove(name)
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 metrics provides a library to post status metrics.
package metrics

import "sync"

// MetricData stores metric information.
type MetricData struct {
	Name    string
	service string
	mu      sync.Mutex
	Fields  map[string]interface{}
}

// AddStringField adds a string field to a metric.
func (m *MetricData) AddStringField(name, value string) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.Fields[name] = value
}

// Bool implements a Bool-type metric.
type Bool struct {
	Value bool
	mu    sync.Mutex
	Data  *MetricData
}

// NewBool sets the metric to a new Bool value.
func NewBool(name, service string) (*Bool, error) {
	return &Bool{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new bool value.
func (b *Bool) Set(value bool) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.Value = value
	return nil
}

// Int implements a Int-type metric.
type Int struct {
	Value int64
	mu    sync.Mutex
	Data  *MetricData
}

// NewInt sets the metric to a new Int value.
func NewInt(name, service string) (*Int, error) {
	return &Int{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new int value.
func (i *Int) Set(value int64) error {
	i.mu.Lock()
	defer i.mu.Unlock()

	i.Value = value
	return nil
}

// NewCounter sets the metric to a new Int value.
func NewCounter(name, service string) (*Int, error) {
	return &Int{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Increment adds to the current int metric value.
func (i *Int) Increment() error {
	i.mu.Lock()
	defer i.mu.Unlock()

	i.Value++
	return nil
}

// String implements a String-type metric.
type String struct {
	Value string
	mu    sync.Mutex
	Data  *MetricData
}

// NewString sets the metric to a new string value.
func NewString(name, service string) (*String, error) {
	return &String{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new string value.
func (s *String) Set(value string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.Value = value
	return nil
}
<commit_msg>Add AddBoolField and ensure MetricData.Fields is initialized.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 metrics provides a library to post status metrics.
package metrics

import "sync"

// MetricData stores metric information.
type MetricData struct {
	Name    string
	service string
	mu      sync.Mutex
	Fields  map[string]interface{}
}

// AddBoolField adds a bool field to a metric.
func (m *MetricData) AddBoolField(name string, value bool) {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.Fields == nil {
		m.Fields = make(map[string]interface{})
	}
	m.Fields[name] = value
}

// AddStringField adds a string field to a metric.
func (m *MetricData) AddStringField(name, value string) {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.Fields == nil {
		m.Fields = make(map[string]interface{})
	}
	m.Fields[name] = value
}

// Bool implements a Bool-type metric.
type Bool struct {
	Value bool
	mu    sync.Mutex
	Data  *MetricData
}

// NewBool sets the metric to a new Bool value.
func NewBool(name, service string) (*Bool, error) {
	return &Bool{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new bool value.
func (b *Bool) Set(value bool) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.Value = value
	return nil
}

// Int implements a Int-type metric.
type Int struct {
	Value int64
	mu    sync.Mutex
	Data  *MetricData
}

// NewInt sets the metric to a new Int value.
func NewInt(name, service string) (*Int, error) {
	return &Int{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new int value.
func (i *Int) Set(value int64) error {
	i.mu.Lock()
	defer i.mu.Unlock()

	i.Value = value
	return nil
}

// NewCounter sets the metric to a new Int value.
func NewCounter(name, service string) (*Int, error) {
	return &Int{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Increment adds to the current int metric value.
func (i *Int) Increment() error {
	i.mu.Lock()
	defer i.mu.Unlock()

	i.Value++
	return nil
}

// String implements a String-type metric.
type String struct {
	Value string
	mu    sync.Mutex
	Data  *MetricData
}

// NewString sets the metric to a new string value.
func NewString(name, service string) (*String, error) {
	return &String{
		Data: &MetricData{
			Name:    name,
			service: service,
		},
	}, nil
}

// Set sets the metric to a new string value.
func (s *String) Set(value string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.Value = value
	return nil
}
<|endoftext|>


<commit_before>package rpc

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/xeth"
	"github.com/rs/cors"
)

var rpclistener *stoppableTCPListener

const (
	jsonrpcver       = "2.0"
	maxSizeReqLength = 1024 * 1024 // 1MB
)

func Start(pipe *xeth.XEth, config RpcConfig) error {
	if rpclistener != nil {
		if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() {
			return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String())
		}
		return nil // RPC service already running on given host/port
	}

	l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort))
	if err != nil {
		glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err)
		return err
	}
	rpclistener = l

	var handler http.Handler
	if len(config.CorsDomain) > 0 {
		var opts cors.Options
		opts.AllowedMethods = []string{"POST"}
		opts.AllowedOrigins = []string{config.CorsDomain}

		c := cors.New(opts)
		handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop)
	} else {
		handler = newStoppableHandler(JSONRPC(pipe), l.stop)
	}

	go http.Serve(l, handler)

	return nil
}

func Stop() error {
	if rpclistener != nil {
		rpclistener.Stop()
		rpclistener = nil
	}

	return nil
}

// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
func JSONRPC(pipe *xeth.XEth) http.Handler {
	api := NewEthereumApi(pipe)

	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		// Limit request size to resist DoS
		if req.ContentLength > maxSizeReqLength {
			jsonerr := &RpcErrorObject{-32700, "Request too large"}
			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
			return
		}

		// Read request body
		defer req.Body.Close()
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			jsonerr := &RpcErrorObject{-32700, "Could not read request body"}
			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
		}

		// Try to parse the request as a single
		var reqSingle RpcRequest
		if err := json.Unmarshal(body, &reqSingle); err == nil {
			response := RpcResponse(api, &reqSingle)
			send(w, &response)
			return
		}

		// Try to parse the request to batch
		var reqBatch []RpcRequest
		if err := json.Unmarshal(body, &reqBatch); err == nil {
			// Build response batch
			resBatch := make([]*interface{}, len(reqBatch))
			for i, request := range reqBatch {
				response := RpcResponse(api, &request)
				resBatch[i] = response
			}
			send(w, resBatch)
			return
		}

		// Not a batch or single request, error
		jsonerr := &RpcErrorObject{-32600, "Could not decode request"}
		send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
	})
}

func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
	var reply, response interface{}
	reserr := api.GetRequestReply(request, &reply)
	switch reserr.(type) {
	case nil:
		response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
	case *NotImplementedError, *NotAvailableError:
		jsonerr := &RpcErrorObject{-32601, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
		jsonerr := &RpcErrorObject{-32602, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	default:
		jsonerr := &RpcErrorObject{-32603, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	}

	glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
	return &response
}

func send(writer io.Writer, v interface{}) (n int, err error) {
	var payload []byte
	payload, err = json.MarshalIndent(v, "", "\t")
	if err != nil {
		glog.V(logger.Error).Infoln("Error marshalling JSON", err)
		return 0, err
	}
	glog.V(logger.Detail).Infof("Sending payload: %s", payload)

	return writer.Write(payload)
}
<commit_msg>Omit replies for notification requests<commit_after>package rpc

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/xeth"
	"github.com/rs/cors"
)

var rpclistener *stoppableTCPListener

const (
	jsonrpcver       = "2.0"
	maxSizeReqLength = 1024 * 1024 // 1MB
)

func Start(pipe *xeth.XEth, config RpcConfig) error {
	if rpclistener != nil {
		if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() {
			return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String())
		}
		return nil // RPC service already running on given host/port
	}

	l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort))
	if err != nil {
		glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err)
		return err
	}
	rpclistener = l

	var handler http.Handler
	if len(config.CorsDomain) > 0 {
		var opts cors.Options
		opts.AllowedMethods = []string{"POST"}
		opts.AllowedOrigins = []string{config.CorsDomain}

		c := cors.New(opts)
		handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop)
	} else {
		handler = newStoppableHandler(JSONRPC(pipe), l.stop)
	}

	go http.Serve(l, handler)

	return nil
}

func Stop() error {
	if rpclistener != nil {
		rpclistener.Stop()
		rpclistener = nil
	}

	return nil
}

// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
func JSONRPC(pipe *xeth.XEth) http.Handler {
	api := NewEthereumApi(pipe)

	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		// Limit request size to resist DoS
		if req.ContentLength > maxSizeReqLength {
			jsonerr := &RpcErrorObject{-32700, "Request too large"}
			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
			return
		}

		// Read request body
		defer req.Body.Close()
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			jsonerr := &RpcErrorObject{-32700, "Could not read request body"}
			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
		}

		// Try to parse the request as a single
		var reqSingle RpcRequest
		if err := json.Unmarshal(body, &reqSingle); err == nil {
			response := RpcResponse(api, &reqSingle)
			if reqSingle.Id != nil {
				send(w, &response)
			}
			return
		}

		// Try to parse the request to batch
		var reqBatch []RpcRequest
		if err := json.Unmarshal(body, &reqBatch); err == nil {
			// Build response batch
			resBatch := make([]*interface{}, len(reqBatch))
			resCount := 0

			for i, request := range reqBatch {
				response := RpcResponse(api, &request)
				// this leaves nil entries in the response batch for later removal
				if request.Id != nil {
					resBatch[i] = response
					resCount = resCount + 1
				}
			}

			// make response omitting nil entries
			respBatchComp := make([]*interface{}, resCount)
			resCount = resCount - 1
			for _, v := range resBatch {
				if v != nil {
					respBatchComp[resCount] = v
					resCount = resCount - 1
				}
			}

			send(w, respBatchComp)
			return
		}

		// Not a batch or single request, error
		jsonerr := &RpcErrorObject{-32600, "Could not decode request"}
		send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
	})
}

func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
	var reply, response interface{}
	reserr := api.GetRequestReply(request, &reply)
	switch reserr.(type) {
	case nil:
		response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
	case *NotImplementedError, *NotAvailableError:
		jsonerr := &RpcErrorObject{-32601, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
		jsonerr := &RpcErrorObject{-32602, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	default:
		jsonerr := &RpcErrorObject{-32603, reserr.Error()}
		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
	}

	glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
	return &response
}

func send(writer io.Writer, v interface{}) (n int, err error) {
	var payload []byte
	payload, err = json.MarshalIndent(v, "", "\t")
	if err != nil {
		glog.V(logger.Error).Infoln("Error marshalling JSON", err)
		return 0, err
	}
	glog.V(logger.Detail).Infof("Sending payload: %s", payload)

	return writer.Write(payload)
}
<|endoftext|>