<commit_before>package openstack

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
)

func resourceLBMonitorV1() *schema.Resource {
	return &schema.Resource{
		Create: resourceLBMonitorV1Create,
		Read:   resourceLBMonitorV1Read,
		Update: resourceLBMonitorV1Update,
		Delete: resourceLBMonitorV1Delete,

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
			},
			"tenant_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"delay": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"timeout": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"max_retries": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"url_path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"http_method": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"expected_codes": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"admin_state_up": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
		},
	}
}

func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	createOpts := monitors.CreateOpts{
		TenantID:      d.Get("tenant_id").(string),
		Type:          d.Get("type").(string),
		Delay:         d.Get("delay").(int),
		Timeout:       d.Get("timeout").(int),
		MaxRetries:    d.Get("max_retries").(int),
		URLPath:       d.Get("url_path").(string),
		ExpectedCodes: d.Get("expected_codes").(string),
		HTTPMethod:    d.Get("http_method").(string),
	}

	asuRaw := d.Get("admin_state_up").(string)
	if asuRaw != "" {
		asu, err := strconv.ParseBool(asuRaw)
		if err != nil {
			return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
		}
		createOpts.AdminStateUp = &asu
	}

	log.Printf("[DEBUG] Create Options: %#v", createOpts)
	m, err := monitors.Create(networkingClient, createOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err)
	}
	log.Printf("[INFO] LB Monitor ID: %s", m.ID)

	d.SetId(m.ID)

	return resourceLBMonitorV1Read(d, meta)
}

func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	m, err := monitors.Get(networkingClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "LB monitor")
	}

	log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m)

	d.Set("type", m.Type)
	d.Set("delay", m.Delay)
	d.Set("timeout", m.Timeout)
	d.Set("max_retries", m.MaxRetries)
	d.Set("tenant_id", m.TenantID)
	d.Set("url_path", m.URLPath)
	d.Set("http_method", m.HTTPMethod)
	d.Set("expected_codes", m.ExpectedCodes)
	d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp))

	return nil
}

func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	updateOpts := monitors.UpdateOpts{
		Delay:         d.Get("delay").(int),
		Timeout:       d.Get("timeout").(int),
		MaxRetries:    d.Get("max_retries").(int),
		URLPath:       d.Get("url_path").(string),
		HTTPMethod:    d.Get("http_method").(string),
		ExpectedCodes: d.Get("expected_codes").(string),
	}

	if d.HasChange("admin_state_up") {
		asuRaw := d.Get("admin_state_up").(string)
		if asuRaw != "" {
			asu, err := strconv.ParseBool(asuRaw)
			if err != nil {
				return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
			}
			updateOpts.AdminStateUp = &asu
		}
	}

	log.Printf("[DEBUG] Updating OpenStack LB Monitor %s with options: %+v", d.Id(), updateOpts)

	_, err = monitors.Update(networkingClient, d.Id(), updateOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err)
	}

	return resourceLBMonitorV1Read(d, meta)
}

func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	err = monitors.Delete(networkingClient, d.Id()).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error deleting OpenStack LB Monitor: %s", err)
	}

	d.SetId("")
	return nil
}
<commit_msg>Fixing TestAccLBV1Monitor_basic<commit_after>package openstack

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
)

func resourceLBMonitorV1() *schema.Resource {
	return &schema.Resource{
		Create: resourceLBMonitorV1Create,
		Read:   resourceLBMonitorV1Read,
		Update: resourceLBMonitorV1Update,
		Delete: resourceLBMonitorV1Delete,

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
			},
			"tenant_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"delay": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"timeout": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"max_retries": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},
			"url_path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"http_method": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"expected_codes": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
			"admin_state_up": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},
		},
	}
}

func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	createOpts := monitors.CreateOpts{
		TenantID:      d.Get("tenant_id").(string),
		Type:          d.Get("type").(string),
		Delay:         d.Get("delay").(int),
		Timeout:       d.Get("timeout").(int),
		MaxRetries:    d.Get("max_retries").(int),
		URLPath:       d.Get("url_path").(string),
		ExpectedCodes: d.Get("expected_codes").(string),
		HTTPMethod:    d.Get("http_method").(string),
	}

	asuRaw := d.Get("admin_state_up").(string)
	if asuRaw != "" {
		asu, err := strconv.ParseBool(asuRaw)
		if err != nil {
			return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
		}
		createOpts.AdminStateUp = &asu
	}

	log.Printf("[DEBUG] Create Options: %#v", createOpts)
	m, err := monitors.Create(networkingClient, createOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err)
	}
	log.Printf("[INFO] LB Monitor ID: %s", m.ID)

	d.SetId(m.ID)

	return resourceLBMonitorV1Read(d, meta)
}

func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	m, err := monitors.Get(networkingClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "LB monitor")
	}

	log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m)

	d.Set("type", m.Type)
	d.Set("delay", m.Delay)
	d.Set("timeout", m.Timeout)
	d.Set("max_retries", m.MaxRetries)
	d.Set("tenant_id", m.TenantID)
	d.Set("url_path", m.URLPath)
	d.Set("http_method", m.HTTPMethod)
	d.Set("expected_codes", m.ExpectedCodes)
	d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp))

	return nil
}

func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	updateOpts := monitors.UpdateOpts{
		Delay:         d.Get("delay").(int),
		Timeout:       d.Get("timeout").(int),
		MaxRetries:    d.Get("max_retries").(int),
		URLPath:       d.Get("url_path").(string),
		HTTPMethod:    d.Get("http_method").(string),
		ExpectedCodes: d.Get("expected_codes").(string),
	}

	if d.HasChange("admin_state_up") {
		asuRaw := d.Get("admin_state_up").(string)
		if asuRaw != "" {
			asu, err := strconv.ParseBool(asuRaw)
			if err != nil {
				return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
			}
			updateOpts.AdminStateUp = &asu
		}
	}

	log.Printf("[DEBUG] Updating OpenStack LB Monitor %s with options: %+v", d.Id(), updateOpts)

	_, err = monitors.Update(networkingClient, d.Id(), updateOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err)
	}

	return resourceLBMonitorV1Read(d, meta)
}

func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	err = monitors.Delete(networkingClient, d.Id()).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error deleting OpenStack LB Monitor: %s", err)
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	realos "os"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/cloudfoundry/cli/testhelpers/io"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type FakeOS struct {
	exitCalled                 int
	exitCalledWithCode         int
	mkdirCalled                int
	mkdirCalledWithPath        string
	mkdirCalledWithMode        realos.FileMode
	removeCalled               int
	removeCalledWithPath       string
	symlinkCalled              int
	symlinkCalledWithTarget    string
	symlinkCalledWithSource    string
	readdirCalled              int
	readdirCalledWithPath      string
	readfileCalled             int
	readfileCalledWithPath     string
	writefileCalled            int
	writefileCalledWithPath    string
	writefileCalledWithContent []byte
	writefileCalledWithMode    realos.FileMode
	readdirShouldReturn        []realos.FileInfo
	readfileShouldReturn       []byte
}

func (os *FakeOS) Exit(code int) {
	os.exitCalled++
	os.exitCalledWithCode = code
}

func (os *FakeOS) Mkdir(path string, mode realos.FileMode) {
	os.mkdirCalled++
	os.mkdirCalledWithPath = path
	os.mkdirCalledWithMode = mode
}

func (os *FakeOS) Remove(path string) {
	os.removeCalled++
	os.removeCalledWithPath = path
}

func (os *FakeOS) Symlink(target string, source string) error {
	os.symlinkCalled++
	os.symlinkCalledWithTarget = target
	os.symlinkCalledWithSource = source
	return nil
}

func (os *FakeOS) ReadDir(path string) ([]realos.FileInfo, error) {
	os.readdirCalled++
	os.readdirCalledWithPath = path
	return os.readdirShouldReturn, nil
}

func (os *FakeOS) ReadFile(path string) ([]byte, error) {
	os.readfileCalled++
	os.readfileCalledWithPath = path
	return os.readfileShouldReturn, nil
}

func (os *FakeOS) WriteFile(path string, content []byte, mode realos.FileMode) error {
	os.writefileCalled++
	os.writefileCalledWithPath = path
	os.writefileCalledWithContent = content
	os.writefileCalledWithMode = mode
	return nil
}

var _ = Describe("TargetsPlugin", func() {

	var fakeCliConnection *fakes.FakeCliConnection
	var targetsPlugin *TargetsPlugin
	var fakeOS FakeOS

	BeforeEach(func() {
		fakeOS = FakeOS{}
		os = &fakeOS
		fakeCliConnection = &fakes.FakeCliConnection{}
		targetsPlugin = newTargetsPlugin()
	})

	Describe("Command Syntax", func() {
		It("displays usage when targets called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "targets"}))
		})

		It("displays usage when set-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when save-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when save-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when delete-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays usage when delete-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays proper first time message", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets"})
			})
			Expect(fakeOS.exitCalled).To(Equal(0))
			Expect(output).To(ContainSubstrings([]string{"No targets have been saved"}))
			Expect(output).To(ContainSubstrings([]string{"cf", "save-target", "NAME"}))
		})
	})

	Describe("Configuration File Manipulation", func() {

		It("creates the proper target directory", func() {
			Expect(fakeOS.mkdirCalled).To(Equal(1))
			Expect(fakeOS.mkdirCalledWithPath).To(HaveSuffix("/.cf/targets"))
		})

		It("properly saves first target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			Expect(fakeOS.writefileCalled).To(Equal(1))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/first.config.json"))
			Expect(fakeOS.symlinkCalled).To(Equal(1))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/first.config.json"))
		})

		It("properly saves second target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "second"})
			Expect(fakeOS.writefileCalled).To(Equal(2))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/second.config.json"))
			Expect(fakeOS.removeCalledWithPath).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalled).To(Equal(2))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/second.config.json"))
		})
	})
})
<commit_msg>Make tests a bit more robust<commit_after>package main

import (
	realos "os"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/cloudfoundry/cli/testhelpers/io"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type FakeOS struct {
	exitCalled                 int
	exitCalledWithCode         int
	mkdirCalled                int
	mkdirCalledWithPath        string
	mkdirCalledWithMode        realos.FileMode
	removeCalled               int
	removeCalledWithPath       string
	symlinkCalled              int
	symlinkCalledWithTarget    string
	symlinkCalledWithSource    string
	readdirCalled              int
	readdirCalledWithPath      string
	readfileCalled             int
	readfileCalledWithPath     string
	writefileCalled            int
	writefileCalledWithPath    string
	writefileCalledWithContent []byte
	writefileCalledWithMode    realos.FileMode
	readdirShouldReturn        []realos.FileInfo
	readfileShouldReturn       []byte
}

func (os *FakeOS) Exit(code int) {
	os.exitCalled++
	os.exitCalledWithCode = code
}

func (os *FakeOS) Mkdir(path string, mode realos.FileMode) {
	os.mkdirCalled++
	os.mkdirCalledWithPath = path
	os.mkdirCalledWithMode = mode
}

func (os *FakeOS) Remove(path string) {
	os.removeCalled++
	os.removeCalledWithPath = path
}

func (os *FakeOS) Symlink(target string, source string) error {
	os.symlinkCalled++
	os.symlinkCalledWithTarget = target
	os.symlinkCalledWithSource = source
	return nil
}

func (os *FakeOS) ReadDir(path string) ([]realos.FileInfo, error) {
	os.readdirCalled++
	os.readdirCalledWithPath = path
	return os.readdirShouldReturn, nil
}

func (os *FakeOS) ReadFile(path string) ([]byte, error) {
	os.readfileCalled++
	os.readfileCalledWithPath = path
	return os.readfileShouldReturn, nil
}

func (os *FakeOS) WriteFile(path string, content []byte, mode realos.FileMode) error {
	os.writefileCalled++
	os.writefileCalledWithPath = path
	os.writefileCalledWithContent = content
	os.writefileCalledWithMode = mode
	return nil
}

var _ = Describe("TargetsPlugin", func() {

	var fakeCliConnection *fakes.FakeCliConnection
	var targetsPlugin *TargetsPlugin
	var fakeOS FakeOS

	BeforeEach(func() {
		fakeOS = FakeOS{}
		os = &fakeOS
		fakeCliConnection = &fakes.FakeCliConnection{}
		targetsPlugin = newTargetsPlugin()
	})

	Describe("Command Syntax", func() {
		It("displays usage when targets called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "targets"}))
		})

		It("displays usage when set-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when save-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when save-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when delete-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays usage when delete-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays proper first time message", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets"})
			})
			Expect(fakeOS.exitCalled).To(Equal(0))
			Expect(output).To(ContainSubstrings([]string{"No targets have been saved"}))
			Expect(output).To(ContainSubstrings([]string{"cf", "save-target", "NAME"}))
		})
	})

	Describe("Configuration File Manipulation", func() {

		It("creates the proper target directory", func() {
			Expect(fakeOS.mkdirCalled).To(Equal(1))
			Expect(fakeOS.mkdirCalledWithPath).To(HaveSuffix("/.cf/targets"))
		})

		It("properly saves first target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			Expect(fakeOS.exitCalled).To(Equal(0))
			Expect(fakeOS.writefileCalled).To(Equal(1))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/first.config.json"))
			Expect(fakeOS.symlinkCalled).To(Equal(1))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/first.config.json"))
		})

		It("properly saves second target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "second"})
			Expect(fakeOS.exitCalled).To(Equal(0))
			Expect(fakeOS.writefileCalled).To(Equal(2))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/second.config.json"))
			Expect(fakeOS.removeCalledWithPath).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalled).To(Equal(2))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/second.config.json"))
		})
	})
})
<|endoftext|>


<commit_before>// pounce.go
package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/guelfey/go.dbus"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

var (
	transactions map[string]Transaction = make(map[string]Transaction)
)

type Transaction struct {
	Url         string
	Destination string
}

func notify(msg string) {
	conn, err := dbus.SessionBus()
	if err != nil {
		fmt.Printf("Unable to connect to dbus session bus: %v\n", err)
	}

	obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
	call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
		"", "pounce", msg, []string{}, map[string]dbus.Variant{}, int32(5000))
	if call.Err != nil {
		fmt.Printf("Error while trying to send notification: %v\n", call.Err)
	}
}

func nameGenerator(url, destination string) string {
	var name string

	urlSplit := strings.Split(url, "/")
	if urlSplit[len(urlSplit)-1] != "" {
		name = urlSplit[len(urlSplit)-1]
	} else {
		name = urlSplit[len(urlSplit)-2]
	}
	return fmt.Sprintf("%v/%v", destination, name)
}

func download(url, output string, multi bool, response chan<- *http.Response) {
	fmt.Printf("Retreiving from %v\n", url)
	if multi {
		output = nameGenerator(url, output)
	}
	resp, err := http.Get(url)

	if err != nil {
		fmt.Printf("Unable to get resource from '%v'. Error: %v\n", url, err)
	}
	transaction := Transaction{Url: resp.Request.URL.String(), Destination: output}
	transactions[resp.Request.URL.String()] = transaction
	response <- resp
}

func readFile(filename string) []string {
	var urls []string

	file, err := os.Open(filename)
	if err != nil {
		fmt.Printf("Problem opening input file!")
		panic(err)
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		urls = append(urls, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Problem reading input file!")
		panic(err)
	}

	return urls
}

func create(filename string) *os.File {
	file, err := os.Create(filename)
	if err != nil {
		fmt.Printf("Unable to create file!")
		panic(err)
	}

	return file
}

func save(file io.Writer, resp *http.Response) int64 {
	complete, err := io.Copy(file, resp.Body)
	if err != nil {
		fmt.Printf("Unable to write file, error: %v\n", err)
	}
	return complete
}

func main() {
	app := cli.NewApp()
	app.Name = "pounce"
	app.Version = "0.0.2"
	app.Usage = "A very simple file downloader in the vein of wget."
	app.Authors = []cli.Author{cli.Author{
		Name:  "Brian Tomlinson",
		Email: "darthlukan@gmail.com",
	}}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "url, u",
			Value: "",
			Usage: "The input URL",
		},
		cli.StringFlag{
			Name:  "file, f",
			Value: "",
			Usage: "The input file",
		},
		cli.StringFlag{
			Name:  "outfile, o",
			Value: "",
			Usage: "The output file",
		},
		cli.StringFlag{
			Name:  "dir, d",
			Value: "",
			Usage: "The output directory",
		},
	}
	app.Action = func(c *cli.Context) {
		counter := 0
		url := c.String("url")
		inFile := c.String("file")
		outFile := c.String("outfile")
		outDir := c.String("dir")

		if url == "" && inFile == "" {
			fmt.Println(errors.New("Missing input arguments, please see 'pounce --help'\n"))
			return
		} else if outFile == "" && outDir == "" {
			fmt.Println(errors.New("Missing output arguments, please see 'pounce --help'\n"))
			return
		}

		startTime := time.Now()

		respChan := make(chan *http.Response)

		if inFile != "" && outDir != "" {
			urls := readFile(inFile)
			for _, url := range urls {
				go download(url, outDir, true, respChan)
				counter += 1
			}
		}

		if url != "" && outFile != "" {
			go download(url, outFile, false, respChan)
			counter += 1
		}

		for counter > 0 {
			select {
			case r := <-respChan:
				u := fmt.Sprintf("%v", r.Request.URL.String())
				if transaction, ok := transactions[u]; ok == true {
					defer r.Body.Close()
					f := create(transaction.Destination)
					defer f.Close()
					bytesWritten := save(f, r)
					endTime := time.Now()
					msg := fmt.Sprintf("Downloaded %vkb file '%v' in %v\n",
						bytesWritten/1024, f.Name(), endTime.Sub(startTime))
					go notify(msg)
					fmt.Printf(msg)
				} else {
					fmt.Printf("%v: %v.\n", "Problem processing transaction", r.Body)
				}
				counter--
			}
		}

	}
	app.Run(os.Args)
}
<commit_msg>Handle transaction creation in a goroutine.  This code works with large lists of urls<commit_after>// pounce.go
package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/guelfey/go.dbus"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

var (
	transactions map[string]Transaction = make(map[string]Transaction)
)

type Transaction struct {
	Url         string
	Destination string
}

func notify(msg string) {
	conn, err := dbus.SessionBus()
	if err != nil {
		fmt.Printf("Unable to connect to dbus session bus: %v\n", err)
	}

	obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
	call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
		"", "pounce", msg, []string{}, map[string]dbus.Variant{}, int32(5000))
	if call.Err != nil {
		fmt.Printf("Error while trying to send notification: %v\n", call.Err)
	}
}

func nameGenerator(url, destination string) string {
	var name string

	urlSplit := strings.Split(url, "/")
	if urlSplit[len(urlSplit)-1] != "" {
		name = urlSplit[len(urlSplit)-1]
	} else {
		name = urlSplit[len(urlSplit)-2]
	}

	destination = strings.Trim(destination, "/")
	return fmt.Sprintf("%v/%v", destination, name)
}

func download(url, output string, multi bool, response chan<- *http.Response) {
	fmt.Printf("Retreiving from %v\n", url)
	if multi {
		output = nameGenerator(url, output)
	}
	resp, err := http.Get(url)

	if err != nil {
		fmt.Printf("Unable to get resource from '%v'. Error: %v\n", url, err)
	}

	go func() {
		transaction := Transaction{Url: resp.Request.URL.String(), Destination: output}
		transactions[resp.Request.URL.String()] = transaction
	}()
	response <- resp
}

func readFile(filename string) []string {
	var urls []string

	file, err := os.Open(filename)
	if err != nil {
		fmt.Printf("Problem opening input file!")
		panic(err)
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		urls = append(urls, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Problem reading input file!")
		panic(err)
	}

	return urls
}

func create(filename string) *os.File {
	file, err := os.Create(filename)
	if err != nil {
		fmt.Printf("Unable to create file!")
		panic(err)
	}

	return file
}

func save(file io.Writer, resp *http.Response) int64 {
	complete, err := io.Copy(file, resp.Body)
	if err != nil {
		fmt.Printf("Unable to write file, error: %v\n", err)
	}
	return complete
}

func main() {
	app := cli.NewApp()
	app.Name = "pounce"
	app.Version = "0.0.2"
	app.Usage = "A very simple file downloader in the vein of wget."
	app.Authors = []cli.Author{cli.Author{
		Name:  "Brian Tomlinson",
		Email: "darthlukan@gmail.com",
	}}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "url, u",
			Value: "",
			Usage: "The input URL",
		},
		cli.StringFlag{
			Name:  "file, f",
			Value: "",
			Usage: "The input file",
		},
		cli.StringFlag{
			Name:  "outfile, o",
			Value: "",
			Usage: "The output file",
		},
		cli.StringFlag{
			Name:  "dir, d",
			Value: "",
			Usage: "The output directory",
		},
	}
	app.Action = func(c *cli.Context) {
		counter := 0
		url := c.String("url")
		inFile := c.String("file")
		outFile := c.String("outfile")
		outDir := c.String("dir")

		if url == "" && inFile == "" {
			fmt.Println(errors.New("Missing input arguments, please see 'pounce --help'\n"))
			return
		} else if outFile == "" && outDir == "" {
			fmt.Println(errors.New("Missing output arguments, please see 'pounce --help'\n"))
			return
		}

		startTime := time.Now()

		respChan := make(chan *http.Response)

		if inFile != "" && outDir != "" {
			urls := readFile(inFile)
			for _, url := range urls {
				go download(url, outDir, true, respChan)
				counter += 1
			}
		}

		if url != "" && outFile != "" {
			go download(url, outFile, false, respChan)
			counter += 1
		}

		for counter > 0 {
			select {
			case r := <-respChan:
				u := fmt.Sprintf("%v", r.Request.URL.String())
				if transaction, ok := transactions[u]; ok == true {
					defer r.Body.Close()
					f := create(transaction.Destination)
					defer f.Close()
					bytesWritten := save(f, r)
					endTime := time.Now()
					msg := fmt.Sprintf("Downloaded %vkb file '%v' in %v\n",
						bytesWritten/1024, f.Name(), endTime.Sub(startTime))
					go notify(msg)
					fmt.Printf(msg)
				} else {
					fmt.Printf("%v: %v.\n", "Problem processing transaction", r.Body)
				}
				counter--
			}
		}

	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"net/http"
)

// 封装ServeMux，使所有添加的路由项的匹配模式都带上指定的前缀。
//  p := srv.Prefix("/api")
//  p.Get("/users")  // 相当于 srv.Get("/api/users")
//  p.Get("/user/1") // 相当于 srv.Get("/api/user/1")
type Prefix struct {
	mux    *ServeMux
	group  *Group
	prefix string
}

// Add相当于ServeMux.Add(prefix+pattern, h, "POST"...)的简易写法
func (p *Prefix) Add(pattern string, h http.Handler, methods ...string) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, methods...)
	return p
}

// Get相当于ServeMux.Get(prefix+pattern, h)的简易写法
func (p *Prefix) Get(pattern string, h http.Handler) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, "GET")
	return p
}

// Post相当于ServeMux.Post(prefix+pattern, h)的简易写法
func (p *Prefix) Post(pattern string, h http.Handler) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, "POST")
	return p
}

// Delete相当于ServeMux.Delete(prefix+pattern, h)的简易写法
func (p *Prefix) Delete(pattern string, h http.Handler) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, "DELETE")
	return p
}

// Put相当于ServeMux.Put(prefix+pattern, h)的简易写法
func (p *Prefix) Put(pattern string, h http.Handler) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, "PUT")
	return p
}

// Any相当于ServeMux.Any(prefix+pattern, h)的简易写法
func (p *Prefix) Any(pattern string, h http.Handler) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h)
	return p
}

// AddFunc功能同ServeMux.AddFunc(prefix+pattern, fun, ...)
func (p *Prefix) AddFunc(pattern string, fun func(http.ResponseWriter, *http.Request), methods ...string) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, methods...)
	return p
}

// GetFunc相当于ServeMux.GetFunc(prefix+pattern, func)的简易写法
func (p *Prefix) GetFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, "GET")
	return p
}

// PutFunc相当于ServeMux.PutFunc(prefix+pattern, func)的简易写法
func (p *Prefix) PutFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, "PUT")
	return p
}

// PostFunc相当于ServeMux.PostFunc(prefix+pattern, func)的简易写法
func (p *Prefix) PostFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, "POST")
	return p
}

// DeleteFunc相当于ServeMux.DeleteFunc(prefix+pattern, func)的简易写法
func (p *Prefix) DeleteFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, "DELETE")
	return p
}

// AnyFunc相当于ServeMux.AnyFunc(prefix+pattern, func)的简易写法
func (p *Prefix) AnyFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun)
	return p
}

// AnyFunc相当于ServeMux.Remove(prefix+pattern, methods...)的简易写法
func (p *Prefix) Remove(pattern string, methods ...string) {
	p.mux.Remove(p.prefix+pattern, methods...)
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := g.Prefix("/api")
//  p.Get("/users")  // 相当于 g.Get("/api/users")
//  p.Get("/user/1") // 相当于 g.Get("/api/user/1")
func (p *Prefix) Prefix(prefix string) *Prefix {
	return &Prefix{
		group:  p.group,
		mux:    p.mux,
		prefix: p.prefix + prefix,
	}
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := g.Prefix("/api")
//  p.Get("/users")  // 相当于 g.Get("/api/users")
//  p.Get("/user/1") // 相当于 g.Get("/api/user/1")
func (g *Group) Prefix(prefix string) *Prefix {
	return &Prefix{
		group:  g,
		prefix: prefix,
		mux:    g.mux,
	}
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := srv.Prefix("/api")
//  p.Get("/users")  // 相当于 srv.Get("/api/users")
//  p.Get("/user/1") // 相当于 srv.Get("/api/user/1")
func (mux *ServeMux) Prefix(prefix string) *Prefix {
	return &Prefix{
		mux:    mux,
		prefix: prefix,
	}
}
<commit_msg>精简Prefix的方法调用代码<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"net/http"
)

// 封装ServeMux，使所有添加的路由项的匹配模式都带上指定的前缀。
//  p := srv.Prefix("/api")
//  p.Get("/users")  // 相当于 srv.Get("/api/users")
//  p.Get("/user/1") // 相当于 srv.Get("/api/user/1")
type Prefix struct {
	mux    *ServeMux
	group  *Group
	prefix string
}

// Add相当于ServeMux.Add(prefix+pattern, h, "POST"...)的简易写法
func (p *Prefix) Add(pattern string, h http.Handler, methods ...string) *Prefix {
	p.mux.add(p.group, p.prefix+pattern, h, methods...)
	return p
}

// Get相当于ServeMux.Get(prefix+pattern, h)的简易写法
func (p *Prefix) Get(pattern string, h http.Handler) *Prefix {
	return p.Add(pattern, h, "GET")
}

// Post相当于ServeMux.Post(prefix+pattern, h)的简易写法
func (p *Prefix) Post(pattern string, h http.Handler) *Prefix {
	return p.Add(pattern, h, "POST")
}

// Delete相当于ServeMux.Delete(prefix+pattern, h)的简易写法
func (p *Prefix) Delete(pattern string, h http.Handler) *Prefix {
	return p.Add(pattern, h, "DELETE")
}

// Put相当于ServeMux.Put(prefix+pattern, h)的简易写法
func (p *Prefix) Put(pattern string, h http.Handler) *Prefix {
	return p.Add(pattern, h, "PUT")
}

// Any相当于ServeMux.Any(prefix+pattern, h)的简易写法
func (p *Prefix) Any(pattern string, h http.Handler) *Prefix {
	return p.Add(pattern, h)
}

// AddFunc功能同ServeMux.AddFunc(prefix+pattern, fun, ...)
func (p *Prefix) AddFunc(pattern string, fun func(http.ResponseWriter, *http.Request), methods ...string) *Prefix {
	p.mux.addFunc(p.group, p.prefix+pattern, fun, methods...)
	return p
}

// GetFunc相当于ServeMux.GetFunc(prefix+pattern, func)的简易写法
func (p *Prefix) GetFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	return p.AddFunc(pattern, fun, "GET")
}

// PutFunc相当于ServeMux.PutFunc(prefix+pattern, func)的简易写法
func (p *Prefix) PutFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	return p.AddFunc(pattern, fun, "PUT")
}

// PostFunc相当于ServeMux.PostFunc(prefix+pattern, func)的简易写法
func (p *Prefix) PostFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	return p.AddFunc(pattern, fun, "POST")
}

// DeleteFunc相当于ServeMux.DeleteFunc(prefix+pattern, func)的简易写法
func (p *Prefix) DeleteFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	return p.AddFunc(pattern, fun, "DELETE")
}

// AnyFunc相当于ServeMux.AnyFunc(prefix+pattern, func)的简易写法
func (p *Prefix) AnyFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Prefix {
	return p.AddFunc(pattern, fun)
}

// AnyFunc相当于ServeMux.Remove(prefix+pattern, methods...)的简易写法
func (p *Prefix) Remove(pattern string, methods ...string) {
	p.mux.Remove(p.prefix+pattern, methods...)
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := g.Prefix("/api")
//  p.Get("/users")  // 相当于 g.Get("/api/users")
//  p.Get("/user/1") // 相当于 g.Get("/api/user/1")
func (p *Prefix) Prefix(prefix string) *Prefix {
	return &Prefix{
		group:  p.group,
		mux:    p.mux,
		prefix: p.prefix + prefix,
	}
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := g.Prefix("/api")
//  p.Get("/users")  // 相当于 g.Get("/api/users")
//  p.Get("/user/1") // 相当于 g.Get("/api/user/1")
func (g *Group) Prefix(prefix string) *Prefix {
	return &Prefix{
		group:  g,
		prefix: prefix,
		mux:    g.mux,
	}
}

// 创建一个路由组，该组中添加的路由项，都会带上前缀prefix
// prefix 前缀字符串，所有从Prefix中声明的路由都将包含此前缀。
//  p := srv.Prefix("/api")
//  p.Get("/users")  // 相当于 srv.Get("/api/users")
//  p.Get("/user/1") // 相当于 srv.Get("/api/user/1")
func (mux *ServeMux) Prefix(prefix string) *Prefix {
	return &Prefix{
		mux:    mux,
		prefix: prefix,
	}
}
<|endoftext|>


<commit_before>package ogdat

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

const (
	Info            = 1
	Warning         = 2
	Error           = 3
	StructuralError = 4
)

var isolangfilemap map[string]*ISO6392Lang = nil

type ISO6392Lang struct {
	Code, Identifier string
}

func CheckISOLanguage(lang string) bool {
	const iso639file = "ISO-639-2_utf-8.txt"
	if isolangfilemap == nil {
		var err error
		if isolangfilemap, err = loadisolanguagefile(iso639file); err != nil {
			panic(fmt.Sprintf("Can not load ISO language file '%s'", iso639file))
		}
	}
	_, ok := isolangfilemap[lang]
	return ok
}

func loadisolanguagefile(filename string) (isolangfilemap map[string]*ISO6392Lang, _ error) {

	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	defer reader.Close()
	isolangfilemap = make(map[string]*ISO6392Lang)
	csvreader := csv.NewReader(reader)
	csvreader.Comma = '|'

	for record, err := csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
		isorecord := &ISO6392Lang{Code: record[0], Identifier: record[3]}
		isolangfilemap[isorecord.Code] = isorecord
		if len(record[1]) > 0 {
			isorecord = &ISO6392Lang{Code: record[1], Identifier: record[3]}
			isolangfilemap[record[1]] = isorecord
		}
	}
	log.Printf("Info: Read %d ISO language records", len(isolangfilemap))

	return
}

var ianaencmap map[string]struct{} = nil

func CheckIANAEncoding(enc string) bool {
	const ianaencfile = "character-sets.txt"
	if ianaencmap == nil {
		var err error
		if ianaencmap, err = loadianaencodingfile(ianaencfile); err != nil {
			panic(fmt.Sprintf("Can not load IANA encoding definition file '%s'", ianaencfile))
		}
	}
	enc = strings.ToLower(enc)
	_, ok := ianaencmap[enc]
	if !ok {
		enc = strings.Replace(enc, "-", "", -1)
		_, ok = ianaencmap[enc]
	}
	return ok
}

func loadianaencodingfile(filename string) (ianamap map[string]struct{}, _ error) {
	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	ianamap = make(map[string]struct{})
	bufreader := bufio.NewReader(reader)
	delim := byte('\n')

	for line, err := bufreader.ReadString(delim); err != io.EOF; line, err = bufreader.ReadString(delim) {
		// ReadString includes the delimeter, get rid of it
		line = line[:len(line)-1]
		// normalize by lower casing
		line = strings.ToLower(line)

		ianamap[line] = struct{}{}
	}
	log.Printf("Info: Read %d IANA encoding names", len(ianamap))
	return
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

type CheckError struct {
	Status, Position int
	message          string
}

func (ce *CheckError) Error() string {
	return ce.message
}

func strrange(minrange, maxrange, idx int, s string) string {
	if minrange > maxrange {
		panic("minrange > maxrange")
	}

	if idx > len(s) {
		idx = len(s)
	}

	var prepend string
	start := idx + minrange
	if start < 0 {
		start = 0
	} else {
		prepend = "..."
	}

	var postpone string
	end := idx + maxrange
	if end > len(s) {
		end = len(s)
	} else {
		postpone = "..."
	}
	return prepend + s[start:end] + postpone
}

var regexphtmlcodecheck = regexp.MustCompile(`<\w+.*('|"|)>`)
var regexphtmlescape = regexp.MustCompile(`&\w{1,10};|&#\d{1,6};`)
var regexpurlencode = regexp.MustCompile(`%[0-9a-fA-F][0-9a-fA-F]`)
var regexpposixescape = regexp.MustCompile(`\\n|\\b|\\v|\\t`)

// return values are:
// ok = false indicates sthg. was wrong in which case error will not be nil
//
// error: if is of type CheckError:
// Status: 1 = Info, 2 = Warning, 3 = Error
// Position: beginning position of offending input
// message: An error message describing the problem
func CheckOGDTextStringForSaneCharacters(str string) (ok bool, _ error) {
	if !utf8.ValidString(str) {
		return false, &CheckError{Error, 0, "Zeichenfolge ist nicht durchgängig gültig als UTF8 kodiert"}
	}
	for idx, val := range str {
		if val == unicode.ReplacementChar {
			return false, &CheckError{Error, idx, fmt.Sprintf("Ungültige Unicode-Sequenz: '0x%x' (Bereich '%s')", val, strrange(-20, 20, idx, str))}
		}
	}

	if idx := regexphtmlcodecheck.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche HTML-Sequenz: '%s'", str[idx[0]:min(20, idx[1]-idx[0])])}
	}
	if idx := regexphtmlescape.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche HTML-Escapes: '%s'", str[idx[0]:min(15, idx[1]-idx[0])])}
	}
	if idx := regexpurlencode.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche Url-Escapes: '%s'", str[idx[0]:min(8, idx[1]-idx[0])])}
	}
	if idx := regexpposixescape.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche Posix-Escapes: '%s'", str[idx[0]:min(5, idx[1]-idx[0])])}
	}
	return true, nil
}

var regexpbboxWKT = regexp.MustCompile(`^POLYGON\s{0,1}\({1,2}\s{0,2}[-+]?[0-9]*\.?[0-9]+\s{1,2}[-+]?[0-9]*\.?[0-9]+,\s{0,2}[-+]?[0-9]*\.?[0-9]+\s{1,2}[-+]?[0-9]*\.?[0-9]+\s{0,2}\){1,2}$`)

func CheckOGDBBox(str string) (bool, error) {
	if !utf8.ValidString(str) {
		return false, &CheckError{Error, -1, "Zeichenfolge ist nicht durchgängig gültig als UTF8 kodiert"}
	}
	if idx := regexpbboxWKT.FindStringIndex(str); idx == nil {
		return false, &CheckError{Error, -1, fmt.Sprintf("Keine gültige WKT-Angabe einer BoundingBox: '%s'", str)}
	}
	return true, nil
}

var regexpEMail = regexp.MustCompile(`^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$`)

func CheckUrlContact(url string, followhttplink bool) (bool, error) {
	// it's a contact point if it's a http-link (starts with "http(s)" )
	if len(url) >= 4 && url[:4] == "http" {
		if followhttplink {
			resp, err := http.Head(url)
			if err != nil {
				return false, &CheckError{Error, -1, fmt.Sprintf("URL kann nicht aufgelöst werden: '%s'", err)}
			}
			if sc := resp.StatusCode; sc != 200 {
				return false, &CheckError{Error, -1, fmt.Sprintf("HEAD request liefert nicht-OK Statuscode '%d'", sc)}
			}
		}
		return true, nil
	}
	// it's a contact point if it's an email address
	if idx := regexpEMail.FindStringIndex(url); idx != nil {
		return true, nil
	}
	return false, &CheckError{Warning, -1, fmt.Sprintf("vermutlich keine gültige Web- oder E-Mail Adresse: '%s' (Auszug)", url[:min(20, len(url))])}
}

type CheckMessage struct {
	Type    int // 1 = Info, 2 = Warning, 3 = Error, 4 = StructuralError
	Text    string
	OGDID   int
	Context string
}

type Checker interface {
	Check(bool) ([]CheckMessage, error)
}

func Loadogdatspec(version, filename string) (*OGDSet, error) {
	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	csvreader := csv.NewReader(reader)
	csvreader.Comma = '|'
	csvreader.LazyQuotes = true

	// Read the first line and use it as the labels for the items to load
	record, err := csvreader.Read()
	if err != nil {
		return nil, err
	}

	set := &OGDSet{Label: record}

	spec := make([]*Beschreibung, 0)
	for record, err = csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
		id, _ := strconv.Atoi(record[0])
		var occ Occurrence
		switch record[12][0] {
		case 'R':
			occ = OccRequired
		case 'O':
			occ = OccOptional
		}
		descrecord := NewBeschreibung(id, occ, version)

		descrecord.Bezeichner = record[1]
		descrecord.OGD_Kurzname = record[2]
		descrecord.CKAN_Feld = record[3]
		descrecord.Anzahl = record[4]
		descrecord.Definition_DE = record[5]
		descrecord.Erlauterung = record[6]
		descrecord.Beispiel = record[7]
		descrecord.ONA2270 = record[8]
		descrecord.ISO19115 = record[9]
		descrecord.RDFProperty = record[10]
		descrecord.Definition_EN = record[11]

		spec = append(spec, descrecord)
	}
	set.Beschreibung = spec
	log.Printf("Info: Read %d %s specifiaction records", len(spec), version)

	return set, nil
}
<commit_msg>check emails case-insensitive<commit_after>package ogdat

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

const (
	Info            = 1
	Warning         = 2
	Error           = 3
	StructuralError = 4
)

var isolangfilemap map[string]*ISO6392Lang = nil

type ISO6392Lang struct {
	Code, Identifier string
}

func CheckISOLanguage(lang string) bool {
	const iso639file = "ISO-639-2_utf-8.txt"
	if isolangfilemap == nil {
		var err error
		if isolangfilemap, err = loadisolanguagefile(iso639file); err != nil {
			panic(fmt.Sprintf("Can not load ISO language file '%s'", iso639file))
		}
	}
	_, ok := isolangfilemap[lang]
	return ok
}

func loadisolanguagefile(filename string) (isolangfilemap map[string]*ISO6392Lang, _ error) {

	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	defer reader.Close()
	isolangfilemap = make(map[string]*ISO6392Lang)
	csvreader := csv.NewReader(reader)
	csvreader.Comma = '|'

	for record, err := csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
		isorecord := &ISO6392Lang{Code: record[0], Identifier: record[3]}
		isolangfilemap[isorecord.Code] = isorecord
		if len(record[1]) > 0 {
			isorecord = &ISO6392Lang{Code: record[1], Identifier: record[3]}
			isolangfilemap[record[1]] = isorecord
		}
	}
	log.Printf("Info: Read %d ISO language records", len(isolangfilemap))

	return
}

var ianaencmap map[string]struct{} = nil

func CheckIANAEncoding(enc string) bool {
	const ianaencfile = "character-sets.txt"
	if ianaencmap == nil {
		var err error
		if ianaencmap, err = loadianaencodingfile(ianaencfile); err != nil {
			panic(fmt.Sprintf("Can not load IANA encoding definition file '%s'", ianaencfile))
		}
	}
	enc = strings.ToLower(enc)
	_, ok := ianaencmap[enc]
	if !ok {
		enc = strings.Replace(enc, "-", "", -1)
		_, ok = ianaencmap[enc]
	}
	return ok
}

func loadianaencodingfile(filename string) (ianamap map[string]struct{}, _ error) {
	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	ianamap = make(map[string]struct{})
	bufreader := bufio.NewReader(reader)
	delim := byte('\n')

	for line, err := bufreader.ReadString(delim); err != io.EOF; line, err = bufreader.ReadString(delim) {
		// ReadString includes the delimeter, get rid of it
		line = line[:len(line)-1]
		// normalize by lower casing
		line = strings.ToLower(line)

		ianamap[line] = struct{}{}
	}
	log.Printf("Info: Read %d IANA encoding names", len(ianamap))
	return
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

type CheckError struct {
	Status, Position int
	message          string
}

func (ce *CheckError) Error() string {
	return ce.message
}

func strrange(minrange, maxrange, idx int, s string) string {
	if minrange > maxrange {
		panic("minrange > maxrange")
	}

	if idx > len(s) {
		idx = len(s)
	}

	var prepend string
	start := idx + minrange
	if start < 0 {
		start = 0
	} else {
		prepend = "..."
	}

	var postpone string
	end := idx + maxrange
	if end > len(s) {
		end = len(s)
	} else {
		postpone = "..."
	}
	return prepend + s[start:end] + postpone
}

var regexphtmlcodecheck = regexp.MustCompile(`<\w+.*('|"|)>`)
var regexphtmlescape = regexp.MustCompile(`&\w{1,10};|&#\d{1,6};`)
var regexpurlencode = regexp.MustCompile(`%[0-9a-fA-F][0-9a-fA-F]`)
var regexpposixescape = regexp.MustCompile(`\\n|\\b|\\v|\\t`)

// return values are:
// ok = false indicates sthg. was wrong in which case error will not be nil
//
// error: if is of type CheckError:
// Status: 1 = Info, 2 = Warning, 3 = Error
// Position: beginning position of offending input
// message: An error message describing the problem
func CheckOGDTextStringForSaneCharacters(str string) (ok bool, _ error) {
	if !utf8.ValidString(str) {
		return false, &CheckError{Error, 0, "Zeichenfolge ist nicht durchgängig gültig als UTF8 kodiert"}
	}
	for idx, val := range str {
		if val == unicode.ReplacementChar {
			return false, &CheckError{Error, idx, fmt.Sprintf("Ungültige Unicode-Sequenz: '0x%x' (Bereich '%s')", val, strrange(-20, 20, idx, str))}
		}
	}

	if idx := regexphtmlcodecheck.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche HTML-Sequenz: '%s'", str[idx[0]:min(20, idx[1]-idx[0])])}
	}
	if idx := regexphtmlescape.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche HTML-Escapes: '%s'", str[idx[0]:min(15, idx[1]-idx[0])])}
	}
	if idx := regexpurlencode.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche Url-Escapes: '%s'", str[idx[0]:min(8, idx[1]-idx[0])])}
	}
	if idx := regexpposixescape.FindStringIndex(str); idx != nil {
		return false, &CheckError{Warning, idx[0], fmt.Sprintf("Mögliche Posix-Escapes: '%s'", str[idx[0]:min(5, idx[1]-idx[0])])}
	}
	return true, nil
}

var regexpbboxWKT = regexp.MustCompile(`^POLYGON\s{0,1}\({1,2}\s{0,2}[-+]?[0-9]*\.?[0-9]+\s{1,2}[-+]?[0-9]*\.?[0-9]+,\s{0,2}[-+]?[0-9]*\.?[0-9]+\s{1,2}[-+]?[0-9]*\.?[0-9]+\s{0,2}\){1,2}$`)

func CheckOGDBBox(str string) (bool, error) {
	if !utf8.ValidString(str) {
		return false, &CheckError{Error, -1, "Zeichenfolge ist nicht durchgängig gültig als UTF8 kodiert"}
	}
	if idx := regexpbboxWKT.FindStringIndex(str); idx == nil {
		return false, &CheckError{Error, -1, fmt.Sprintf("Keine gültige WKT-Angabe einer BoundingBox: '%s'", str)}
	}
	return true, nil
}

// TODO: add switch for case-insenstive check
var regexpEMail = regexp.MustCompile(`^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$`)

func CheckUrlContact(url string, followhttplink bool) (bool, error) {
	// it's a contact point if it's a http-link (starts with "http(s)" )
	if len(url) >= 4 && url[:4] == "http" {
		if followhttplink {
			resp, err := http.Head(url)
			if err != nil {
				return false, &CheckError{Error, -1, fmt.Sprintf("URL kann nicht aufgelöst werden: '%s'", err)}
			}
			if sc := resp.StatusCode; sc != 200 {
				return false, &CheckError{Error, -1, fmt.Sprintf("HEAD request liefert nicht-OK Statuscode '%d'", sc)}
			}
		}
		return true, nil
	}
	// it's a contact point if it's an email address
	if idx := regexpEMail.FindStringIndex(url); idx != nil {
		return true, nil
	}
	return false, &CheckError{Warning, -1, fmt.Sprintf("vermutlich keine gültige Web- oder E-Mail Adresse: '%s' (Auszug)", url[:min(20, len(url))])}
}

type CheckMessage struct {
	Type    int // 1 = Info, 2 = Warning, 3 = Error, 4 = StructuralError
	Text    string
	OGDID   int
	Context string
}

type Checker interface {
	Check(bool) ([]CheckMessage, error)
}

func Loadogdatspec(version, filename string) (*OGDSet, error) {
	reader, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	csvreader := csv.NewReader(reader)
	csvreader.Comma = '|'
	csvreader.LazyQuotes = true

	// Read the first line and use it as the labels for the items to load
	record, err := csvreader.Read()
	if err != nil {
		return nil, err
	}

	set := &OGDSet{Label: record}

	spec := make([]*Beschreibung, 0)
	for record, err = csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
		id, _ := strconv.Atoi(record[0])
		var occ Occurrence
		switch record[12][0] {
		case 'R':
			occ = OccRequired
		case 'O':
			occ = OccOptional
		}
		descrecord := NewBeschreibung(id, occ, version)

		descrecord.Bezeichner = record[1]
		descrecord.OGD_Kurzname = record[2]
		descrecord.CKAN_Feld = record[3]
		descrecord.Anzahl = record[4]
		descrecord.Definition_DE = record[5]
		descrecord.Erlauterung = record[6]
		descrecord.Beispiel = record[7]
		descrecord.ONA2270 = record[8]
		descrecord.ISO19115 = record[9]
		descrecord.RDFProperty = record[10]
		descrecord.Definition_EN = record[11]

		spec = append(spec, descrecord)
	}
	set.Beschreibung = spec
	log.Printf("Info: Read %d %s specifiaction records", len(spec), version)

	return set, nil
}
<|endoftext|>


<commit_before>/*
** Copyright [2013-2015] [Megam Systems]
**
** 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 ubuntu

/*
import (
	"os"
	"fmt"
	"github.com/megamsys/libmegdc/templates"
	"github.com/megamsys/urknall"
	//"github.com/megamsys/libgo/cmd"
)

const (
	Bridge = "Bridge"
	Hdd     = "Osd"
	Phy    = "PhyDev"
	VgName  = "VgName"
)

var ubuntulvminstall *UbuntuLvmInstall

func init() {
	ubuntulvminstall = &UbuntuLvmInstall{}
	templates.Register("UbuntuLvmInstall", ubuntulvminstall)
}

type UbuntuLvmInstall struct {
	osds      []string
	bridge string
	phydev    string
	vgname string
}

func (tpl *UbuntuLvmInstall) Options(t *templates.Template) {
	if osds, ok := t.Maps[Hdd]; ok {
		tpl.osds = osds
	}
	if bridge, ok := t.Options[Bridge]; ok {
		tpl.bridge = bridge
	}
	if phydev, ok := t.Options[Phy]; ok {
		tpl.phydev = phydev
	}
	if vgname, ok := t.Options[VgName]; ok {
		tpl.vgname = vgname
	}
}

func (tpl *UbuntuLvmInstall) Render(p urknall.Package) {
	p.AddTemplate("lvm", &UbuntuLvmInstallTemplate{
		osds:     tpl.osds,
		bridge: tpl.bridge,
	  vgname: tpl.vgname,
		phydev:    tpl.phydev,
	})
}

func (tpl *UbuntuLvmInstall) Run(target urknall.Target,inputs []string) error {
	return urknall.Run(target, &UbuntuLvmInstall{
		osds:     tpl.osds,
		bridge: tpl.bridge,
		phydev:    tpl.phydev,
		vgname: tpl.vgname,

	},inputs)
}

type UbuntuLvmInstallTemplate struct {
  osds     []string
	bridge string
	vgname string
	phydev    string
}

func (m *UbuntuLvmInstallTemplate) Render(pkg urknall.Package) {
	host,_ := os.Hostname()
	phy := m.phydev
	ip := IP(phy)
  osddir := ArraytoString("/dev/","",m.osds)
	bridge := m.bridge
	vg := m.vgname
  fmt.Println("bridge  ",bridge )
 pkg.AddCommands("lvminstall",
	  UpdatePackagesOmitError(),
		InstallPackages("clvm lvm2 kvm libvirt-bin ruby nfs-common bridge-utils"),
	)
	pkg.AddCommands("vg-setup",
		Shell("ip addr flush dev "+phy+""),
		Shell("brctl addbr "+ bridge),
		Shell("brctl addif "+ bridge+" "+phy+""),
		Shell("pvcreate "+osddir+""),
		Shell("vgcreate "+vg+" "+osddir+""),
	)
}
*/
<commit_msg>lvm template<commit_after>/*
** Copyright [2013-2015] [Megam Systems]
**
** 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 ubuntu

import (
	"os"
	"fmt"
	"github.com/megamsys/libmegdc/templates"
	"github.com/megamsys/urknall"
	//"github.com/megamsys/libgo/cmd"
)

const (
	Hdd     = "Osd"
	VgName  = "VgName"
)

var ubuntulvminstall *UbuntuLvmInstall

func init() {
	ubuntulvminstall = &UbuntuLvmInstall{}
	templates.Register("UbuntuLvmInstall", ubuntulvminstall)
}

type UbuntuLvmInstall struct {
	osds      []string
	vgname string
}

func (tpl *UbuntuLvmInstall) Options(t *templates.Template) {
	if osds, ok := t.Maps[Hdd]; ok {
		tpl.osds = osds
	}
	if vgname, ok := t.Options[VgName]; ok {
		tpl.vgname = vgname
	}
}

func (tpl *UbuntuLvmInstall) Render(p urknall.Package) {
	p.AddTemplate("lvm", &UbuntuLvmInstallTemplate{
		osds:     tpl.osds,
		phydev:    tpl.phydev,
	})
}

func (tpl *UbuntuLvmInstall) Run(target urknall.Target,inputs []string) error {
	return urknall.Run(target, &UbuntuLvmInstall{
		osds:     tpl.osds,
		vgname: tpl.vgname,

	},inputs)
}

type UbuntuLvmInstallTemplate struct {
  osds     []string
	vgname string
}

func (m *UbuntuLvmInstallTemplate) Render(pkg urknall.Package) {
  osddir := ArraytoString("/dev/","",m.osds)
	vg := m.vgname
 pkg.AddCommands("lvminstall",
	  UpdatePackagesOmitError(),
		InstallPackages("clvm lvm2 kvm"),
	)
	pkg.AddCommands("vg-setup",
		Shell("pvcreate "+osddir+""),
		Shell("vgcreate "+vg+" "+osddir+""),
	)
}
<|endoftext|>


<commit_before>package iodaemon_test

import (
	"time"

	"io/ioutil"
	"os"
	"path/filepath"

	"bytes"
	"io"

	"github.com/cloudfoundry-incubator/guardian/rundmc/iodaemon"
	linkpkg "github.com/cloudfoundry-incubator/guardian/rundmc/iodaemon/link"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

type wc struct {
	*bytes.Buffer
}

func (b wc) Close() error {
	return nil
}

var _ = Describe("Iodaemon", func() {
	var (
		socketPath       string
		tmpdir           string
		fakeOut          wc
		fakeErr          wc
		expectedExitCode int

		wirer  *iodaemon.Wirer
		daemon *iodaemon.Daemon

		exited chan struct{}
	)

	const DEFAULT_TIMEOUT = "3s"

	BeforeEach(func() {
		var err error
		expectedExitCode = 0
		tmpdir, err = ioutil.TempDir("", "socket-dir")
		Expect(err).ToNot(HaveOccurred())

		socketPath = filepath.Join(tmpdir, "iodaemon.sock")

		exited = make(chan struct{})

		fakeOut = wc{
			bytes.NewBuffer([]byte{}),
		}
		fakeErr = wc{
			bytes.NewBuffer([]byte{}),
		}

		wirer = &iodaemon.Wirer{}
		daemon = &iodaemon.Daemon{}
	})

	AfterEach(func() {
		defer os.RemoveAll(tmpdir)

		Eventually(exited, DEFAULT_TIMEOUT).Should(BeClosed())

		By("tidying up the socket file")
		if _, err := os.Stat(socketPath); !os.IsNotExist(err) {
			Fail("socket file not cleaned up")
		}
	})

	Context("spawning a process", func() {
		spawnProcess := func(args ...string) {
			go func() {
				iodaemon.Spawn(socketPath, args, time.Second, fakeOut, wirer, daemon)
				close(exited)
			}()
		}

		It("times out when no listeners connect", func() {
			spawnProcess("echo", "hello")

			Eventually(exited, DEFAULT_TIMEOUT).Should(BeClosed())
		})

		It("reports back stdout", func() {
			spawnProcess("echo", "hello")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello\n"))
		})

		It("supports re-linking to an iodaemon instance", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			err = l.Writer.TerminateConnection()
			Expect(err).ToNot(HaveOccurred())

			m, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = m.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("reports back stderr", func() {
			spawnProcess("bash", "-c", "echo error 1>&2")

			_, _, linkStderr, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStderr, DEFAULT_TIMEOUT).Should(gbytes.Say("error\n"))
		})

		It("sends stdin to child", func() {
			spawnProcess("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo hello\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*hello.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("exits when the child exits", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("closes stdin when the link is closed", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			l.Close() //bash will normally terminate when it receives EOF on stdin
		})

		Context("when there is an existing socket file", func() {
			BeforeEach(func() {
				file, err := os.Create(socketPath)
				Expect(err).ToNot(HaveOccurred())
				file.Close()
			})

			It("still creates the process", func() {
				spawnProcess("echo", "hello")

				_, linkStdout, _, err := createLink(socketPath)
				Expect(err).ToNot(HaveOccurred())
				Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello\n"))
			})
		})
	})

	Context("spawning a tty", func() {
		spawnTty := func(args ...string) {
			go func() {
				iodaemon.Spawn(socketPath, args, time.Second, fakeOut, wirer, daemon)
				close(exited)
			}()
		}

		BeforeEach(func() {
			wirer.WithTty = true
			wirer.WindowColumns = 200
			wirer.WindowRows = 80
			daemon.WithTty = true
		})

		It("reports back stdout", func() {
			spawnTty("echo", "hello")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello"))
		})

		It("reports back stderr to stdout", func() {
			spawnTty("bash", "-c", "echo error 1>&2")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("error"))
		})

		It("exits when the child exits", func() {
			spawnTty("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("closes stdin when the link is closed", func() {
			spawnTty("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			l.Close() //bash will normally terminate when it receives EOF on stdin
		})

		It("sends stdin to child", func() {
			spawnTty("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo hello\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*hello.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("correctly sets the window size", func() {
			spawnTty("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo $COLUMNS $LINES\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*\\s200 80\\s.*"))

			Expect(l.SetWindowSize(100, 40)).To(Succeed())

			_, err = l.Write([]byte("echo $COLUMNS $LINES\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*\\s100 40\\s.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})
	})

})

func createLink(socketPath string) (*linkpkg.Link, io.WriteCloser, io.WriteCloser, error) {
	linkStdout := gbytes.NewBuffer()
	linkStderr := gbytes.NewBuffer()
	var l *linkpkg.Link
	var err error
	for i := 0; i < 100; i++ {
		time.Sleep(10 * time.Millisecond)
		l, err = linkpkg.Create(socketPath, linkStdout, linkStderr)
		if err == nil {
			break
		}
	}
	return l, linkStdout, linkStderr, err
}
<commit_msg>more error checking in iodaemon tests<commit_after>package iodaemon_test

import (
	"time"

	"io/ioutil"
	"os"
	"path/filepath"

	"bytes"
	"io"

	"github.com/cloudfoundry-incubator/guardian/rundmc/iodaemon"
	linkpkg "github.com/cloudfoundry-incubator/guardian/rundmc/iodaemon/link"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

type wc struct {
	*bytes.Buffer
}

func (b wc) Close() error {
	return nil
}

var _ = Describe("Iodaemon", func() {
	var (
		socketPath       string
		tmpdir           string
		fakeOut          wc
		fakeErr          wc
		expectedExitCode int

		wirer  *iodaemon.Wirer
		daemon *iodaemon.Daemon

		exited chan struct{}
	)

	const DEFAULT_TIMEOUT = "3s"

	BeforeEach(func() {
		var err error
		expectedExitCode = 0
		tmpdir, err = ioutil.TempDir("", "socket-dir")
		Expect(err).ToNot(HaveOccurred())

		socketPath = filepath.Join(tmpdir, "iodaemon.sock")

		exited = make(chan struct{})

		fakeOut = wc{
			bytes.NewBuffer([]byte{}),
		}
		fakeErr = wc{
			bytes.NewBuffer([]byte{}),
		}

		wirer = &iodaemon.Wirer{}
		daemon = &iodaemon.Daemon{}
	})

	AfterEach(func() {
		defer os.RemoveAll(tmpdir)

		Eventually(exited, DEFAULT_TIMEOUT).Should(BeClosed())

		By("tidying up the socket file")
		if _, err := os.Stat(socketPath); !os.IsNotExist(err) {
			Fail("socket file not cleaned up")
		}
	})

	Context("spawning a process: when no listeners connect", func() {
		spawnProcess := func(args ...string) {
			go func() {
				iodaemon.Spawn(socketPath, args, time.Second, fakeOut, wirer, daemon)
				close(exited)
			}()
		}

		It("times out when no listeners connect", func() {
			spawnProcess("echo", "hello")

			Eventually(exited, DEFAULT_TIMEOUT).Should(BeClosed())
		})
	})

	Context("spawning a process: when listeners connect", func() {
		spawnProcess := func(args ...string) {
			go func() {
				defer GinkgoRecover()
				Expect(iodaemon.Spawn(socketPath, args, time.Second, fakeOut, wirer, daemon)).To(Succeed())
				close(exited)
			}()
		}

		It("reports back stdout", func() {
			spawnProcess("echo", "hello")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello\n"))
		})

		It("supports re-linking to an iodaemon instance", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			err = l.Writer.TerminateConnection()
			Expect(err).ToNot(HaveOccurred())

			m, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = m.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("reports back stderr", func() {
			spawnProcess("bash", "-c", "echo error 1>&2")

			_, _, linkStderr, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStderr, DEFAULT_TIMEOUT).Should(gbytes.Say("error\n"))
		})

		It("sends stdin to child", func() {
			spawnProcess("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo hello\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*hello.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("exits when the child exits", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("closes stdin when the link is closed", func() {
			spawnProcess("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			Expect(l.Close()).To(Succeed()) //bash will normally terminate when it receives EOF on stdin
		})

		Context("when there is an existing socket file", func() {
			BeforeEach(func() {
				file, err := os.Create(socketPath)
				Expect(err).ToNot(HaveOccurred())
				Expect(file.Close()).To(Succeed())
			})

			It("still creates the process", func() {
				spawnProcess("echo", "hello")

				_, linkStdout, _, err := createLink(socketPath)
				Expect(err).ToNot(HaveOccurred())
				Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello\n"))
			})
		})
	})

	Context("spawning a tty", func() {
		spawnTty := func(args ...string) {
			go func() {
				defer GinkgoRecover()
				Expect(iodaemon.Spawn(socketPath, args, time.Second, fakeOut, wirer, daemon)).To(Succeed())
				close(exited)
			}()
		}

		BeforeEach(func() {
			wirer.WithTty = true
			wirer.WindowColumns = 200
			wirer.WindowRows = 80
			daemon.WithTty = true
		})

		It("reports back stdout", func() {
			spawnTty("echo", "hello")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("hello"))
		})

		It("reports back stderr to stdout", func() {
			spawnTty("bash", "-c", "echo error 1>&2")

			_, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say("error"))
		})

		It("exits when the child exits", func() {
			spawnTty("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("closes stdin when the link is closed", func() {
			spawnTty("bash")

			l, _, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			Expect(l.Close()).To(Succeed()) //bash will normally terminate when it receives EOF on stdin
		})

		It("sends stdin to child", func() {
			spawnTty("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo hello\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*hello.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})

		It("correctly sets the window size", func() {
			spawnTty("env", "-i", "bash", "--noprofile", "--norc")

			l, linkStdout, _, err := createLink(socketPath)
			Expect(err).ToNot(HaveOccurred())

			_, err = l.Write([]byte("echo $COLUMNS $LINES\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*\\s200 80\\s.*"))

			Expect(l.SetWindowSize(100, 40)).To(Succeed())

			_, err = l.Write([]byte("echo $COLUMNS $LINES\n"))
			Expect(err).ToNot(HaveOccurred())
			Eventually(linkStdout, DEFAULT_TIMEOUT).Should(gbytes.Say(".*\\s100 40\\s.*"))

			_, err = l.Write([]byte("exit\n"))
			Expect(err).ToNot(HaveOccurred())
		})
	})

})

func createLink(socketPath string) (*linkpkg.Link, io.WriteCloser, io.WriteCloser, error) {
	linkStdout := gbytes.NewBuffer()
	linkStderr := gbytes.NewBuffer()
	var l *linkpkg.Link
	var err error
	for i := 0; i < 100; i++ {
		time.Sleep(10 * time.Millisecond)
		l, err = linkpkg.Create(socketPath, linkStdout, linkStderr)
		if err == nil {
			break
		}
	}
	return l, linkStdout, linkStderr, err
}
<|endoftext|>


<commit_before>package generator

import (
	"encoding/xml"

	"socialapi/config"
	"socialapi/workers/sitemap/common"
	"socialapi/workers/sitemap/models"

	"github.com/koding/bongo"
	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/robfig/cron"
)

type Controller struct {
	log          logging.Logger
	fileSelector FileSelector
	fileName     string
	redisConn    *redis.RedisSession
}

const (
	// before sending this interval, beware that you have to change
	// TIMERANGE in cache key file
	// run cron job every 30 minutes starting from 0
	SCHEDULE = "0 0-59/30 * * * *"
)

var (
	cronJob *cron.Cron
)

func New(log logging.Logger, redisConn *redis.RedisSession) (*Controller, error) {
	c := &Controller{
		log:          log,
		fileSelector: CachedFileSelector{},
		redisConn:    redisConn,
	}

	return c, c.initCron()
}

func (c *Controller) initCron() error {
	cronJob = cron.New()
	if err := cronJob.AddFunc(SCHEDULE, c.generate); err != nil {
		return err
	}
	cronJob.Start()

	return nil
}

func (c *Controller) Shutdown() {
	cronJob.Stop()
}

func (c *Controller) generate() {
	c.log.Info("Sitemap update started")
	for {
		name, err := c.fileSelector.Select()
		if err == redis.ErrNil {
			return
		}

		if err != nil {
			c.log.Error("Could not fetch file name: %s", err)
			return
		}
		c.log.Info("Updating sitemap: %s", name)
		// there is not any waiting sitemap updates
		if name == "" {
			return
		}

		c.fileName = name

		els, err := c.fetchElements()
		if err != nil {
			c.log.Critical("Could not fetch updated elements: %s", err)
			c.handleError(els)
			continue
		}

		if len(els) == 0 {
			c.log.Info("Items are already added")
			continue
		}

		container := c.buildContainer(els)

		if err := c.updateFile(container); err != nil {
			c.handleError(els)
			c.log.Critical("Could not update file: %s", err)
			continue
		}
	}
	c.log.Info("Sitemap update finished")
}

// handleError re-adds updated items to next file update queue
func (c *Controller) handleError(items []*models.SitemapItem) {
	// re-add filename to next queue
	key := common.PrepareNextFileNameCacheKey()
	if _, err := c.redisConn.AddSetMembers(key, c.fileName); err != nil {
		c.log.Critical("Could not re-add the filename: %s", err)
		return
	}

	key = common.PrepareNextFileCacheKey(c.fileName)
	values := make([]interface{}, len(items))
	for k := range items {
		values[k] = items[k].PrepareSetValue()
	}

	if _, err := c.redisConn.AddSetMembers(key, values...); err != nil {
		c.log.Critical("Could not re-add the updated items: %s", err)
	}

}

func (c *Controller) fetchElements() ([]*models.SitemapItem, error) {
	key := common.PrepareCurrentFileCacheKey(c.fileName)
	els := make([]*models.SitemapItem, 0)

	members, err := c.redisConn.GetSetMembers(key)
	if err != nil && err != redis.ErrNil {
		return nil, err
	}

	for i := range members {
		item, err := c.redisConn.String(members[i])
		if err != nil {
			c.log.Error("Could not convert item: %s", err)
			continue
		}
		if item == "" {
			continue
		}

		i := &models.SitemapItem{}
		// if there is a syntax error in item, do not need to try to
		// recreate it
		if err := i.Populate(item); err != nil {
			c.log.Error("Could not get item %s: %s", item, err)
			continue
		}
		els = append(els, i)
	}
	if _, err := c.redisConn.Del(key); err != nil {
		c.log.Error("Could not delete key %s: %s", key, err)
	}

	return els, nil
}

func (c *Controller) updateFile(container *models.ItemContainer) error {
	sf := models.NewSitemapFile()
	newItem := false
	err := sf.ByName(c.fileName)
	if err == bongo.RecordNotFound {
		newItem = true
	}

	if err != nil && !newItem {
		return err
	}

	s := models.NewItemSet()
	if !newItem && len(sf.Blob) > 0 {
		if err := xml.Unmarshal(sf.Blob, &s); err != nil {
			return err
		}
	}

	s.Populate(container)
	v, err := xml.Marshal(s)
	if err != nil {
		return err
	}
	sf.Blob = v

	if newItem {
		sf.Name = c.fileName
		return sf.Create()
	}

	return sf.Update()
}

func (c *Controller) buildContainer(items []*models.SitemapItem) *models.ItemContainer {
	container := models.NewItemContainer()
	for _, v := range items {
		item := v.Definition(config.MustGet().Uri)
		switch v.Status {
		case models.STATUS_ADD:
			container.Add = append(container.Add, item)
		case models.STATUS_DELETE:
			container.Delete = append(container.Delete, item)
		case models.STATUS_UPDATE:
			container.Update = append(container.Update, item)
		}
	}

	return container
}
<commit_msg>Sitemap: Fix "update finished" is not displayed in log bug<commit_after>package generator

import (
	"encoding/xml"

	"socialapi/config"
	"socialapi/workers/sitemap/common"
	"socialapi/workers/sitemap/models"

	"github.com/koding/bongo"
	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/robfig/cron"
)

type Controller struct {
	log          logging.Logger
	fileSelector FileSelector
	fileName     string
	redisConn    *redis.RedisSession
}

const (
	// before sending this interval, beware that you have to change
	// TIMERANGE in cache key file
	// run cron job every 30 minutes starting from 0
	SCHEDULE = "0 0-59/30 * * * *"
)

var (
	cronJob *cron.Cron
)

func New(log logging.Logger, redisConn *redis.RedisSession) (*Controller, error) {
	c := &Controller{
		log:          log,
		fileSelector: CachedFileSelector{},
		redisConn:    redisConn,
	}

	return c, c.initCron()
}

func (c *Controller) initCron() error {
	cronJob = cron.New()
	if err := cronJob.AddFunc(SCHEDULE, c.generate); err != nil {
		return err
	}
	cronJob.Start()

	return nil
}

func (c *Controller) Shutdown() {
	cronJob.Stop()
}

func (c *Controller) generate() {
	c.log.Info("Sitemap update started")
	for {
		name, err := c.fileSelector.Select()
		if err == redis.ErrNil {
			c.log.Info("Sitemap update finished")
			return
		}

		if err != nil {
			c.log.Error("Could not fetch file name: %s", err)
			return
		}
		// there is not any waiting sitemap updates
		if name == "" {
			continue
		}

		c.log.Info("Updating sitemap: %s", name)

		c.fileName = name

		els, err := c.fetchElements()
		if err != nil {
			c.log.Critical("Could not fetch updated elements: %s", err)
			c.handleError(els)
			continue
		}

		if len(els) == 0 {
			c.log.Info("Items are already added")
			continue
		}

		container := c.buildContainer(els)

		if err := c.updateFile(container); err != nil {
			c.handleError(els)
			c.log.Critical("Could not update file: %s", err)
			continue
		}
	}
}

// handleError re-adds updated items to next file update queue
func (c *Controller) handleError(items []*models.SitemapItem) {
	// re-add filename to next queue
	key := common.PrepareNextFileNameCacheKey()
	if _, err := c.redisConn.AddSetMembers(key, c.fileName); err != nil {
		c.log.Critical("Could not re-add the filename: %s", err)
		return
	}

	key = common.PrepareNextFileCacheKey(c.fileName)
	values := make([]interface{}, len(items))
	for k := range items {
		values[k] = items[k].PrepareSetValue()
	}

	if _, err := c.redisConn.AddSetMembers(key, values...); err != nil {
		c.log.Critical("Could not re-add the updated items: %s", err)
	}

}

func (c *Controller) fetchElements() ([]*models.SitemapItem, error) {
	key := common.PrepareCurrentFileCacheKey(c.fileName)
	els := make([]*models.SitemapItem, 0)

	members, err := c.redisConn.GetSetMembers(key)
	if err != nil && err != redis.ErrNil {
		return nil, err
	}

	for i := range members {
		item, err := c.redisConn.String(members[i])
		if err != nil {
			c.log.Error("Could not convert item: %s", err)
			continue
		}
		if item == "" {
			continue
		}

		i := &models.SitemapItem{}
		// if there is a syntax error in item, do not need to try to
		// recreate it
		if err := i.Populate(item); err != nil {
			c.log.Error("Could not get item %s: %s", item, err)
			continue
		}
		els = append(els, i)
	}
	if _, err := c.redisConn.Del(key); err != nil {
		c.log.Error("Could not delete key %s: %s", key, err)
	}

	return els, nil
}

func (c *Controller) updateFile(container *models.ItemContainer) error {
	sf := models.NewSitemapFile()
	newItem := false
	err := sf.ByName(c.fileName)
	if err == bongo.RecordNotFound {
		newItem = true
	}

	if err != nil && !newItem {
		return err
	}

	s := models.NewItemSet()
	if !newItem && len(sf.Blob) > 0 {
		if err := xml.Unmarshal(sf.Blob, &s); err != nil {
			return err
		}
	}

	s.Populate(container)
	v, err := xml.Marshal(s)
	if err != nil {
		return err
	}
	sf.Blob = v

	if newItem {
		sf.Name = c.fileName
		return sf.Create()
	}

	return sf.Update()
}

func (c *Controller) buildContainer(items []*models.SitemapItem) *models.ItemContainer {
	container := models.NewItemContainer()
	for _, v := range items {
		item := v.Definition(config.MustGet().Uri)
		switch v.Status {
		case models.STATUS_ADD:
			container.Add = append(container.Add, item)
		case models.STATUS_DELETE:
			container.Delete = append(container.Delete, item)
		case models.STATUS_UPDATE:
			container.Update = append(container.Update, item)
		}
	}

	return container
}
<|endoftext|>


<commit_before>package prompt

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	logfile      = "/tmp/go-prompt-debug.log"
	envEnableLog = "GO_PROMPT_ENABLE_LOG"
)

type Executor func(string)
type Completer func(string) []Suggest

type Prompt struct {
	in         ConsoleParser
	buf        *Buffer
	renderer   *Render
	executor   Executor
	history    *History
	completion *CompletionManager
}

type Exec struct {
	input string
}

func (p *Prompt) Run() {
	p.setUp()
	defer p.tearDown()

	if os.Getenv(envEnableLog) != "true" {
		log.SetOutput(ioutil.Discard)
	} else if f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
		log.SetOutput(ioutil.Discard)
	} else {
		defer f.Close()
		log.SetOutput(f)
		log.Println("[INFO] Logging is enabled.")
	}

	p.renderer.Render(p.buf, p.completion)

	bufCh := make(chan []byte, 128)
	stopReadBufCh := make(chan struct{})
	go p.readBuffer(bufCh, stopReadBufCh)

	exitCh := make(chan int)
	winSizeCh := make(chan *WinSize)
	go handleSignals(p.in, exitCh, winSizeCh)

	for {
		select {
		case b := <-bufCh:
			if shouldExit, e := p.feed(b); shouldExit {
				return
			} else if e != nil {
				// Stop goroutine to run readBuffer function
				stopReadBufCh <- struct{}{}

				// Unset raw mode
				// Reset to Blocking mode because returned EAGAIN when still set non-blocking mode.
				p.in.TearDown()
				p.executor(e.input)

				p.completion.Update(p.buf.Text())
				p.renderer.Render(p.buf, p.completion)

				// Set raw mode
				p.in.Setup()
				go p.readBuffer(bufCh, stopReadBufCh)
			} else {
				p.completion.Update(p.buf.Text())
				p.renderer.Render(p.buf, p.completion)
			}
		case w := <-winSizeCh:
			p.renderer.UpdateWinSize(w)
			p.renderer.Render(p.buf, p.completion)
		case code := <-exitCh:
			p.tearDown()
			os.Exit(code)
		default:
			time.Sleep(10 * time.Millisecond)
		}
	}
}

func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
	key := p.in.GetKey(b)

	switch key {
	case ControlJ, Enter:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
		}
		p.renderer.BreakLine(p.buf)

		exec = &Exec{input: p.buf.Text()}
		log.Printf("[History] %s", p.buf.Text())
		p.buf = NewBuffer()
		p.completion.Reset()
		if exec.input != "" {
			p.history.Add(exec.input)
		}
	case ControlC:
		p.renderer.BreakLine(p.buf)
		p.buf = NewBuffer()
		p.completion.Reset()
		p.history.Clear()
	case ControlD:
		shouldExit = true
	case Up:
		if !p.completion.Completing() {
			if newBuf, changed := p.history.Older(p.buf); changed {
				p.buf = newBuf
			}
			return
		}
		fallthrough
	case BackTab:
		p.completion.Previous()
	case Down:
		if !p.completion.Completing() {
			if newBuf, changed := p.history.Newer(p.buf); changed {
				p.buf = newBuf
			}
			return
		}
		fallthrough
	case Tab, ControlI:
		p.completion.Next()
	case Left:
		p.buf.CursorLeft(1)
	case Right:
		p.buf.CursorRight(1)
	case Backspace:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
			p.completion.Reset()
		}
		p.buf.DeleteBeforeCursor(1)
	case NotDefined:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
		}
		p.completion.Reset()
		p.buf.InsertText(string(b), false, true)
	default:
		p.completion.Reset()
	}
	return
}

func (p *Prompt) setUp() {
	p.in.Setup()
	p.renderer.Setup()
	p.renderer.UpdateWinSize(p.in.GetWinSize())
}

func (p *Prompt) tearDown() {
	p.in.TearDown()
	p.renderer.TearDown()
}

func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) {
	buf := make([]byte, 1024)

	log.Printf("[INFO] readBuffer start")
	for {
		time.Sleep(10 * time.Millisecond)
		select {
		case <-stopCh:
			log.Print("[INFO] stop p.readBuffer")
			return
		default:
			if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
				bufCh <- buf[:n]
			}
		}
	}
}

func handleSignals(in ConsoleParser, exitCh chan int, winSizeCh chan *WinSize) {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(
		sigCh,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		syscall.SIGWINCH,
	)

	for {
		s := <-sigCh
		switch s {
		case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
			log.Println("[SIGNAL] Catch SIGINT")
			exitCh <- 0

		case syscall.SIGTERM: // kill -SIGTERM XXXX
			log.Println("[SIGNAL] Catch SIGTERM")
			exitCh <- 1

		case syscall.SIGQUIT: // kill -SIGQUIT XXXX
			log.Println("[SIGNAL] Catch SIGQUIT")
			exitCh <- 0

		case syscall.SIGWINCH:
			log.Println("[SIGNAL] Catch SIGWINCH")
			winSizeCh <- in.GetWinSize()
		default:
			time.Sleep(10 * time.Millisecond)
		}
	}
}
<commit_msg>Enter when exit<commit_after>package prompt

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	logfile      = "/tmp/go-prompt-debug.log"
	envEnableLog = "GO_PROMPT_ENABLE_LOG"
)

type Executor func(string)
type Completer func(string) []Suggest

type Prompt struct {
	in         ConsoleParser
	buf        *Buffer
	renderer   *Render
	executor   Executor
	history    *History
	completion *CompletionManager
}

type Exec struct {
	input string
}

func (p *Prompt) Run() {
	p.setUp()
	defer p.tearDown()

	if os.Getenv(envEnableLog) != "true" {
		log.SetOutput(ioutil.Discard)
	} else if f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
		log.SetOutput(ioutil.Discard)
	} else {
		defer f.Close()
		log.SetOutput(f)
		log.Println("[INFO] Logging is enabled.")
	}

	p.renderer.Render(p.buf, p.completion)

	bufCh := make(chan []byte, 128)
	stopReadBufCh := make(chan struct{})
	go p.readBuffer(bufCh, stopReadBufCh)

	exitCh := make(chan int)
	winSizeCh := make(chan *WinSize)
	go handleSignals(p.in, exitCh, winSizeCh)

	for {
		select {
		case b := <-bufCh:
			if shouldExit, e := p.feed(b); shouldExit {
				p.renderer.BreakLine(p.buf)
				return
			} else if e != nil {
				// Stop goroutine to run readBuffer function
				stopReadBufCh <- struct{}{}

				// Unset raw mode
				// Reset to Blocking mode because returned EAGAIN when still set non-blocking mode.
				p.in.TearDown()
				p.executor(e.input)

				p.completion.Update(p.buf.Text())
				p.renderer.Render(p.buf, p.completion)

				// Set raw mode
				p.in.Setup()
				go p.readBuffer(bufCh, stopReadBufCh)
			} else {
				p.completion.Update(p.buf.Text())
				p.renderer.Render(p.buf, p.completion)
			}
		case w := <-winSizeCh:
			p.renderer.UpdateWinSize(w)
			p.renderer.Render(p.buf, p.completion)
		case code := <-exitCh:
			p.renderer.BreakLine(p.buf)
			p.tearDown()
			os.Exit(code)
		default:
			time.Sleep(10 * time.Millisecond)
		}
	}
}

func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
	key := p.in.GetKey(b)

	switch key {
	case ControlJ, Enter:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
		}
		p.renderer.BreakLine(p.buf)

		exec = &Exec{input: p.buf.Text()}
		log.Printf("[History] %s", p.buf.Text())
		p.buf = NewBuffer()
		p.completion.Reset()
		if exec.input != "" {
			p.history.Add(exec.input)
		}
	case ControlC:
		p.renderer.BreakLine(p.buf)
		p.buf = NewBuffer()
		p.completion.Reset()
		p.history.Clear()
	case ControlD:
		shouldExit = true
	case Up:
		if !p.completion.Completing() {
			if newBuf, changed := p.history.Older(p.buf); changed {
				p.buf = newBuf
			}
			return
		}
		fallthrough
	case BackTab:
		p.completion.Previous()
	case Down:
		if !p.completion.Completing() {
			if newBuf, changed := p.history.Newer(p.buf); changed {
				p.buf = newBuf
			}
			return
		}
		fallthrough
	case Tab, ControlI:
		p.completion.Next()
	case Left:
		p.buf.CursorLeft(1)
	case Right:
		p.buf.CursorRight(1)
	case Backspace:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
			p.completion.Reset()
		}
		p.buf.DeleteBeforeCursor(1)
	case NotDefined:
		if s, ok := p.completion.GetSelectedSuggestion(); ok {
			w := p.buf.Document().GetWordBeforeCursor()
			if w != "" {
				p.buf.DeleteBeforeCursor(len([]rune(w)))
			}
			p.buf.InsertText(s.Text, false, true)
		}
		p.completion.Reset()
		p.buf.InsertText(string(b), false, true)
	default:
		p.completion.Reset()
	}
	return
}

func (p *Prompt) setUp() {
	p.in.Setup()
	p.renderer.Setup()
	p.renderer.UpdateWinSize(p.in.GetWinSize())
}

func (p *Prompt) tearDown() {
	p.in.TearDown()
	p.renderer.TearDown()
}

func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) {
	buf := make([]byte, 1024)

	log.Printf("[INFO] readBuffer start")
	for {
		time.Sleep(10 * time.Millisecond)
		select {
		case <-stopCh:
			log.Print("[INFO] stop p.readBuffer")
			return
		default:
			if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
				bufCh <- buf[:n]
			}
		}
	}
}

func handleSignals(in ConsoleParser, exitCh chan int, winSizeCh chan *WinSize) {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(
		sigCh,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		syscall.SIGWINCH,
	)

	for {
		s := <-sigCh
		switch s {
		case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
			log.Println("[SIGNAL] Catch SIGINT")
			exitCh <- 0

		case syscall.SIGTERM: // kill -SIGTERM XXXX
			log.Println("[SIGNAL] Catch SIGTERM")
			exitCh <- 1

		case syscall.SIGQUIT: // kill -SIGQUIT XXXX
			log.Println("[SIGNAL] Catch SIGQUIT")
			exitCh <- 0

		case syscall.SIGWINCH:
			log.Println("[SIGNAL] Catch SIGWINCH")
			winSizeCh <- in.GetWinSize()
		default:
			time.Sleep(10 * time.Millisecond)
		}
	}
}
<|endoftext|>