<commit_before>// Copyright (c) 2013, Bartosz Kratochwil
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.

//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.

//     * Neither the name of Aaron France nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.

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

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

/*
 ListDBInstances will list all the available database instances

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-database-instances.html
*/
func (a Access) ListDBInstances() (*DBInstances, error) {
	url := fmt.Sprintf("%s%s/instances", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}
	dbs := &DBInstances{}
	err = json.Unmarshal(body, dbs)
	return dbs, err
}

/*
 This function takes instance ID and deletes database instance with this ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/delete-instance.html
*/
func (a Access) DeleteDBInstance(instanceID string) error {
	url := fmt.Sprintf("%s%s/instances/%s", RDB_URL, a.TenantID,
		instanceID)
	_, err := a.baseRequest(url, "DELETE", nil)
	return err
}

/*
 This function takes instance ID and restarts DB instance with this ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/restart-instance.html
*/
func (a Access) RestartDBInstance(instanceID string) error {
	b := `{"restart":{}}`
	url := fmt.Sprintf("%s%s/instances/%s/action", RDB_URL,
		a.TenantID, instanceID)
	_, err := a.baseRequest(url, "POST", strings.NewReader(b))
	return err
}

/*
 ListAllFlavors lists all available database flavors.
 This function implements interface as described in:-
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-flavors.html
*/
func (a Access) ListAllFlavors() (*DBFlavors, error) {
	url := fmt.Sprintf("%s%s/flavors", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}

	flv := &DBFlavors{}
	err = json.Unmarshal(body, flv)
	return flv, err
}

/*
 This function returns flavor specs for given flavor.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-flavor.html
*/
func (a Access) GetDBFlavor(ID string) (*DBFlavor, error) {
	url := fmt.Sprintf("%s/flavors/%s", RDB_URL, ID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}

	flv := &DBFlavor{}
	err = json.Unmarshal(body, flv)
	if err != nil {
		return nil, err
	}
	return flv, nil
}

/*
 CreateDBInstance creates new database instance in the HPCloud using
settings found in the DatabaseReq instance passed to this function

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/create-instance.html
*/
func (a Access) CreateDBInstance(db DatabaseReq) (*NewDBInstance, error) {
	b, err := db.MarshalJSON()
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s%s/instances", RDB_URL, a.TenantID)

	body, err := a.baseRequest(url, "POST",
		strings.NewReader(string(b)))
	if err != nil {
		return nil, err
	}

	type respDB struct {
		Instance NewDBInstance `json:"instance"`
	}

	sr := &respDB{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.Instance, nil
}

/*
 This function retrieves details of the instance with provided ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-instance.html
*/
func (a Access) GetDBInstance(id string) (*InstDetails, error) {
	url := fmt.Sprintf("%s%s/instances/%s", RDB_URL, a.TenantID, id)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}
	det := &InstDetails{}
	err = json.Unmarshal(body, det)
	return det, err
}

/*
 This function takes instance ID and resets password for this instance. It
 returns a new instance password.

 This function implements the interface as decribed in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/reset-instance-password.html
*/
func (a Access) ResetDBPassword(id string) (*DBCredentials, error) {
	b := `{"reset-password":{}}`
	url := fmt.Sprintf("%s%s/instances/%s/action", RDB_URL,
		a.TenantID, id)
	body, err := a.baseRequest(url, "POST", strings.NewReader(b))

	sr := &DBCredentials{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
 This function lists all the security groups available for tenant.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-security-groups.html
*/
func (a Access) GetDBSecurityGroups() (*[]SecurityGroup, error) {
	url := fmt.Sprintf("%s%s/security-groups", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)

	type resp struct {
		SecurityGroups []SecurityGroup `json:"security_groups"`
	}
	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.SecurityGroups, nil
}

/*
 This function lists specific security group.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-security-group.html
*/
func (a Access) DBSecGroupDetails(sg string) (*SecurityGroup, error) {
	url := fmt.Sprintf("%s%s/security-groups/%s", RDB_URL, a.TenantID, sg)
	body, err := a.baseRequest(url, "GET", nil)

	type resp struct {
		SecurityGroup SecurityGroup `json:"security_group"`
	}

	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.SecurityGroup, nil
}

/*
 Creates new security group rule

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/create-security-group-rule.html
*/
func (a Access) CreateDBSecRule(Req DBSecRuleReq) (*DBSecRule, error) {
	url := fmt.Sprintf("%s%s/security-group-rules", RDB_URL, a.TenantID)
	b, err := Req.MarshalJSON()

	if err != nil {
		return nil, err
	}

	body, err := a.baseRequest(url, "POST",
		strings.NewReader(string(b)))

	type resp struct {
		SecurityGroupRule DBSecRule `json:"security_group_rule"`
	}

	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}

	return &sr.SecurityGroupRule, nil
}

type DBInstance struct {
	Created string `json:"created"`
	Id      string `json:"id"`
	Links   []Link `json:"links"`
	Name    string `json:"name"`
	Status  string `json:"name"`
	Flavor  struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
}

type DBInstances struct {
	Instances []DBInstance `json:"instances"`
}

type Database struct {
	Name      string `json:"name"`
	FlavorRef string `json:"flavorRef"`
	Port      int    `json:"port"`
	DBType    struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	} `json:"dbtype"`
}

type DBFlavors struct {
	Flavors []DBFlavor `json:"flavors"`
}

/*
 Instance Details type that is returned by server
*/
type InstDetails struct {
	Created        string          `json:"created"`
	Hostname       string          `json:"hostname"`
	ID             string          `json:"id"`
	Links          []Link          `json:"links"`
	Name           string          `json:"name"`
	Port           int             `json:"port"`
	SecurityGroups []SecurityGroup `json:"security_groups"`
	Status         string          `json:"status"`
	Updated        string          `json:"updated"`
	Flavor         struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
}

/*
 Type describing database flavor
*/
type DBFlavor struct {
	Id    int    `json:"id"`
	Links []Link `json:"links"`
	Name  string `json:"name"`
	Ram   int    `json:"ram"`
	Vcpu  int    `json:"vcpu"`
}

/*
 This type describes the JSON data which should be sent to the
create database instance resource.
*/
type DatabaseReq struct {
	Instance Database `json:"instance"`
}

/*
 This type describes JSON response from a successful CreateDBInstance
 call.
*/
type NewDBInstance struct {
	Created    string        `json:"created"`
	Credential DBCredentials `json:"credential"`
	Flavor     struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
	Hostname       string        `json:"hostname"`
	Id             string        `json:"id"`
	Links          []Link        `json:"links"`
	Name           string        `json:"name"`
	SecurityGroups []DBSecGroups `json:"security_groups"`
	Status         string        `json:"status"`
}

/*
 This type describes Database Security groups
*/
type DBSecGroups struct {
	Id    string `json:"id"`
	Links []Link `json:"links"`
}

/*
 This type describes Database Credentials
*/
type DBCredentials struct {
	Password string `json:"password"`
	Username string `json:"username"`
}

/*
 DB Security Group Create request struct
*/
type DBSecRuleReq struct {
	SecurityGroupID string `json:"security_group_rule"`
	Cidr            string `json:"cidr"`
	FromPort        int64  `json:"from_port"`
	ToPort          int64  `json:"to_port"`
}

type DBSecRule struct {
	ID              string `json:"id"`
	SecurityGroupID string `json:"security_group_rule"`
	Cidr            string `json:"cidr"`
	FromPort        int64  `json:"from_port"`
	ToPort          int64  `json:"to_port"`
	Created         string `json:"created"`
}

func (f DBFlavors) GetFlavorRef(fn string) string {
	for _, val := range f.Flavors {
		if val.Name == fn {
			return val.Links[0].HREF
		}
	}
	panic("Flavor not found")
}

/*
 Creates JSON string for Create DB request
*/
func (db DatabaseReq) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString(`{"instance":{`)
	if db.Instance.Name == "" {
		return nil, errors.New("A name is required")
	}
	b.WriteString(fmt.Sprintf(`"name":"%s",`, db.Instance.Name))
	if db.Instance.FlavorRef == "" {
		return nil, errors.New("Flavor is required")
	}
	b.WriteString(fmt.Sprintf(`"flavorRef":"%s",`,
		db.Instance.FlavorRef))
	if db.Instance.Port == 0 {
		b.WriteString(`"port":"3306",`)
	} else {
		b.WriteString(fmt.Sprintf(`"port":"%s",`, db.Instance.Port))
	}
	b.WriteString(`"dbtype":{`)
	b.WriteString(`"name":"mysql",`)
	b.WriteString(`"version":"5.5"}}}`)

	return b.Bytes(), nil
}

func (rq DBSecRuleReq) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString(`{"security_group_rule":{`)
	if rq.SecurityGroupID == "" {
		return nil, errors.New("Security group ID required")
	}
	b.WriteString(fmt.Sprintf(`"security_group_id":"%s",`,
		rq.SecurityGroupID))
	if rq.Cidr == "" {
		return nil, errors.New("Cidr is missing")
	}
	b.WriteString(fmt.Sprintf(`"cidr":"%s",`, rq.Cidr))
	if rq.FromPort == 0 {
		return nil, errors.New("from_port value is missing")
	}
	b.WriteString(fmt.Sprintf(`"from_port":"%s",`, rq.FromPort))
	if rq.ToPort == 0 {
		return nil, errors.New("to_port value is missing")
	}
	b.WriteString(fmt.Sprintf(`"to_port":"%s"}}`, rq.ToPort))

	return b.Bytes(), nil
}
<commit_msg>added fixes to add sec rule<commit_after>// Copyright (c) 2013, Bartosz Kratochwil
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.

//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.

//     * Neither the name of Aaron France nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.

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

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

/*
 ListDBInstances will list all the available database instances

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-database-instances.html
*/
func (a Access) ListDBInstances() (*DBInstances, error) {
	url := fmt.Sprintf("%s%s/instances", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}
	dbs := &DBInstances{}
	err = json.Unmarshal(body, dbs)
	return dbs, err
}

/*
 This function takes instance ID and deletes database instance with this ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/delete-instance.html
*/
func (a Access) DeleteDBInstance(instanceID string) error {
	url := fmt.Sprintf("%s%s/instances/%s", RDB_URL, a.TenantID,
		instanceID)
	_, err := a.baseRequest(url, "DELETE", nil)
	return err
}

/*
 This function takes instance ID and restarts DB instance with this ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/restart-instance.html
*/
func (a Access) RestartDBInstance(instanceID string) error {
	b := `{"restart":{}}`
	url := fmt.Sprintf("%s%s/instances/%s/action", RDB_URL,
		a.TenantID, instanceID)
	_, err := a.baseRequest(url, "POST", strings.NewReader(b))
	return err
}

/*
 ListAllFlavors lists all available database flavors.
 This function implements interface as described in:-
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-flavors.html
*/
func (a Access) ListAllFlavors() (*DBFlavors, error) {
	url := fmt.Sprintf("%s%s/flavors", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}

	flv := &DBFlavors{}
	err = json.Unmarshal(body, flv)
	return flv, err
}

/*
 This function returns flavor specs for given flavor.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-flavor.html
*/
func (a Access) GetDBFlavor(ID string) (*DBFlavor, error) {
	url := fmt.Sprintf("%s/flavors/%s", RDB_URL, ID)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}

	flv := &DBFlavor{}
	err = json.Unmarshal(body, flv)
	if err != nil {
		return nil, err
	}
	return flv, nil
}

/*
 CreateDBInstance creates new database instance in the HPCloud using
settings found in the DatabaseReq instance passed to this function

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/create-instance.html
*/
func (a Access) CreateDBInstance(db DatabaseReq) (*NewDBInstance, error) {
	b, err := db.MarshalJSON()
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s%s/instances", RDB_URL, a.TenantID)

	body, err := a.baseRequest(url, "POST",
		strings.NewReader(string(b)))
	if err != nil {
		return nil, err
	}

	type respDB struct {
		Instance NewDBInstance `json:"instance"`
	}

	sr := &respDB{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.Instance, nil
}

/*
 This function retrieves details of the instance with provided ID.

This function implements the interface as described in:
http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-instance.html
*/
func (a Access) GetDBInstance(id string) (*InstDetails, error) {
	url := fmt.Sprintf("%s%s/instances/%s", RDB_URL, a.TenantID, id)
	body, err := a.baseRequest(url, "GET", nil)
	if err != nil {
		return nil, err
	}
	det := &InstDetails{}
	err = json.Unmarshal(body, det)
	return det, err
}

/*
 This function takes instance ID and resets password for this instance. It
 returns a new instance password.

 This function implements the interface as decribed in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/reset-instance-password.html
*/
func (a Access) ResetDBPassword(id string) (*DBCredentials, error) {
	b := `{"reset-password":{}}`
	url := fmt.Sprintf("%s%s/instances/%s/action", RDB_URL,
		a.TenantID, id)
	body, err := a.baseRequest(url, "POST", strings.NewReader(b))

	sr := &DBCredentials{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
 This function lists all the security groups available for tenant.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/list-security-groups.html
*/
func (a Access) GetDBSecurityGroups() (*[]SecurityGroup, error) {
	url := fmt.Sprintf("%s%s/security-groups", RDB_URL, a.TenantID)
	body, err := a.baseRequest(url, "GET", nil)

	type resp struct {
		SecurityGroups []SecurityGroup `json:"security_groups"`
	}
	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.SecurityGroups, nil
}

/*
 This function lists specific security group.

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/get-security-group.html
*/
func (a Access) DBSecGroupDetails(sg string) (*SecurityGroup, error) {
	url := fmt.Sprintf("%s%s/security-groups/%s", RDB_URL, a.TenantID, sg)
	body, err := a.baseRequest(url, "GET", nil)

	type resp struct {
		SecurityGroup SecurityGroup `json:"security_group"`
	}

	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return &sr.SecurityGroup, nil
}

/*
 Creates new security group rule

 This function implements the interface as described in:
 http://api-docs.hpcloud.com/hpcloud-rdb-mysql/1.0/content/create-security-group-rule.html
*/
func (a Access) CreateDBSecRule(Req DBSecRuleReq) (*DBSecRule, error) {
	url := fmt.Sprintf("%s%s/security-group-rules", RDB_URL, a.TenantID)
	b, err := Req.MarshalJSON()

	if err != nil {
		return nil, err
	}

	body, err := a.baseRequest(url, "POST",
		strings.NewReader(string(b)))

	type resp struct {
		SecurityGroupRule DBSecRule `json:"security_group_rule"`
	}

	sr := &resp{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}

	return &sr.SecurityGroupRule, nil
}

type DBInstance struct {
	Created string `json:"created"`
	Id      string `json:"id"`
	Links   []Link `json:"links"`
	Name    string `json:"name"`
	Status  string `json:"name"`
	Flavor  struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
}

type DBInstances struct {
	Instances []DBInstance `json:"instances"`
}

type Database struct {
	Name      string `json:"name"`
	FlavorRef string `json:"flavorRef"`
	Port      int    `json:"port"`
	DBType    struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	} `json:"dbtype"`
}

type DBFlavors struct {
	Flavors []DBFlavor `json:"flavors"`
}

/*
 Instance Details type that is returned by server
*/
type InstDetails struct {
	Created        string          `json:"created"`
	Hostname       string          `json:"hostname"`
	ID             string          `json:"id"`
	Links          []Link          `json:"links"`
	Name           string          `json:"name"`
	Port           int             `json:"port"`
	SecurityGroups []SecurityGroup `json:"security_groups"`
	Status         string          `json:"status"`
	Updated        string          `json:"updated"`
	Flavor         struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
}

/*
 Type describing database flavor
*/
type DBFlavor struct {
	Id    int    `json:"id"`
	Links []Link `json:"links"`
	Name  string `json:"name"`
	Ram   int    `json:"ram"`
	Vcpu  int    `json:"vcpu"`
}

/*
 This type describes the JSON data which should be sent to the
create database instance resource.
*/
type DatabaseReq struct {
	Instance Database `json:"instance"`
}

/*
 This type describes JSON response from a successful CreateDBInstance
 call.
*/
type NewDBInstance struct {
	Created    string        `json:"created"`
	Credential DBCredentials `json:"credential"`
	Flavor     struct {
		Name  string `json:"name"`
		ID    string `json:"id"`
		Links []Link `json:"links"`
	} `json:"flavor"`
	Hostname       string        `json:"hostname"`
	Id             string        `json:"id"`
	Links          []Link        `json:"links"`
	Name           string        `json:"name"`
	SecurityGroups []DBSecGroups `json:"security_groups"`
	Status         string        `json:"status"`
}

/*
 This type describes Database Security groups
*/
type DBSecGroups struct {
	Id    string `json:"id"`
	Links []Link `json:"links"`
}

/*
 This type describes Database Credentials
*/
type DBCredentials struct {
	Password string `json:"password"`
	Username string `json:"username"`
}

/*
 DB Security Group Create request struct
*/
type DBSecRuleReq struct {
	SecurityGroupID string `json:"security_group_rule"`
	Cidr            string `json:"cidr"`
	FromPort        int64  `json:"from_port"`
	ToPort          int64  `json:"to_port"`
}

type DBSecRule struct {
	ID              string `json:"id"`
	SecurityGroupID string `json:"security_group_rule"`
	Cidr            string `json:"cidr"`
	FromPort        int64  `json:"from_port"`
	ToPort          int64  `json:"to_port"`
	Created         string `json:"created"`
}

func (f DBFlavors) GetFlavorRef(fn string) string {
	for _, val := range f.Flavors {
		if val.Name == fn {
			return val.Links[0].HREF
		}
	}
	panic("Flavor not found")
}

/*
 Creates JSON string for Create DB request
*/
func (db DatabaseReq) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString(`{"instance":{`)
	if db.Instance.Name == "" {
		return nil, errors.New("A name is required")
	}
	b.WriteString(fmt.Sprintf(`"name":"%s",`, db.Instance.Name))
	if db.Instance.FlavorRef == "" {
		return nil, errors.New("Flavor is required")
	}
	b.WriteString(fmt.Sprintf(`"flavorRef":"%s",`,
		db.Instance.FlavorRef))
	if db.Instance.Port == 0 {
		b.WriteString(`"port":"3306",`)
	} else {
		b.WriteString(fmt.Sprintf(`"port":"%s",`, db.Instance.Port))
	}
	b.WriteString(`"dbtype":{`)
	b.WriteString(`"name":"mysql",`)
	b.WriteString(`"version":"5.5"}}}`)

	return b.Bytes(), nil
}

func (rq DBSecRuleReq) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString(`{"security_group_rule":{`)
	if rq.SecurityGroupID == "" {
		return nil, errors.New("Security group ID required")
	}
	b.WriteString(fmt.Sprintf(`"security_group_id":"%s",`,
		rq.SecurityGroupID))
	if rq.Cidr == "" {
		return nil, errors.New("Cidr is missing")
	}
	b.WriteString(fmt.Sprintf(`"cidr":"%s",`, rq.Cidr))
	if rq.FromPort == 0 {
		return nil, errors.New("from_port value is missing")
	}
	b.WriteString(fmt.Sprintf(`"from_port":%d,`, rq.FromPort))
	if rq.ToPort == 0 {
		return nil, errors.New("to_port value is missing")
	}
	b.WriteString(fmt.Sprintf(`"to_port":%d}}`, rq.ToPort))

	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>package rfw

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"
)

type Rfw struct {
	lock               sync.RWMutex
	basepath           string
	lastTime           time.Time
	remainCntOfLogFile int
	outFile            *os.File
}

type RfwOption func(r *Rfw)

func WithCleanUp(remainCnt int) RfwOption {
	return func(r *Rfw) {
		r.remainCntOfLogFile = remainCnt
	}
}

func generatePath(basepath string, t time.Time) string {
	return fmt.Sprintf("%s-%4d%02d%02d", basepath, t.Year(), t.Month(), t.Day())
}

func New(basepath string) (*Rfw, error) {
	return NewWithOptions(basepath)
}

func NewWithOptions(basepath string, opts ...RfwOption) (*Rfw, error) {
	t := time.Now()
	path := generatePath(basepath, t)
	r, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return nil, err
	}
	rt := &Rfw{basepath: basepath, lastTime: t, outFile: r}
	for _, o := range opts {
		o(rt)
	}
	if err = rt.checkClearLogFile(t); err != nil {
		return nil, err
	}
	return rt, nil
}

func getOutdatedPath(basepath string, paths []string, now time.Time, remain int) []string {
	sort.Strings(paths)
	edgepath := generatePath(basepath, now.AddDate(0, 0, 0-remain))
	i := sort.SearchStrings(paths, edgepath)
	if i <= 0 {
		return []string{}
	} else if i > len(paths) {
		return paths
	} else {
		return paths[:i]
	}
}

func (w *Rfw) checkClearLogFile(now time.Time) error {
	if w.remainCntOfLogFile <= 0 {
		return nil
	}
	matches, err := filepath.Glob(w.basepath + "-")
	if err != nil {
		return err
	}
	torms := getOutdatedPath(w.basepath, matches, now, w.remainCntOfLogFile)
	for _, p := range torms {
		os.Remove(p)
	}
	return nil
}

func (w *Rfw) Write(p []byte) (int, error) {
	w.lock.RLock()
	defer w.lock.RUnlock()
	if w.outFile == nil {
		return 0, errors.New(fmt.Sprintf("Rfw is closed. Basepath=%s", w.basepath))
	}
	t := time.Now()
	if t.YearDay() != w.lastTime.YearDay() || t.Year() != w.lastTime.Year() {
		needcheck := false
		w.lock.RUnlock()
		w.lock.Lock()
		if t.YearDay() != w.lastTime.YearDay() || t.Year() != w.lastTime.Year() {
			needcheck = true
			path := generatePath(w.basepath, t)
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
			if err != nil {
				w.lock.Unlock()
				w.lock.RLock()
				return 0, err
			}
			w.outFile.Close()
			w.outFile = f
			w.lastTime = t
		}
		w.lock.Unlock()
		w.lock.RLock()
		if needcheck {
			w.checkClearLogFile(t)
		}
	}
	return w.outFile.Write(p)
}

func (w *Rfw) Close() (err error) {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.outFile != nil {
		w.outFile.Close()
		w.outFile = nil
	}
	return
}
<commit_msg>fix bug of checkCleanLogfile<commit_after>package rfw

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"
)

type Rfw struct {
	lock               sync.RWMutex
	basepath           string
	lastTime           time.Time
	remainCntOfLogFile int
	outFile            *os.File
}

type RfwOption func(r *Rfw)

func WithCleanUp(remainCnt int) RfwOption {
	return func(r *Rfw) {
		r.remainCntOfLogFile = remainCnt
	}
}

func generatePath(basepath string, t time.Time) string {
	return fmt.Sprintf("%s-%4d%02d%02d", basepath, t.Year(), t.Month(), t.Day())
}

func New(basepath string) (*Rfw, error) {
	return NewWithOptions(basepath)
}

func NewWithOptions(basepath string, opts ...RfwOption) (*Rfw, error) {
	t := time.Now()
	path := generatePath(basepath, t)
	r, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return nil, err
	}
	rt := &Rfw{basepath: basepath, lastTime: t, outFile: r}
	for _, o := range opts {
		o(rt)
	}
	if err = rt.checkClearLogFile(t); err != nil {
		return nil, err
	}
	return rt, nil
}

func getOutdatedPath(basepath string, paths []string, now time.Time, remain int) []string {
	sort.Strings(paths)
	edgepath := generatePath(basepath, now.AddDate(0, 0, 0-remain))
	i := sort.SearchStrings(paths, edgepath)
	if i <= 0 {
		return []string{}
	} else if i > len(paths) {
		return paths
	} else {
		return paths[:i]
	}
}

func (w *Rfw) checkClearLogFile(now time.Time) error {
	if w.remainCntOfLogFile <= 0 {
		return nil
	}
	matches, err := filepath.Glob(w.basepath + "-*")
	if err != nil {
		return err
	}
	torms := getOutdatedPath(w.basepath, matches, now, w.remainCntOfLogFile)
	for _, p := range torms {
		os.Remove(p)
	}
	return nil
}

func (w *Rfw) Write(p []byte) (int, error) {
	w.lock.RLock()
	defer w.lock.RUnlock()
	if w.outFile == nil {
		return 0, errors.New(fmt.Sprintf("Rfw is closed. Basepath=%s", w.basepath))
	}
	t := time.Now()
	if t.YearDay() != w.lastTime.YearDay() || t.Year() != w.lastTime.Year() {
		needcheck := false
		w.lock.RUnlock()
		w.lock.Lock()
		if t.YearDay() != w.lastTime.YearDay() || t.Year() != w.lastTime.Year() {
			needcheck = true
			path := generatePath(w.basepath, t)
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
			if err != nil {
				w.lock.Unlock()
				w.lock.RLock()
				return 0, err
			}
			w.outFile.Close()
			w.outFile = f
			w.lastTime = t
		}
		w.lock.Unlock()
		w.lock.RLock()
		if needcheck {
			w.checkClearLogFile(t)
		}
	}
	return w.outFile.Write(p)
}

func (w *Rfw) Close() (err error) {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.outFile != nil {
		w.outFile.Close()
		w.outFile = nil
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"math/rand"
	"os"
	"path"
	"strconv"

	"github.com/MJKWoolnough/memio"
	"github.com/MJKWoolnough/minecraft"
	"github.com/MJKWoolnough/minewebgen/internal/data"
)

type RPC struct {
	c *Config
}

func (r RPC) Settings(_ struct{}, settings *data.ServerSettings) error {
	*settings = r.c.Settings()
	return nil
}

func (r RPC) SetSettings(settings data.ServerSettings, _ *struct{}) error {
	settings.DirMaps = path.Clean(settings.DirMaps)
	settings.DirServers = path.Clean(settings.DirServers)
	if settings.DirMaps == settings.DirServers {
		return errors.New("map and server paths cannot be the same")
	}
	r.c.SetSettings(settings)
	go r.c.Save()
	return nil
}

func (r RPC) ServerName(_ struct{}, serverName *string) error {
	*serverName = r.c.Settings().ServerName
	return nil
}

func (r RPC) ServerList(_ struct{}, list *[]data.Server) error {
	r.c.Servers.mu.RLock()
	defer r.c.Servers.mu.RUnlock()
	*list = make([]data.Server, len(r.c.Servers.List))
	for n, s := range r.c.Servers.List {
		(*list)[n] = *s
	}
	return nil
}

func (r RPC) MapList(_ struct{}, list *[]data.Map) error {
	r.c.Maps.mu.RLock()
	defer r.c.Maps.mu.RUnlock()
	*list = make([]data.Map, len(r.c.Maps.List))
	for n, m := range r.c.Maps.List {
		(*list)[n] = *m
	}
	return nil
}

func (r RPC) Server(id int, s *data.Server) error {
	ser := r.c.Server(id)
	ser.RLock()
	defer ser.RUnlock()
	*s = *ser
	return nil
}

func (r RPC) Map(id int, m *data.Map) error {
	mp := r.c.Map(id)
	mp.RLock()
	defer mp.RUnlock()
	*m = *mp
	return nil
}

func (r RPC) SetServer(s data.Server, _ *struct{}) error {
	ser := r.c.Server(s.ID)
	if ser == nil {
		return ErrUnknownServer
	}
	if ser.State != data.StateStopped {
		return ErrServerRunning
	}
	ser.Lock()
	defer ser.Unlock()
	ser.Name = s.Name
	ser.Args = s.Args
	go r.c.Save()
	return nil
}

func (r RPC) SetMap(m data.Map, _ *struct{}) error {
	mp := r.c.Map(m.ID)
	if mp == nil {
		return ErrUnknownMap
	}
	mp.RLock()
	sID := mp.Server
	mp.RUnlock()
	if mp.Server != -1 {
		ser := r.c.Server(mp.Server)
		if ser != nil {
			ser.RLock()
			s := ser.State
			ser.RUnlock()
			if s != data.StateStopped {
				return ErrServerRunning
			}
		}
	}
	mp.Lock()
	defer mp.Unlock()
	mp.Name = m.Name
	go r.c.Save()
	return nil
}

func (r RPC) SetServerMap(ids [2]int, _ *struct{}) error {
	if ids[0] != -1 {
		serv := r.c.Server(ids[0])
		if serv == nil {
			return ErrUnknownServer
		}
		serv.RLock()
		mID := serv.Map
		s := serv.State
		serv.RUnlock()
		if s != data.StateStopped {
			return ErrServerRunning
		}
		if mID == ids[1] {
			return nil
		}
		if mID != -1 {
			mp := r.c.Map(mID)
			if mp != nil {
				mp.Lock()
				mp.Server = -1
				mp.Unlock()
			}
		}
		serv.Lock()
		serv.Map = ids[1]
		serv.Unlock()
	}
	if ids[1] != -1 {
		mp := r.c.Map(ids[1])
		if mp == nil {
			return ErrUnknownMap
		}
		mp.RLock()
		sID := mp.Server
		mp.RUnlock()
		if sID != -1 {
			serv := r.c.Server(sID)
			if serv != nil {
				serv.RLock()
				s := serv.State
				serv.RUnlock()
				if s != data.StateStopped {
					return ErrServerRunning
				}
				serv.Lock()
				serv.Map = -1
				serv.Unlock()
			}
		}
		mp.Lock()
		mp.Server = ids[0]
		mp.Unlock()
	}
	go r.c.Save()
	return nil
}

func (r RPC) ServerProperties(id int, sp *ServerProperties) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Open(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	*sp = make(ServerProperties)
	return sp.ReadFrom(f)
}

func (r RPC) SetServerProperties(sp data.ServerProperties, _ *struct{}) error {
	s := r.c.Server(sp.ID)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Create(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) MapProperties(id int, mp *ServerProperties) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Open(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	*mp = make(ServerProperties)
	return mp.ReadFrom(f)
}

func (r RPC) SetMapProperties(sp data.ServerProperties, _ *struct{}) error {
	m := r.c.Map(sp.ID)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Create(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) RemoveServer(id int, _ *struct{}) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.Lock()
	defer s.Unlock()
	if s.State != data.StateStopped {
		return ErrServerRunning
	}
	if s.Map >= 0 {
		m := r.c.Map(s.Map)
		m.Lock()
		m.Server = -1
		m.Unlock()
	}
	s.ID = -1
	r.c.RemoveServer(id)
	go r.c.Save()
	return nil
}

func (r RPC) RemoveMap(id int, _ *struct{}) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.Lock()
	defer m.Unlock()
	if m.Server >= 0 {
		s := r.c.Server(m.Server)
		m.Lock()
		defer m.Unlock()
		if s.State != data.StateStopped {
			return ErrServerRunning
		}
		m.Server = -1
	}
	m.ID = -1
	r.c.RemoveMap(id)
	go r.c.Save()
	return nil
}

func (r RPC) CreateDefaultMap(data data.DefaultMap, _ *struct{}) error {
	return r.createMap(data, "")
}

func (r RPC) createMap(data data.DefaultMap, generatorSettings string) error {
	if data.Seed == 0 {
		data.Seed = rand.Int63()
	}
	m := r.c.NewMap()
	if m == nil {
		return errors.New("failed to create map")
	}
	m.Lock()
	defer m.Unlock()
	p, err := minecraft.NewFilePath(m.Path)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l, err := minecraft.NewLevel(p)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l.GameMode(data.GameMode)
	l.LevelName(data.Name)
	l.LevelName(data.Name)
	switch data.Mode {
	case 0:
		l.Generator(minecraft.DefaultGenerator)
	case 1:
		l.Generator(minecraft.FlatGenerator)
	case 2:
		l.Generator(minecraft.LargeBiomeGenerator)
	case 3:
		l.Generator(minecraft.AmplifiedGenerator)
	case 4:
		l.Generator(minecraft.CustomGenerator)
	}
	l.Seed(data.Seed)
	l.AllowCommands(data.Cheats)
	l.MapFeatures(data.Structures)
	if generatorSettings != "" {
		l.GeneratorOptions(generatorSettings)
	}
	l.Save()
	f, err := os.Create(path.Join(m.Path))
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	defer f.Close()
	ms := DefaultMapSettings()
	ms["gamemode"] = strconv.Itoa(int(data.GameMode))
	if !data.Structures {
		ms["generate-structures"] = "false"
	}
	if data.GameMode == 3 {
		ms["hardcore"] = "true"
	}
	if generatorSettings != "" {
		ms["generator-settings"] = generatorSettings
	}
	ms["level-seed"] = strconv.FormatInt(data.Seed, 10)
	ms["motd"] = data.Name
	switch data.Mode {
	case 0:
		ms["level-type"] = minecraft.DefaultGenerator
	case 1:
		ms["level-type"] = minecraft.FlatGenerator
	case 2:
		ms["level-type"] = minecraft.LargeBiomeGenerator
	case 3:
		ms["level-type"] = minecraft.AmplifiedGenerator
	case 4:
		ms["level-type"] = minecraft.CustomGenerator
	case 5:
		ms["level-type"] = minecraft.DebugGenerator
	}
	if err := ms.WriteTo(f); err != nil {
		return err
	}
	go r.c.Save()
	return nil
}

func (r RPC) CreateSuperflatMap(data data.SuperFlatMap, _ *struct{}) error {
	return r.createMap(data.DefaultMap, data.GeneratorSettings)
}

func (r RPC) CreateCustomMap(data data.CustomMap, _ *struct{}) error {
	// check settings for validity
	var buf []byte
	err := json.NewEncoder(memio.Create(&buf)).Encode(data.GeneratorSettings)
	if err != nil {
		return err
	}
	return r.createMap(data.DefaultMap, string(buf))
}

// Errors

var (
	ErrUnknownServer = errors.New("unknown server")
	ErrUnknownMap    = errors.New("unknown map")
	ErrServerRunning = errors.New("server running")
)
<commit_msg>Corrected same map check<commit_after>package main

import (
	"encoding/json"
	"errors"
	"math/rand"
	"os"
	"path"
	"strconv"

	"github.com/MJKWoolnough/memio"
	"github.com/MJKWoolnough/minecraft"
	"github.com/MJKWoolnough/minewebgen/internal/data"
)

type RPC struct {
	c *Config
}

func (r RPC) Settings(_ struct{}, settings *data.ServerSettings) error {
	*settings = r.c.Settings()
	return nil
}

func (r RPC) SetSettings(settings data.ServerSettings, _ *struct{}) error {
	settings.DirMaps = path.Clean(settings.DirMaps)
	settings.DirServers = path.Clean(settings.DirServers)
	if settings.DirMaps == settings.DirServers {
		return errors.New("map and server paths cannot be the same")
	}
	r.c.SetSettings(settings)
	go r.c.Save()
	return nil
}

func (r RPC) ServerName(_ struct{}, serverName *string) error {
	*serverName = r.c.Settings().ServerName
	return nil
}

func (r RPC) ServerList(_ struct{}, list *[]data.Server) error {
	r.c.Servers.mu.RLock()
	defer r.c.Servers.mu.RUnlock()
	*list = make([]data.Server, len(r.c.Servers.List))
	for n, s := range r.c.Servers.List {
		(*list)[n] = *s
	}
	return nil
}

func (r RPC) MapList(_ struct{}, list *[]data.Map) error {
	r.c.Maps.mu.RLock()
	defer r.c.Maps.mu.RUnlock()
	*list = make([]data.Map, len(r.c.Maps.List))
	for n, m := range r.c.Maps.List {
		(*list)[n] = *m
	}
	return nil
}

func (r RPC) Server(id int, s *data.Server) error {
	ser := r.c.Server(id)
	ser.RLock()
	defer ser.RUnlock()
	*s = *ser
	return nil
}

func (r RPC) Map(id int, m *data.Map) error {
	mp := r.c.Map(id)
	mp.RLock()
	defer mp.RUnlock()
	*m = *mp
	return nil
}

func (r RPC) SetServer(s data.Server, _ *struct{}) error {
	ser := r.c.Server(s.ID)
	if ser == nil {
		return ErrUnknownServer
	}
	if ser.State != data.StateStopped {
		return ErrServerRunning
	}
	ser.Lock()
	defer ser.Unlock()
	ser.Name = s.Name
	ser.Args = s.Args
	go r.c.Save()
	return nil
}

func (r RPC) SetMap(m data.Map, _ *struct{}) error {
	mp := r.c.Map(m.ID)
	if mp == nil {
		return ErrUnknownMap
	}
	mp.RLock()
	sID := mp.Server
	mp.RUnlock()
	if mp.Server != -1 {
		ser := r.c.Server(mp.Server)
		if ser != nil {
			ser.RLock()
			s := ser.State
			ser.RUnlock()
			if s != data.StateStopped {
				return ErrServerRunning
			}
		}
	}
	mp.Lock()
	defer mp.Unlock()
	mp.Name = m.Name
	go r.c.Save()
	return nil
}

func (r RPC) SetServerMap(ids [2]int, _ *struct{}) error {
	if ids[0] != -1 {
		serv := r.c.Server(ids[0])
		if serv == nil {
			return ErrUnknownServer
		}
		serv.RLock()
		mID := serv.Map
		s := serv.State
		serv.RUnlock()
		if s != data.StateStopped {
			return ErrServerRunning
		}
		if mID != -1 {
			if mID == ids[1] {
				return nil
			}
			mp := r.c.Map(mID)
			if mp != nil {
				mp.Lock()
				mp.Server = -1
				mp.Unlock()
			}
		}
		serv.Lock()
		serv.Map = ids[1]
		serv.Unlock()
	}
	if ids[1] != -1 {
		mp := r.c.Map(ids[1])
		if mp == nil {
			return ErrUnknownMap
		}
		mp.RLock()
		sID := mp.Server
		mp.RUnlock()
		if sID != -1 {
			serv := r.c.Server(sID)
			if serv != nil {
				serv.RLock()
				s := serv.State
				serv.RUnlock()
				if s != data.StateStopped {
					return ErrServerRunning
				}
				serv.Lock()
				serv.Map = -1
				serv.Unlock()
			}
		}
		mp.Lock()
		mp.Server = ids[0]
		mp.Unlock()
	}
	go r.c.Save()
	return nil
}

func (r RPC) ServerProperties(id int, sp *ServerProperties) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Open(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	*sp = make(ServerProperties)
	return sp.ReadFrom(f)
}

func (r RPC) SetServerProperties(sp data.ServerProperties, _ *struct{}) error {
	s := r.c.Server(sp.ID)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Create(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) MapProperties(id int, mp *ServerProperties) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Open(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	*mp = make(ServerProperties)
	return mp.ReadFrom(f)
}

func (r RPC) SetMapProperties(sp data.ServerProperties, _ *struct{}) error {
	m := r.c.Map(sp.ID)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Create(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) RemoveServer(id int, _ *struct{}) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.Lock()
	defer s.Unlock()
	if s.State != data.StateStopped {
		return ErrServerRunning
	}
	if s.Map >= 0 {
		m := r.c.Map(s.Map)
		m.Lock()
		m.Server = -1
		m.Unlock()
	}
	s.ID = -1
	r.c.RemoveServer(id)
	go r.c.Save()
	return nil
}

func (r RPC) RemoveMap(id int, _ *struct{}) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.Lock()
	defer m.Unlock()
	if m.Server >= 0 {
		s := r.c.Server(m.Server)
		m.Lock()
		defer m.Unlock()
		if s.State != data.StateStopped {
			return ErrServerRunning
		}
		m.Server = -1
	}
	m.ID = -1
	r.c.RemoveMap(id)
	go r.c.Save()
	return nil
}

func (r RPC) CreateDefaultMap(data data.DefaultMap, _ *struct{}) error {
	return r.createMap(data, "")
}

func (r RPC) createMap(data data.DefaultMap, generatorSettings string) error {
	if data.Seed == 0 {
		data.Seed = rand.Int63()
	}
	m := r.c.NewMap()
	if m == nil {
		return errors.New("failed to create map")
	}
	m.Lock()
	defer m.Unlock()
	p, err := minecraft.NewFilePath(m.Path)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l, err := minecraft.NewLevel(p)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l.GameMode(data.GameMode)
	l.LevelName(data.Name)
	l.LevelName(data.Name)
	switch data.Mode {
	case 0:
		l.Generator(minecraft.DefaultGenerator)
	case 1:
		l.Generator(minecraft.FlatGenerator)
	case 2:
		l.Generator(minecraft.LargeBiomeGenerator)
	case 3:
		l.Generator(minecraft.AmplifiedGenerator)
	case 4:
		l.Generator(minecraft.CustomGenerator)
	}
	l.Seed(data.Seed)
	l.AllowCommands(data.Cheats)
	l.MapFeatures(data.Structures)
	if generatorSettings != "" {
		l.GeneratorOptions(generatorSettings)
	}
	l.Save()
	f, err := os.Create(path.Join(m.Path))
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	defer f.Close()
	ms := DefaultMapSettings()
	ms["gamemode"] = strconv.Itoa(int(data.GameMode))
	if !data.Structures {
		ms["generate-structures"] = "false"
	}
	if data.GameMode == 3 {
		ms["hardcore"] = "true"
	}
	if generatorSettings != "" {
		ms["generator-settings"] = generatorSettings
	}
	ms["level-seed"] = strconv.FormatInt(data.Seed, 10)
	ms["motd"] = data.Name
	switch data.Mode {
	case 0:
		ms["level-type"] = minecraft.DefaultGenerator
	case 1:
		ms["level-type"] = minecraft.FlatGenerator
	case 2:
		ms["level-type"] = minecraft.LargeBiomeGenerator
	case 3:
		ms["level-type"] = minecraft.AmplifiedGenerator
	case 4:
		ms["level-type"] = minecraft.CustomGenerator
	case 5:
		ms["level-type"] = minecraft.DebugGenerator
	}
	if err := ms.WriteTo(f); err != nil {
		return err
	}
	go r.c.Save()
	return nil
}

func (r RPC) CreateSuperflatMap(data data.SuperFlatMap, _ *struct{}) error {
	return r.createMap(data.DefaultMap, data.GeneratorSettings)
}

func (r RPC) CreateCustomMap(data data.CustomMap, _ *struct{}) error {
	// check settings for validity
	var buf []byte
	err := json.NewEncoder(memio.Create(&buf)).Encode(data.GeneratorSettings)
	if err != nil {
		return err
	}
	return r.createMap(data.DefaultMap, string(buf))
}

// Errors

var (
	ErrUnknownServer = errors.New("unknown server")
	ErrUnknownMap    = errors.New("unknown map")
	ErrServerRunning = errors.New("server running")
)
<|endoftext|>


<commit_before>package immortal

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"sync/atomic"
	"syscall"
)

func (self *Daemon) stdHandler(p io.ReadCloser) {
	in := bufio.NewScanner(p)
	for in.Scan() {
		self.Log(in.Text())
	}
	p.Close()
}

func (self *Daemon) Run(ch chan<- error) {
	if atomic.SwapUint32(&self.count, uint32(1)) != 0 {
		log.Printf("PID: %d running", self.process.Pid)
		return
	}

	cmd := exec.Command(self.command[0], self.command[1:]...)

	// set environment vars
	env := os.Environ()
	if self.run.Env != nil {
		for k, v := range self.run.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
	}
	cmd.Env = env

	if self.run.Cwd != "" {
		cmd.Dir = self.run.Cwd
	}

	sysProcAttr := new(syscall.SysProcAttr)

	// set owner
	if self.owner != nil {
		uid, err := strconv.Atoi(self.owner.Uid)
		if err != nil {
			ch <- err
		}

		gid, err := strconv.Atoi(self.owner.Gid)
		if err != nil {
			ch <- err
		}

		//	https://golang.org/pkg/syscall/#SysProcAttr
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// Set process group ID to Pgid, or, if Pgid == 0, to new pid
	sysProcAttr.Setpgid = true
	sysProcAttr.Pgid = 0

	cmd.SysProcAttr = sysProcAttr

	var (
		r *io.PipeReader
		w *io.PipeWriter
	)
	if self.log {
		r, w = io.Pipe()
		cmd.Stdout = w
		cmd.Stderr = w
		go self.stdHandler(r)
	} else {
		cmd.Stdin = nil
		cmd.Stdout = nil
		cmd.Stderr = nil
	}

	go func() {
		// count_defer defaults to 0, 1 to run only once/down (don't restart)
		defer func() {
			atomic.StoreUint32(&self.count, self.count_defer)
		}()

		if self.log {
			defer w.Close()
		}

		if err := cmd.Start(); err != nil {
			ch <- err
		}

		self.process = cmd.Process

		// follow pid
		if self.run.FollowPid != "" {
			go self.watchPid(self.process.Pid, self.ctrl.state)
		}

		// write parent pid
		if self.run.ParentPid != "" {
			if err := WritePid(self.run.ParentPid, os.Getpid()); err != nil {
				log.Print(err)
			}
		}

		// write child pid
		if self.run.ChildPid != "" {
			if err := WritePid(self.run.ChildPid, self.process.Pid); err != nil {
				log.Print(err)
			}
		}

		ch <- cmd.Wait()
	}()
}
<commit_msg>	modified:   run.go<commit_after>package immortal

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"sync/atomic"
	"syscall"
)

func (self *Daemon) stdHandler(p io.ReadCloser) {
	in := bufio.NewScanner(p)
	for in.Scan() {
		self.Log(in.Text())
	}
	p.Close()
}

func (self *Daemon) Run(ch chan<- error) {
	if atomic.SwapUint32(&self.count, uint32(1)) != 0 {
		log.Printf("PID: %d running", self.process.Pid)
		return
	}

	cmd := exec.Command(self.command[0], self.command[1:]...)

	// change working directory
	if self.run.Cwd != "" {
		cmd.Dir = self.run.Cwd
	}

	// set environment vars
	env := os.Environ()
	if self.run.Env != nil {
		for k, v := range self.run.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
	}
	cmd.Env = env

	sysProcAttr := new(syscall.SysProcAttr)

	// set owner
	if self.owner != nil {
		uid, err := strconv.Atoi(self.owner.Uid)
		if err != nil {
			ch <- err
		}

		gid, err := strconv.Atoi(self.owner.Gid)
		if err != nil {
			ch <- err
		}

		//	https://golang.org/pkg/syscall/#SysProcAttr
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// Set process group ID to Pgid, or, if Pgid == 0, to new pid
	sysProcAttr.Setpgid = true
	sysProcAttr.Pgid = 0

	cmd.SysProcAttr = sysProcAttr

	var (
		r *io.PipeReader
		w *io.PipeWriter
	)
	if self.log {
		r, w = io.Pipe()
		cmd.Stdout = w
		cmd.Stderr = w
		go self.stdHandler(r)
	} else {
		cmd.Stdin = nil
		cmd.Stdout = nil
		cmd.Stderr = nil
	}

	go func() {
		// count_defer defaults to 0, 1 to run only once/down (don't restart)
		defer func() {
			atomic.StoreUint32(&self.count, self.count_defer)
		}()

		if self.log {
			defer w.Close()
		}

		if err := cmd.Start(); err != nil {
			ch <- err
		}

		self.process = cmd.Process

		// follow pid
		if self.run.FollowPid != "" {
			go self.watchPid(self.process.Pid, self.ctrl.state)
		}

		// write parent pid
		if self.run.ParentPid != "" {
			if err := WritePid(self.run.ParentPid, os.Getpid()); err != nil {
				log.Print(err)
			}
		}

		// write child pid
		if self.run.ChildPid != "" {
			if err := WritePid(self.run.ChildPid, self.process.Pid); err != nil {
				log.Print(err)
			}
		}

		ch <- cmd.Wait()
	}()
}
<|endoftext|>


<commit_before>package tools

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

type AppendStruct struct {
	file   *os.File
	c      chan os.Signal
	cClose chan bool
	fn     string
	err    error
    pid    int
}

func openFile(fn string) (*os.File, error) {
	return os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
}

func (as *AppendStruct) wait() {
	for {
		select {
		case <-as.c:
			io.WriteString(os.Stderr, "we got an signal, restart at "+TimeStr())
			f, err := openFile(as.fn)
			if err != nil {
				as.err = err
				log.Println("open file:", err)
				time.Sleep(time.Second)
				continue
			}
            if as.file != nil {
                as.file.Close()
            }
			as.file = f
		case <-as.cClose:
			signal.Reset(syscall.SIGUSR1)
			return
		}
	}
}

func NewAppender(fn string) (*AppendStruct, error) {
	var as AppendStruct
	f, err := openFile(fn)
	if err != nil {
		return nil, err
	}
	as.file = f
	as.fn = fn
	as.c = make(chan os.Signal)
	as.cClose = make(chan bool)
    as.pid = os.Getpid()
    signal.Notify(as.c, syscall.SIGUSR1)
	go as.wait()
	return &as, nil
}

func (as *AppendStruct) Close() {
	as.cClose <- true
	as.file.Close()
    as.file = nil
	close(as.c)
	close(as.cClose)
}

func (as *AppendStruct) Write(bt []byte) (int, error) {
	if as.err != nil {
		return 0, as.err
	}
	n, err := as.file.Write(bt)
	if err != nil {
		as.err = err
	}
	return n, err
}

func Daemon() {
	os.Stdout.Close()
	os.Stdin.Close()
	os.Stdout = nil
	os.Stdin = nil
}

func InitLog(fn, prefix string) *log.Logger {
	as, err := NewAppender(fn)
	if err != nil {
		return nil
	}
	if prefix == "" {
		prefix = "DefAppendLogger "
	}
	if prefix[len(prefix)-1] != ' ' {
		prefix = prefix + " "
	}
	return log.New(as, prefix, log.LstdFlags|log.Lshortfile)
}
<commit_msg>derace<commit_after>package tools

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

type AppendStruct struct {
	file   *os.File
	c      chan os.Signal
	cClose chan bool
	fn     string
	err    error
    pid    int
    lock   sync.Mutex
}

func openFile(fn string) (*os.File, error) {
	return os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
}

func (as *AppendStruct) wait() {
	for {
		select {
		case <-as.c:
			io.WriteString(os.Stderr, "we got an signal, restart at "+TimeStr())
			f, err := openFile(as.fn)
			if err != nil {
				as.err = err
				log.Println("open file:", err)
				time.Sleep(time.Second)
				continue
			}
            as.lock.Lock()
            if as.file != nil {
                as.file.Close()
            }
			as.file = f
            as.lock.Unlock()
		case <-as.cClose:
			signal.Reset(syscall.SIGUSR1)
			return
		}
	}
}

func NewAppender(fn string) (*AppendStruct, error) {
	var as AppendStruct
	f, err := openFile(fn)
	if err != nil {
		return nil, err
	}
	as.file = f
	as.fn = fn
	as.c = make(chan os.Signal)
	as.cClose = make(chan bool)
    as.pid = os.Getpid()
    signal.Notify(as.c, syscall.SIGUSR1)
	go as.wait()
	return &as, nil
}

func (as *AppendStruct) Close() {
    as.lock.Lock()
    defer as.lock.Unlock()
	as.cClose <- true
	as.file.Close()
    as.file = nil
	close(as.c)
	close(as.cClose)
}

func (as *AppendStruct) Write(bt []byte) (int, error) {
	if as.err != nil {
		return 0, as.err
	}
    as.lock.Lock()
    defer as.lock.Unlock()
	n, err := as.file.Write(bt)
	if err != nil {
		as.err = err
	}
	return n, err
}

func Daemon() {
	os.Stdout.Close()
	os.Stdin.Close()
	os.Stdout = nil
	os.Stdin = nil
}

func InitLog(fn, prefix string) *log.Logger {
	as, err := NewAppender(fn)
	if err != nil {
		return nil
	}
	if prefix == "" {
		prefix = "DefAppendLogger "
	}
	if prefix[len(prefix)-1] != ' ' {
		prefix = prefix + " "
	}
	return log.New(as, prefix, log.LstdFlags|log.Lshortfile)
}
<|endoftext|>


<commit_before>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

// Usage:
// assert := assert.Assert(t)
// ...
// assert.Equal(actual, expected)
// assert.Nil(err, "X threw error")
func Assert(t *testing.T) *Assertion {
	return &Assertion{t}
}

type Assertion struct {
	t *testing.T
}

func caller() string {
	fname, line, caller := "assert.go", 0, 1
	for ; strings.HasSuffix(fname, "assert.go") && caller < 4; caller++ {
		_, fname, line, _ = runtime.Caller(caller)
	}
	fname = fname[strings.LastIndex(fname, "/")+1:]
	return fmt.Sprintf("\n%v:%v ", fname, line)
}

func (a *Assertion) True(b bool, message string, messageParams ...interface{}) {
	if !b {
		a.t.Fatalf(caller()+message, messageParams...)
	}
}

func (a *Assertion) False(b bool, message string, messageParams ...interface{}) {
	a.True(!b, message, messageParams...)
}

func (a *Assertion) Nil(val interface{}) {
	isNil := val == nil || reflect.ValueOf(val).IsNil()
	a.True(isNil, fmt.Sprintf("Expected nil but was %v", val))
}

func (a *Assertion) NotNil(val interface{}) {
	isNil := val == nil || reflect.ValueOf(val).IsNil()
	a.True(!isNil, "Expected not nil but was nil")
}

func (a *Assertion) Equal(actual, expected interface{}) {
	eq := reflect.DeepEqual(actual, expected)
	a.True(eq, "\nExpected: %v\nReceived: %v", expected, actual)
}

func (a *Assertion) NotEqual(actual, expected interface{}) {
	eq := reflect.DeepEqual(actual, expected)
	a.True(!eq, "Expected %v to not equal %v, but it did", expected, actual)
}
<commit_msg>Add assert.Same for testing function pointers<commit_after>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

// Usage:
// assert := assert.Assert(t)
// ...
// assert.Equal(actual, expected)
// assert.Nil(err, "X threw error")
func Assert(t *testing.T) *Assertion {
	return &Assertion{t}
}

type Assertion struct {
	t *testing.T
}

func caller() string {
	fname, line, caller := "assert.go", 0, 1
	for ; strings.HasSuffix(fname, "assert.go") && caller < 4; caller++ {
		_, fname, line, _ = runtime.Caller(caller)
	}
	fname = fname[strings.LastIndex(fname, "/")+1:]
	return fmt.Sprintf("\n%v:%v ", fname, line)
}

func (a *Assertion) True(b bool, message string, messageParams ...interface{}) {
	if !b {
		a.t.Fatalf(caller()+message, messageParams...)
	}
}

func (a *Assertion) False(b bool, message string, messageParams ...interface{}) {
	a.True(!b, message, messageParams...)
}

func (a *Assertion) Nil(val interface{}) {
	isNil := val == nil || reflect.ValueOf(val).IsNil()
	a.True(isNil, fmt.Sprintf("Expected nil but was %v", val))
}

func (a *Assertion) NotNil(val interface{}) {
	isNil := val == nil || reflect.ValueOf(val).IsNil()
	a.True(!isNil, "Expected not nil but was nil")
}

func (a *Assertion) Same(actual, expected interface{}) {
	eq := reflect.ValueOf(actual).Pointer() == reflect.ValueOf(expected).Pointer()
	a.True(eq, "\nExpected: %v\nReceived: %v", expected, actual)
}

func (a *Assertion) Equal(actual, expected interface{}) {
	eq := reflect.DeepEqual(actual, expected)
	a.True(eq, "\nExpected: %v\nReceived: %v", expected, actual)
}

func (a *Assertion) NotEqual(actual, expected interface{}) {
	eq := reflect.DeepEqual(actual, expected)
	a.True(!eq, "Expected %v to not equal %v, but it did", expected, actual)
}
<|endoftext|>


<commit_before>// package assert provides convenience assert methods to complement
// the built in go testing library. It's intended to add onto standard
// Go tests. Example usage:
//	func TestSomething(t *testing.T) {
//		i, err := doSomething()
//    assert.NoErr(err)
//    assert.Equal(i, 123, "returned integer")
//  }
package assert

import (
	"fmt"
	"runtime"
	"testing"
	"reflect"
)

// callerStr returns a string representation of the code numFrames stack
// frames above the code that called callerStr
func callerStr(numFrames int) string {
	_, file, line, _ := runtime.Caller(1 + numFrames)
	return fmt.Sprintf("%s:%d", file, line)
}

// callerStrErrorf calls t.Errorf with fmtStr and vals in it, prefixed
// by a callerStr representation of the code numFrames above the caller of
// this function
func callerStrf(numFrames int, fmtStr string, vals ...interface{}) string {
	origStr := fmt.Sprintf(fmtStr, vals...)
	return fmt.Sprintf("%s: %s", callerStr(1+numFrames), origStr)
}

// True fails the test if b is false. on failure, it calls
// t.Errorf(fmtStr, vals...)
func True(t *testing.T, b bool, fmtStr string, vals ...interface{}) {
	if !b {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// False is the equivalent of True(t, !b, fmtStr, vals...).
func False(t *testing.T, b bool, fmtStr string, vals ...interface{}) {
	if b {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// Nil uses reflect.DeepEqual(i, nil) to determine if i is nil.
// if it's not, Nil calls t.Errorf(fmtStr, vals...)
func Nil(t *testing.T, i interface{}, fmtStr string, vals ...interface{}) {
	if !reflect.DeepEqual(i, nil) {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// NoErr calls t.Errorf if e is not nil.
func NoErr(t *testing.T, e error) {
	if e != nil {
		t.Errorf(callerStrf(1, "expected no error but got %s", e))
	}
}

// Err calls t.Errorf if expected is not equal to actual
func Err(t *testing.T, expected error, actual error) {
	if expected != actual {
		t.Errorf(callerStrf(1, "expected error %s but got %s", expected, actual))
	}
}

// Equal ensures that the actual value returned from a test was equal to an
// expected. it uses reflect.DeepEqual to do so.
// name is the name used to describe the values being compared.
// it's used in the error string if actual != expected.
func Equal(t *testing.T, actual, expected interface{}, noun string) {
	if !reflect.DeepEqual(actual, expected) {
		t.Errorf(callerStrf(1, "actual %s [%+v] != expected %s [%+v]", noun, actual, noun, expected))
	}
}
<commit_msg>fixing example doc<commit_after>// package assert provides convenience assert methods to complement
// the built in go testing library. It's intended to add onto standard
// Go tests. Example usage:
//	func TestSomething(t *testing.T) {
//		i, err := doSomething()
//		assert.NoErr(err)
//		assert.Equal(i, 123, "returned integer")
//	}
package assert

import (
	"fmt"
	"runtime"
	"testing"
	"reflect"
)

// callerStr returns a string representation of the code numFrames stack
// frames above the code that called callerStr
func callerStr(numFrames int) string {
	_, file, line, _ := runtime.Caller(1 + numFrames)
	return fmt.Sprintf("%s:%d", file, line)
}

// callerStrErrorf calls t.Errorf with fmtStr and vals in it, prefixed
// by a callerStr representation of the code numFrames above the caller of
// this function
func callerStrf(numFrames int, fmtStr string, vals ...interface{}) string {
	origStr := fmt.Sprintf(fmtStr, vals...)
	return fmt.Sprintf("%s: %s", callerStr(1+numFrames), origStr)
}

// True fails the test if b is false. on failure, it calls
// t.Errorf(fmtStr, vals...)
func True(t *testing.T, b bool, fmtStr string, vals ...interface{}) {
	if !b {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// False is the equivalent of True(t, !b, fmtStr, vals...).
func False(t *testing.T, b bool, fmtStr string, vals ...interface{}) {
	if b {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// Nil uses reflect.DeepEqual(i, nil) to determine if i is nil.
// if it's not, Nil calls t.Errorf(fmtStr, vals...)
func Nil(t *testing.T, i interface{}, fmtStr string, vals ...interface{}) {
	if !reflect.DeepEqual(i, nil) {
		t.Errorf(callerStrf(1, fmtStr, vals...))
	}
}

// NoErr calls t.Errorf if e is not nil.
func NoErr(t *testing.T, e error) {
	if e != nil {
		t.Errorf(callerStrf(1, "expected no error but got %s", e))
	}
}

// Err calls t.Errorf if expected is not equal to actual
func Err(t *testing.T, expected error, actual error) {
	if expected != actual {
		t.Errorf(callerStrf(1, "expected error %s but got %s", expected, actual))
	}
}

// Equal ensures that the actual value returned from a test was equal to an
// expected. it uses reflect.DeepEqual to do so.
// name is the name used to describe the values being compared.
// it's used in the error string if actual != expected.
func Equal(t *testing.T, actual, expected interface{}, noun string) {
	if !reflect.DeepEqual(actual, expected) {
		t.Errorf(callerStrf(1, "actual %s [%+v] != expected %s [%+v]", noun, actual, noun, expected))
	}
}
<|endoftext|>


<commit_before>package main

import "github.com/astaxie/beego"

const usernameCookieKey string = "username"

type BeeferController struct {
	beego.Controller
	CurrentUser *User // Current logged in user
}

// The Prepare() controller method runs before the real action methods
func (c *BeeferController) Prepare() {
	username := c.Ctx.GetCookie(usernameCookieKey)
	if username == "" {
		return
	}
	user := User{Username: username}
	c.CurrentUser = &user
	c.Data["CurrentUser"] = c.CurrentUser
}

type UserController struct {
	BeeferController
}

type User struct {
	Username string
}

// The Get method to handle the GET request
func (c *BeeferController) Get() {
	name := c.GetString(":user")
	user := User{Username: name}
	c.Data["User"] = user

	c.TplNames = "beefer.tpl"
}

func (c *UserController) Login() {
	c.TplNames = "user/login.tpl"
}

func (c *UserController) Signup() {
	c.TplNames = "user/signup.tpl"
	if c.Ctx.Request.Method == "POST" {
		username := c.GetString("username")
		password := c.GetString("password")
		password2 := c.GetString("password2")
		if password != password2 {
			c.Data["ValidateMessage"] = "两次密码不一致"
			return
		}
		user := User{Username: username}
		c.Data["User"] = user
		c.Ctx.SetCookie(usernameCookieKey, username)
		c.Redirect("/", 302)
	}

}

func (c *UserController) Logout() {
	c.CurrentUser = nil
	c.Data["CurrentUser"] = c.CurrentUser
	c.Ctx.SetCookie(usernameCookieKey, "")
	c.TplNames = "beefer.tpl"
}

func main() {
	beego.Router("/", &BeeferController{})
	beego.Router("/user/:user", &BeeferController{})
	beego.Router("/user/login", &UserController{}, "get:Login")
	beego.Router("/user/signup", &UserController{}, "get:Signup;post:Signup")
	beego.Router("/user/logout", &UserController{}, "get:Logout")
	beego.Run(":8085")
}
<commit_msg>04 session<commit_after>package main

import "github.com/astaxie/beego"

const currentUserSessionKey string = "currentUser"

type BeeferController struct {
	beego.Controller
	CurrentUser *User // Current logged in user
}

// The Prepare() controller method runs before the real action methods
func (c *BeeferController) Prepare() {
	s :=c.GetSession(currentUserSessionKey)
	if s != nil {
		c.CurrentUser = s.(*User)
		c.Data["CurrentUser"] = c.CurrentUser
	}
}

type UserController struct {
	BeeferController
}

type User struct {
	Username string
}

// The Get method to handle the GET request
func (c *BeeferController) Get() {
	name := c.GetString(":user")
	user := User{Username: name}
	c.Data["User"] = user

	c.TplNames = "beefer.tpl"
}

func (c *UserController) Login() {
	c.TplNames = "user/login.tpl"
}

func (c *UserController) Signup() {
	c.TplNames = "user/signup.tpl"
	if c.Ctx.Request.Method == "POST" {
		username := c.GetString("username")
		password := c.GetString("password")
		password2 := c.GetString("password2")
		if password != password2 {
			c.Data["ValidateMessage"] = "两次密码不一致"
			return
		}
		user := &User{Username: username}
		c.Data["User"] = user
		c.SetSession(currentUserSessionKey, user)
		c.Redirect("/", 302)
	}

}

func (c *UserController) Logout() {
	c.Data["CurrentUser"] = nil
	c.DelSession(currentUserSessionKey)
	c.TplNames = "beefer.tpl"
}

func main() {
	// Enable session. The default engine is memory
	beego.SessionOn = true
	beego.Router("/", &BeeferController{})
	beego.Router("/user/:user", &BeeferController{})
	beego.Router("/user/login", &UserController{}, "get:Login")
	beego.Router("/user/signup", &UserController{}, "get:Signup;post:Signup")
	beego.Router("/user/logout", &UserController{}, "get:Logout")
	beego.Run(":8085")
}
<|endoftext|>


<commit_before>package binder

import (
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

// A Binder translates between string parameters and Go data structures.
type ValueBinder func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool)

// The map of query string param name => struct attribute name
type StructAttributes map[string]string

var (
	// Lookup tables
	TypeBinders         = make(map[reflect.Type]ValueBinder)
	KindBinders         = make(map[reflect.Kind]ValueBinder)
	StructAttributesMap = make(map[reflect.Type]StructAttributes)

	// Binds an integer (signed)
	IntBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := values.Get(name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		intValue, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetInt(intValue)
		return pValue.Elem(), false
	}

	// Binds an integer (unsigned)
	UintBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := values.Get(name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		uintValue, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetUint(uintValue)
		return pValue.Elem(), false
	}

	// Binds a float
	FloatBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		floatValue, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetFloat(floatValue)
		return pValue.Elem(), false
	}

	// Binds a string
	StringBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := values.Get(name)
		return reflect.ValueOf(val), false
	}

	// Binds a boolean value, using the following formats :
	// "true" and "false"
	// "on" and "" (a checkbox)
	// "1" and "0" (why not)
	BoolBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := values.Get(name)
		v := strings.TrimSpace(strings.ToLower(val))
		switch v {
		case "yes", "true", "on", "1":
			return reflect.ValueOf(true), false
		}
		// Return false by default.
		return reflect.ValueOf(false), false
	}

	// Binds a comma separated list to a slice of strings.
	StringSliceBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := values.Get(name)
		if val == "" {
			return reflect.MakeSlice(typ, 0, 0), true
		}
		split := strings.Split(val, ",")
		result := reflect.MakeSlice(typ, len(split), len(split))
		for index, item := range split {
			values.Set(name, item)
			itemVal, _ := Bind(values, name, typ.Elem())
			result.Index(index).Set(itemVal)
		}
		return result, false
	}

	// Binds a set of parameters to a struct.
	StructBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		result := reflect.New(typ).Elem()
		for fieldName, attrName := range StructAttributesMap[typ] {
			fieldValue := result.FieldByName(fieldName)
			boundVal, _ := Bind(values, attrName, fieldValue.Type())
			fieldValue.Set(boundVal)
		}
		return result, false
	}

	// Binds a pointer. If nothing found, returns nil.
	PointerBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		pointerOf := typ.Elem()
		value, isNil := Bind(values, name, pointerOf)
		if isNil {
			return reflect.Zero(typ), true
		} else {
			return value.Addr(), false
		}

		return reflect.Zero(typ), true
	}
)

// Builds the lookup table
func init() {
	KindBinders[reflect.Int] = IntBinder
	KindBinders[reflect.Int8] = IntBinder
	KindBinders[reflect.Int16] = IntBinder
	KindBinders[reflect.Int32] = IntBinder
	KindBinders[reflect.Int64] = IntBinder

	KindBinders[reflect.Uint] = UintBinder
	KindBinders[reflect.Uint8] = UintBinder
	KindBinders[reflect.Uint16] = UintBinder
	KindBinders[reflect.Uint32] = UintBinder
	KindBinders[reflect.Uint64] = UintBinder

	KindBinders[reflect.Float32] = FloatBinder
	KindBinders[reflect.Float64] = FloatBinder

	KindBinders[reflect.String] = StringBinder
	KindBinders[reflect.Bool] = BoolBinder
	KindBinders[reflect.Slice] = StringSliceBinder
	KindBinders[reflect.Ptr] = PointerBinder
	KindBinders[reflect.Struct] = StructBinder
}

// Takes the name and type of the desired parameter and constructs it
// from query string values.
// Returns the zero value of the type upon any sort of failure.
func GetBoundValue(values url.Values, name string, typ reflect.Type) reflect.Value {
	ret, _ := Bind(values, name, typ)
	return ret
}

// Constructs a value of a specific type
func Bind(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
	if binder, found := TypeBinders[typ]; found {
		return binder(values, name, typ)
	} else {
		if binder, found := KindBinders[typ.Kind()]; found {
			return binder(values, name, typ)
		}
	}
	return reflect.Zero(typ), true
}

// Registers a custom binder for a specific type
func RegisterBinder(i interface{}, binder ValueBinder) {
	typ := reflect.ValueOf(i).Type()
	TypeBinders[typ] = binder
}

// Register the mapping of params to struct fields
func RegisterStructAttributes(typ reflect.Type) {
	StructAttributesMap[typ] = make(StructAttributes)
	for i := 0; i < typ.NumField(); i++ {
		f := typ.Field(i)
		if f.Type.Kind() == reflect.Struct {
			RegisterStructAttributes(f.Type)
			StructAttributesMap[typ][f.Name] = "*"
		} else {
			tag := f.Tag.Get("binder")
			if tag != "" {
				StructAttributesMap[typ][f.Name] = tag
			}
		}
	}
}

// Get a single value from url.Values (query string or pat url parameter)
func GetValue(values url.Values, name string) string {
	// pat url parameter
	if _, found := values[":"+name]; found {
		return values.Get(":" + name)
	}
	// query string
	return values.Get(name)
}
<commit_msg>binders: compatibility with pat<commit_after>package binder

import (
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

// A Binder translates between string parameters and Go data structures.
type ValueBinder func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool)

// The map of query string param name => struct attribute name
type StructAttributes map[string]string

var (
	// Lookup tables
	TypeBinders         = make(map[reflect.Type]ValueBinder)
	KindBinders         = make(map[reflect.Kind]ValueBinder)
	StructAttributesMap = make(map[reflect.Type]StructAttributes)

	// Binds an integer (signed)
	IntBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		intValue, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetInt(intValue)
		return pValue.Elem(), false
	}

	// Binds an integer (unsigned)
	UintBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		uintValue, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetUint(uintValue)
		return pValue.Elem(), false
	}

	// Binds a float
	FloatBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		if len(val) == 0 {
			return reflect.Zero(typ), true
		}
		floatValue, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return reflect.Zero(typ), true
		}
		pValue := reflect.New(typ)
		pValue.Elem().SetFloat(floatValue)
		return pValue.Elem(), false
	}

	// Binds a string
	StringBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		return reflect.ValueOf(val), false
	}

	// Binds a boolean value, using the following formats :
	// "true" and "false"
	// "on" and "" (a checkbox)
	// "1" and "0" (why not)
	BoolBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		v := strings.TrimSpace(strings.ToLower(val))
		switch v {
		case "yes", "true", "on", "1":
			return reflect.ValueOf(true), false
		}
		// Return false by default.
		return reflect.ValueOf(false), false
	}

	// Binds a comma separated list to a slice
	StringSliceBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		val := GetValue(values, name)
		if val == "" {
			return reflect.MakeSlice(typ, 0, 0), true
		}
		split := strings.Split(val, ",")
		result := reflect.MakeSlice(typ, len(split), len(split))
		for index, item := range split {
			values.Set(name, item)
			itemVal, _ := Bind(values, name, typ.Elem())
			result.Index(index).Set(itemVal)
		}
		return result, false
	}

	// Binds a set of parameters to a struct.
	StructBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		result := reflect.New(typ).Elem()
		for fieldName, attrName := range StructAttributesMap[typ] {
			fieldValue := result.FieldByName(fieldName)
			boundVal, _ := Bind(values, attrName, fieldValue.Type())
			fieldValue.Set(boundVal)
		}
		return result, false
	}

	// Binds a pointer. If nothing found, returns nil.
	PointerBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
		pointerOf := typ.Elem()
		value, isNil := Bind(values, name, pointerOf)
		if isNil {
			return reflect.Zero(typ), true
		} else {
			return value.Addr(), false
		}

		return reflect.Zero(typ), true
	}
)

// Builds the lookup table
func init() {
	KindBinders[reflect.Int] = IntBinder
	KindBinders[reflect.Int8] = IntBinder
	KindBinders[reflect.Int16] = IntBinder
	KindBinders[reflect.Int32] = IntBinder
	KindBinders[reflect.Int64] = IntBinder

	KindBinders[reflect.Uint] = UintBinder
	KindBinders[reflect.Uint8] = UintBinder
	KindBinders[reflect.Uint16] = UintBinder
	KindBinders[reflect.Uint32] = UintBinder
	KindBinders[reflect.Uint64] = UintBinder

	KindBinders[reflect.Float32] = FloatBinder
	KindBinders[reflect.Float64] = FloatBinder

	KindBinders[reflect.String] = StringBinder
	KindBinders[reflect.Bool] = BoolBinder
	KindBinders[reflect.Slice] = StringSliceBinder
	KindBinders[reflect.Ptr] = PointerBinder
	KindBinders[reflect.Struct] = StructBinder
}

// Takes the name and type of the desired parameter and constructs it
// from query string values.
// Returns the zero value of the type upon any sort of failure.
func GetBoundValue(values url.Values, name string, typ reflect.Type) reflect.Value {
	ret, _ := Bind(values, name, typ)
	return ret
}

// Constructs a value of a specific type
func Bind(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
	if binder, found := TypeBinders[typ]; found {
		return binder(values, name, typ)
	} else {
		if binder, found := KindBinders[typ.Kind()]; found {
			return binder(values, name, typ)
		}
	}
	return reflect.Zero(typ), true
}

// Registers a custom binder for a specific type
func RegisterBinder(i interface{}, binder ValueBinder) {
	typ := reflect.ValueOf(i).Type()
	TypeBinders[typ] = binder
}

// Register the mapping of params to struct fields
func RegisterStructAttributes(typ reflect.Type) {
	StructAttributesMap[typ] = make(StructAttributes)
	for i := 0; i < typ.NumField(); i++ {
		f := typ.Field(i)
		if f.Type.Kind() == reflect.Struct {
			RegisterStructAttributes(f.Type)
			StructAttributesMap[typ][f.Name] = "*"
		} else {
			tag := f.Tag.Get("binder")
			if tag != "" {
				StructAttributesMap[typ][f.Name] = tag
			}
		}
	}
}

// Get a single value from url.Values (query string or pat url parameter)
func GetValue(values url.Values, name string) string {
	// pat url parameter
	if _, found := values[":"+name]; found {
		return values.Get(":" + name)
	}
	// query string
	return values.Get(name)
}
<|endoftext|>