<commit_before>package acl

import (
	"fmt"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
	"github.com/lxc/lxd/shared/version"
)

// Define type for rule directions.
type ruleDirection string

const ruleDirectionIngress ruleDirection = "ingress"
const ruleDirectionEgress ruleDirection = "egress"

// common represents a Network ACL.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	projectName string
	info        *api.NetworkACL
}

// init initialise internal variables.
func (d *common) init(state *state.State, id int64, projectName string, info *api.NetworkACL) {
	if info == nil {
		d.info = &api.NetworkACL{}
	} else {
		d.info = info
	}

	d.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "networkACL": d.info.Name})
	d.id = id
	d.projectName = projectName
	d.state = state

	if d.info.Ingress == nil {
		d.info.Ingress = []api.NetworkACLRule{}
	}

	for i := range d.info.Ingress {
		d.info.Ingress[i].Normalise()
	}

	if d.info.Egress == nil {
		d.info.Egress = []api.NetworkACLRule{}
	}

	for i := range d.info.Egress {
		d.info.Egress[i].Normalise()
	}

	if d.info.Config == nil {
		d.info.Config = make(map[string]string)
	}
}

// ID returns the Network ACL ID.
func (d *common) ID() int64 {
	return d.id
}

// Name returns the project.
func (d *common) Project() string {
	return d.projectName
}

// Info returns copy of internal info for the Network ACL.
func (d *common) Info() *api.NetworkACL {
	// Copy internal info to prevent modification externally.
	info := api.NetworkACL{}
	info.Name = d.info.Name
	info.Description = d.info.Description
	info.Ingress = append(make([]api.NetworkACLRule, 0, len(d.info.Ingress)), d.info.Ingress...)
	info.Egress = append(make([]api.NetworkACLRule, 0, len(d.info.Egress)), d.info.Egress...)
	info.Config = util.CopyConfig(d.info.Config)
	info.UsedBy = nil // To indicate its not populated (use Usedby() function to populate).

	return &info
}

// usedBy returns a list of API endpoints referencing this ACL.
// If firstOnly is true then search stops at first result.
func (d *common) usedBy(firstOnly bool) ([]string, error) {
	usedBy := []string{}

	// Find all instance NICs that use this Network ACL.
	err := UsedBy(d.state, d.projectName, d.Info().Name, func(usageType string, projectName string, name string, config map[string]string) error {
		if usageType == "instance" {
			uri := fmt.Sprintf("/%s/instances/%s", version.APIVersion, name)
			if projectName != project.Default {
				uri += fmt.Sprintf("?project=%s", projectName)
			}

			usedBy = append(usedBy, uri)

			if firstOnly {
				return db.ErrInstanceListStop
			}
		} else {
			return fmt.Errorf("Urecognised usage type %q", usageType)
		}

		return nil
	})
	if err != nil && err != db.ErrInstanceListStop {
		return nil, errors.Wrapf(err, "Failed getting ACL usage")
	}

	return usedBy, nil
}

// UsedBy returns a list of API endpoints referencing this ACL.
func (d *common) UsedBy() ([]string, error) {
	return d.usedBy(false)
}

// isUsed returns whether or not the ACL is in use.
func (d *common) isUsed() (bool, error) {
	usedBy, err := d.usedBy(true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// Etag returns the values used for etag generation.
func (d *common) Etag() []interface{} {
	return []interface{}{d.info.Name, d.info.Description, d.info.Ingress, d.info.Egress, d.info.Config}
}

// validateName checks name is valid.
func (d *common) validateName(name string) error {
	if name == "" {
		return fmt.Errorf("Name is required")
	}

	// Don't allow ACL names to start with special port selector character to avoid allow LXD to define
	// special port selectors without risking conflict with user defined ACL names.
	if strings.HasPrefix(name, "#") {
		return fmt.Errorf("Name cannot start with reserved character `#`")
	}

	// Ensures we can differentiate an ACL name from an IP in rules that reference this ACL.
	err := shared.ValidHostname(name)
	if err != nil {
		return err
	}

	return nil
}

// validateConfig checks the config and rules are valid.
func (d *common) validateConfig(info *api.NetworkACLPut) error {
	for k := range info.Config {
		if !shared.IsUserConfig(k) {
			return fmt.Errorf("Only user config keys supported")
		}
	}

	// Normalise rules before validation for duplicate detection.
	for i := range info.Ingress {
		info.Ingress[i].Normalise()
	}

	for i := range info.Egress {
		info.Egress[i].Normalise()
	}

	// Validate each ingress rule.
	for i, ingressRule := range info.Ingress {
		err := d.validateRule(ruleDirectionIngress, ingressRule)
		if err != nil {
			return errors.Wrapf(err, "Invalid ingress rule %d", i)
		}

		// Check for duplicates.
		for ri, r := range info.Ingress {
			if ri == i {
				continue // Skip ourselves.
			}

			if r == ingressRule {
				return fmt.Errorf("Duplicate of ingress rule %d", i)
			}
		}
	}

	// Validate each egress rule.
	for i, egressRule := range info.Egress {
		err := d.validateRule(ruleDirectionEgress, egressRule)
		if err != nil {
			return errors.Wrapf(err, "Invalid egress rule %d", i)
		}

		// Check for duplicates.
		for ri, r := range info.Egress {
			if ri == i {
				continue // Skip ourselves.
			}

			if r == egressRule {
				return fmt.Errorf("Duplicate of egress rule %d", i)
			}
		}
	}

	return nil
}

// validateRule validates the rule supplied.
func (d *common) validateRule(direction ruleDirection, rule api.NetworkACLRule) error {
	// Validate Action field (required).
	validActions := []string{"allow", "drop"}
	if !shared.StringInSlice(rule.Action, validActions) {
		return fmt.Errorf("Action must be one of: %s", strings.Join(validActions, ", "))
	}

	// Validate State field (required).
	validStates := []string{"enabled", "disabled", "logged"}
	if !shared.StringInSlice(rule.State, validStates) {
		return fmt.Errorf("State must be one of: %s", strings.Join(validStates, ", "))
	}

	// Validate Source field.
	if rule.Source != "" {
		err := d.validateRuleSubjects(util.SplitNTrimSpace(rule.Source, ",", -1, false), nil)
		if err != nil {
			return errors.Wrapf(err, "Invalid Source")
		}
	}

	// Validate Destination field.
	if rule.Destination != "" {
		err := d.validateRuleSubjects(util.SplitNTrimSpace(rule.Destination, ",", -1, false), nil)
		if err != nil {
			return errors.Wrapf(err, "Invalid Destination")
		}
	}

	// Validate Protocol field.
	if rule.Protocol != "" {
		validProtocols := []string{"icmp4", "icmp6", "tcp", "udp"}
		if !shared.StringInSlice(rule.Protocol, validProtocols) {
			return fmt.Errorf("Protocol must be one of: %s", strings.Join(validProtocols, ", "))
		}
	}

	// Validate protocol dependent fields.
	if shared.StringInSlice(rule.Protocol, []string{"tcp", "udp"}) {
		if rule.ICMPType != "" {
			return fmt.Errorf("ICMP type cannot be used with protocol")
		}

		if rule.ICMPCode != "" {
			return fmt.Errorf("ICMP code cannot be used with protocol")
		}

		// Validate SourcePort field.
		if rule.SourcePort != "" {
			err := d.validatePorts(util.SplitNTrimSpace(rule.SourcePort, ",", -1, false))
			if err != nil {
				return errors.Wrapf(err, "Invalid Source port")
			}
		}

		// Validate DestinationPort field.
		if rule.DestinationPort != "" {
			err := d.validatePorts(util.SplitNTrimSpace(rule.DestinationPort, ",", -1, false))
			if err != nil {
				return errors.Wrapf(err, "Invalid Destination port")
			}
		}
	} else if shared.StringInSlice(rule.Protocol, []string{"icmp4", "icmp6"}) {
		if rule.SourcePort != "" {
			return fmt.Errorf("Source port cannot be used with protocol")
		}

		if rule.DestinationPort != "" {
			return fmt.Errorf("Destination port cannot be used with protocol")
		}

		// Validate ICMPType field.
		if rule.ICMPType != "" {
			err := validate.IsUint8(rule.ICMPType)
			if err != nil {
				return errors.Wrapf(err, "Invalid ICMP type")
			}
		}

		// Validate ICMPCode field.
		if rule.ICMPCode != "" {
			err := validate.IsUint8(rule.ICMPCode)
			if err != nil {
				return errors.Wrapf(err, "Invalid ICMP code")
			}
		}
	} else {
		if rule.ICMPType != "" {
			return fmt.Errorf("ICMP type cannot be used without specifying protocol")
		}

		if rule.ICMPCode != "" {
			return fmt.Errorf("ICMP code cannot be used without specifying protocol")
		}

		if rule.SourcePort != "" {
			return fmt.Errorf("Source port cannot be used without specifying protocol")
		}

		if rule.DestinationPort != "" {
			return fmt.Errorf("Destination port cannot be used without specifying protocol")
		}
	}

	return nil
}

// validateRuleSubjects checks that the source or destination subjects for a rule are valid.
// Accepts an allowedNames list of allowed ACL or special classifier names.
func (d *common) validateRuleSubjects(subjects []string, allowedNames []string) error {
	checks := []func(s string) error{
		validate.IsNetworkAddressCIDR,
		validate.IsNetworkRange,
	}

	validSubject := func(subject string, allowedNames []string) error {
		// Check if it is one of the network IP types.
		for _, c := range checks {
			err := c(subject)
			if err == nil {
				return nil // Found valid subject.

			}
		}

		// Check if it is one of the allowed names.
		for _, n := range allowedNames {
			if subject == n {
				return nil // Found valid subject.
			}
		}

		return fmt.Errorf("Invalid subject %q", subject)
	}

	for _, s := range subjects {
		err := validSubject(s, allowedNames)
		if err != nil {
			return err
		}
	}

	return nil
}

// validatePorts checks that the source or destination ports for a rule are valid.
func (d *common) validatePorts(ports []string) error {
	checks := []func(s string) error{
		validate.IsNetworkPort,
		validate.IsNetworkPortRange,
	}

	validPort := func(port string) error {
		// Check if it is one of the network port types.
		for _, c := range checks {
			err := c(port)
			if err == nil {
				return nil // Found valid port.

			}
		}

		return fmt.Errorf("Invalid port %q", port)
	}

	for _, port := range ports {
		err := validPort(port)
		if err != nil {
			return err
		}
	}

	return nil
}

// Update applies the supplied config to the ACL.
func (d *common) Update(config *api.NetworkACLPut) error {
	err := d.validateConfig(config)
	if err != nil {
		return err
	}

	err = d.state.Cluster.UpdateNetworkACL(d.id, config)
	if err != nil {
		return err
	}

	// Apply changes internally and reinitialise.
	d.info.NetworkACLPut = *config
	d.init(d.state, d.id, d.projectName, d.info)

	return nil
}

// Rename renames the ACL if not in use.
func (d *common) Rename(newName string) error {
	_, err := LoadByName(d.state, d.projectName, newName)
	if err == nil {
		return fmt.Errorf("An ACL by that name exists already")
	}

	isUsed, err := d.isUsed()
	if err != nil {
		return err
	}

	if isUsed {
		return fmt.Errorf("Cannot rename an ACL that is in use")
	}

	err = d.validateName(newName)
	if err != nil {
		return err
	}

	err = d.state.Cluster.RenameNetworkACL(d.id, newName)
	if err != nil {
		return err
	}

	// Apply changes internally.
	d.info.Name = newName

	return nil
}

// Delete deletes the ACL.
func (d *common) Delete() error {
	isUsed, err := d.isUsed()
	if err != nil {
		return err
	}

	if isUsed {
		return fmt.Errorf("Cannot delete an ACL that is in use")
	}

	return d.state.Cluster.DeleteNetworkACL(d.id)
}
<commit_msg>lxd/network/acl/driver/common: Updates usedBy to use updated UsedBy helper function<commit_after>package acl

import (
	"fmt"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
	"github.com/lxc/lxd/shared/version"
)

// Define type for rule directions.
type ruleDirection string

const ruleDirectionIngress ruleDirection = "ingress"
const ruleDirectionEgress ruleDirection = "egress"

// common represents a Network ACL.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	projectName string
	info        *api.NetworkACL
}

// init initialise internal variables.
func (d *common) init(state *state.State, id int64, projectName string, info *api.NetworkACL) {
	if info == nil {
		d.info = &api.NetworkACL{}
	} else {
		d.info = info
	}

	d.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "networkACL": d.info.Name})
	d.id = id
	d.projectName = projectName
	d.state = state

	if d.info.Ingress == nil {
		d.info.Ingress = []api.NetworkACLRule{}
	}

	for i := range d.info.Ingress {
		d.info.Ingress[i].Normalise()
	}

	if d.info.Egress == nil {
		d.info.Egress = []api.NetworkACLRule{}
	}

	for i := range d.info.Egress {
		d.info.Egress[i].Normalise()
	}

	if d.info.Config == nil {
		d.info.Config = make(map[string]string)
	}
}

// ID returns the Network ACL ID.
func (d *common) ID() int64 {
	return d.id
}

// Name returns the project.
func (d *common) Project() string {
	return d.projectName
}

// Info returns copy of internal info for the Network ACL.
func (d *common) Info() *api.NetworkACL {
	// Copy internal info to prevent modification externally.
	info := api.NetworkACL{}
	info.Name = d.info.Name
	info.Description = d.info.Description
	info.Ingress = append(make([]api.NetworkACLRule, 0, len(d.info.Ingress)), d.info.Ingress...)
	info.Egress = append(make([]api.NetworkACLRule, 0, len(d.info.Egress)), d.info.Egress...)
	info.Config = util.CopyConfig(d.info.Config)
	info.UsedBy = nil // To indicate its not populated (use Usedby() function to populate).

	return &info
}

// usedBy returns a list of API endpoints referencing this ACL.
// If firstOnly is true then search stops at first result.
func (d *common) usedBy(firstOnly bool) ([]string, error) {
	usedBy := []string{}

	// Find all networks, profiles and instance NICs that use this Network ACL.
	err := UsedBy(d.state, d.projectName, func(_ []string, usageType interface{}, _ string, _ map[string]string) error {
		switch u := usageType.(type) {
		case db.Instance:
			uri := fmt.Sprintf("/%s/instances/%s", version.APIVersion, u.Name)
			if u.Project != project.Default {
				uri += fmt.Sprintf("?project=%s", u.Project)
			}

			usedBy = append(usedBy, uri)
		case *api.Network:
			uri := fmt.Sprintf("/%s/networks/%s", version.APIVersion, u.Name)
			if d.projectName != project.Default {
				uri += fmt.Sprintf("?project=%s", d.projectName)
			}

			usedBy = append(usedBy, uri)
		case db.Profile:
			uri := fmt.Sprintf("/%s/profiles/%s", version.APIVersion, u.Name)
			if u.Project != project.Default {
				uri += fmt.Sprintf("?project=%s", u.Project)
			}

			usedBy = append(usedBy, uri)
		case *api.NetworkACL:
			uri := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, u.Name)
			if d.projectName != project.Default {
				uri += fmt.Sprintf("?project=%s", d.projectName)
			}

			usedBy = append(usedBy, uri)
		default:
			return fmt.Errorf("Unrecognised usage type %T", u)
		}

		if firstOnly {
			return db.ErrInstanceListStop
		}

		return nil
	}, d.Info().Name)
	if err != nil {
		if err == db.ErrInstanceListStop {
			return usedBy, nil
		}

		return nil, errors.Wrapf(err, "Failed getting ACL usage")
	}

	return usedBy, nil
}

// UsedBy returns a list of API endpoints referencing this ACL.
func (d *common) UsedBy() ([]string, error) {
	return d.usedBy(false)
}

// isUsed returns whether or not the ACL is in use.
func (d *common) isUsed() (bool, error) {
	usedBy, err := d.usedBy(true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// Etag returns the values used for etag generation.
func (d *common) Etag() []interface{} {
	return []interface{}{d.info.Name, d.info.Description, d.info.Ingress, d.info.Egress, d.info.Config}
}

// validateName checks name is valid.
func (d *common) validateName(name string) error {
	if name == "" {
		return fmt.Errorf("Name is required")
	}

	// Don't allow ACL names to start with special port selector character to avoid allow LXD to define
	// special port selectors without risking conflict with user defined ACL names.
	if strings.HasPrefix(name, "#") {
		return fmt.Errorf("Name cannot start with reserved character `#`")
	}

	// Ensures we can differentiate an ACL name from an IP in rules that reference this ACL.
	err := shared.ValidHostname(name)
	if err != nil {
		return err
	}

	return nil
}

// validateConfig checks the config and rules are valid.
func (d *common) validateConfig(info *api.NetworkACLPut) error {
	for k := range info.Config {
		if !shared.IsUserConfig(k) {
			return fmt.Errorf("Only user config keys supported")
		}
	}

	// Normalise rules before validation for duplicate detection.
	for i := range info.Ingress {
		info.Ingress[i].Normalise()
	}

	for i := range info.Egress {
		info.Egress[i].Normalise()
	}

	// Validate each ingress rule.
	for i, ingressRule := range info.Ingress {
		err := d.validateRule(ruleDirectionIngress, ingressRule)
		if err != nil {
			return errors.Wrapf(err, "Invalid ingress rule %d", i)
		}

		// Check for duplicates.
		for ri, r := range info.Ingress {
			if ri == i {
				continue // Skip ourselves.
			}

			if r == ingressRule {
				return fmt.Errorf("Duplicate of ingress rule %d", i)
			}
		}
	}

	// Validate each egress rule.
	for i, egressRule := range info.Egress {
		err := d.validateRule(ruleDirectionEgress, egressRule)
		if err != nil {
			return errors.Wrapf(err, "Invalid egress rule %d", i)
		}

		// Check for duplicates.
		for ri, r := range info.Egress {
			if ri == i {
				continue // Skip ourselves.
			}

			if r == egressRule {
				return fmt.Errorf("Duplicate of egress rule %d", i)
			}
		}
	}

	return nil
}

// validateRule validates the rule supplied.
func (d *common) validateRule(direction ruleDirection, rule api.NetworkACLRule) error {
	// Validate Action field (required).
	validActions := []string{"allow", "drop"}
	if !shared.StringInSlice(rule.Action, validActions) {
		return fmt.Errorf("Action must be one of: %s", strings.Join(validActions, ", "))
	}

	// Validate State field (required).
	validStates := []string{"enabled", "disabled", "logged"}
	if !shared.StringInSlice(rule.State, validStates) {
		return fmt.Errorf("State must be one of: %s", strings.Join(validStates, ", "))
	}

	// Validate Source field.
	if rule.Source != "" {
		err := d.validateRuleSubjects(util.SplitNTrimSpace(rule.Source, ",", -1, false), nil)
		if err != nil {
			return errors.Wrapf(err, "Invalid Source")
		}
	}

	// Validate Destination field.
	if rule.Destination != "" {
		err := d.validateRuleSubjects(util.SplitNTrimSpace(rule.Destination, ",", -1, false), nil)
		if err != nil {
			return errors.Wrapf(err, "Invalid Destination")
		}
	}

	// Validate Protocol field.
	if rule.Protocol != "" {
		validProtocols := []string{"icmp4", "icmp6", "tcp", "udp"}
		if !shared.StringInSlice(rule.Protocol, validProtocols) {
			return fmt.Errorf("Protocol must be one of: %s", strings.Join(validProtocols, ", "))
		}
	}

	// Validate protocol dependent fields.
	if shared.StringInSlice(rule.Protocol, []string{"tcp", "udp"}) {
		if rule.ICMPType != "" {
			return fmt.Errorf("ICMP type cannot be used with protocol")
		}

		if rule.ICMPCode != "" {
			return fmt.Errorf("ICMP code cannot be used with protocol")
		}

		// Validate SourcePort field.
		if rule.SourcePort != "" {
			err := d.validatePorts(util.SplitNTrimSpace(rule.SourcePort, ",", -1, false))
			if err != nil {
				return errors.Wrapf(err, "Invalid Source port")
			}
		}

		// Validate DestinationPort field.
		if rule.DestinationPort != "" {
			err := d.validatePorts(util.SplitNTrimSpace(rule.DestinationPort, ",", -1, false))
			if err != nil {
				return errors.Wrapf(err, "Invalid Destination port")
			}
		}
	} else if shared.StringInSlice(rule.Protocol, []string{"icmp4", "icmp6"}) {
		if rule.SourcePort != "" {
			return fmt.Errorf("Source port cannot be used with protocol")
		}

		if rule.DestinationPort != "" {
			return fmt.Errorf("Destination port cannot be used with protocol")
		}

		// Validate ICMPType field.
		if rule.ICMPType != "" {
			err := validate.IsUint8(rule.ICMPType)
			if err != nil {
				return errors.Wrapf(err, "Invalid ICMP type")
			}
		}

		// Validate ICMPCode field.
		if rule.ICMPCode != "" {
			err := validate.IsUint8(rule.ICMPCode)
			if err != nil {
				return errors.Wrapf(err, "Invalid ICMP code")
			}
		}
	} else {
		if rule.ICMPType != "" {
			return fmt.Errorf("ICMP type cannot be used without specifying protocol")
		}

		if rule.ICMPCode != "" {
			return fmt.Errorf("ICMP code cannot be used without specifying protocol")
		}

		if rule.SourcePort != "" {
			return fmt.Errorf("Source port cannot be used without specifying protocol")
		}

		if rule.DestinationPort != "" {
			return fmt.Errorf("Destination port cannot be used without specifying protocol")
		}
	}

	return nil
}

// validateRuleSubjects checks that the source or destination subjects for a rule are valid.
// Accepts an allowedNames list of allowed ACL or special classifier names.
func (d *common) validateRuleSubjects(subjects []string, allowedNames []string) error {
	checks := []func(s string) error{
		validate.IsNetworkAddressCIDR,
		validate.IsNetworkRange,
	}

	validSubject := func(subject string, allowedNames []string) error {
		// Check if it is one of the network IP types.
		for _, c := range checks {
			err := c(subject)
			if err == nil {
				return nil // Found valid subject.

			}
		}

		// Check if it is one of the allowed names.
		for _, n := range allowedNames {
			if subject == n {
				return nil // Found valid subject.
			}
		}

		return fmt.Errorf("Invalid subject %q", subject)
	}

	for _, s := range subjects {
		err := validSubject(s, allowedNames)
		if err != nil {
			return err
		}
	}

	return nil
}

// validatePorts checks that the source or destination ports for a rule are valid.
func (d *common) validatePorts(ports []string) error {
	checks := []func(s string) error{
		validate.IsNetworkPort,
		validate.IsNetworkPortRange,
	}

	validPort := func(port string) error {
		// Check if it is one of the network port types.
		for _, c := range checks {
			err := c(port)
			if err == nil {
				return nil // Found valid port.

			}
		}

		return fmt.Errorf("Invalid port %q", port)
	}

	for _, port := range ports {
		err := validPort(port)
		if err != nil {
			return err
		}
	}

	return nil
}

// Update applies the supplied config to the ACL.
func (d *common) Update(config *api.NetworkACLPut) error {
	err := d.validateConfig(config)
	if err != nil {
		return err
	}

	err = d.state.Cluster.UpdateNetworkACL(d.id, config)
	if err != nil {
		return err
	}

	// Apply changes internally and reinitialise.
	d.info.NetworkACLPut = *config
	d.init(d.state, d.id, d.projectName, d.info)

	return nil
}

// Rename renames the ACL if not in use.
func (d *common) Rename(newName string) error {
	_, err := LoadByName(d.state, d.projectName, newName)
	if err == nil {
		return fmt.Errorf("An ACL by that name exists already")
	}

	isUsed, err := d.isUsed()
	if err != nil {
		return err
	}

	if isUsed {
		return fmt.Errorf("Cannot rename an ACL that is in use")
	}

	err = d.validateName(newName)
	if err != nil {
		return err
	}

	err = d.state.Cluster.RenameNetworkACL(d.id, newName)
	if err != nil {
		return err
	}

	// Apply changes internally.
	d.info.Name = newName

	return nil
}

// Delete deletes the ACL.
func (d *common) Delete() error {
	isUsed, err := d.isUsed()
	if err != nil {
		return err
	}

	if isUsed {
		return fmt.Errorf("Cannot delete an ACL that is in use")
	}

	return d.state.Cluster.DeleteNetworkACL(d.id)
}
<|endoftext|>


<commit_before>// Copyright 2012 Thomas Jager <mail@jager.no> All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Driver for ADAM-4000 series I/O Modules from Advantech

package adam4000

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
)

func NewADAM4000(addr byte, rc *bufio.Reader, wc *bufio.Writer) *ADAM4000 {
	var a ADAM4000
	a.address = addr
	a.rc = rc
	a.wc = wc
	a.Value = make([]float64, 8)
	a.Retries = 3
	go a.startReader()
	return &a
}

func (a *ADAM4000) GetName() (string, error) {
	resp, err := a.comResF("$%02XM\r", a.address)
	if err != nil {
		return "", err
	}
	a.Name = strings.Trim(string(resp[3:]), "\r ")
	return a.Name, nil
}

func (a *ADAM4000) GetAllValue() ([]float64, error) {
	resp, err := a.comResF("#%02X\r", a.address)
	if err != nil {
		return nil, err
	}
	values := string(resp[1:])
	fmt.Printf("%d\n", len(values))
	if len(values) == 57 {
		a.Value[0], err = strconv.ParseFloat(values[0:7], 64)
		a.Value[1], err = strconv.ParseFloat(values[7:14], 64)
		a.Value[2], err = strconv.ParseFloat(values[14:21], 64)
		a.Value[3], err = strconv.ParseFloat(values[21:28], 64)
		a.Value[4], err = strconv.ParseFloat(values[28:35], 64)
		a.Value[5], err = strconv.ParseFloat(values[35:42], 64)
		a.Value[6], err = strconv.ParseFloat(values[42:49], 64)
		a.Value[7], err = strconv.ParseFloat(values[49:56], 64)
	} else {
		intvals := make([]int64, 8)
		intvals[0], err = strconv.ParseInt(values[0:4], 16, 64)
		intvals[1], err = strconv.ParseInt(values[4:8], 16, 64)
		intvals[2], err = strconv.ParseInt(values[8:12], 16, 64)
		intvals[3], err = strconv.ParseInt(values[12:16], 16, 64)
		intvals[4], err = strconv.ParseInt(values[16:20], 16, 64)
		intvals[5], err = strconv.ParseInt(values[20:24], 16, 64)
		intvals[6], err = strconv.ParseInt(values[24:28], 16, 64)
		intvals[7], err = strconv.ParseInt(values[28:32], 16, 64)
		a.Value[0] = float64(intvals[0])
		a.Value[1] = float64(intvals[1])
		a.Value[2] = float64(intvals[2])
		a.Value[3] = float64(intvals[3])
		a.Value[4] = float64(intvals[4])
		a.Value[5] = float64(intvals[5])
		a.Value[6] = float64(intvals[6])
		a.Value[7] = float64(intvals[7])
	}
	return a.Value, err
}

func (a *ADAM4000) GetChannelValue(n int) (float64, error) {
	resp, err := a.comResF("#%02X%d\r", a.address, n)
	if err != nil {
		return float64(0), err
	}
	values := string(resp[1:])
	if len(values) == 7 {
		a.Value[n], err = strconv.ParseFloat(values[0:7], 64)
	} else {
		intval, _ := strconv.ParseInt(values[0:4], 16, 64)
		a.Value[n] = float64(intval)
	}
	return a.Value[n], err
}

func (a *ADAM4000) GetVersion() (string, error) {
	resp, err := a.comResF("$%02XF\r", a.address)
	if err != nil {
		return "", err
	}
	a.Version = strings.Trim(string(resp[3:]), "\r ")
	return a.Version, nil
}

func (a *ADAM4000) SetChannelRange(channel int, rangec InputRangeCode) error {
	_, err := a.comResF("$%02X7C%dR%02X\r", a.address, channel, byte(rangec))
	if err != nil {
		return err
	}
	return nil
}

func (a *ADAM4000) GetChannelRange(channel int) (InputRangeCode, error) {
	resp, err := a.comResF("$%02X8C%d\r", a.address, channel)
	if err != nil {
		return 0, err
	}
	rangec := make([]byte, 1)
	hex.Decode(rangec, resp[6:8])
	return InputRangeCode(rangec[0]), nil
}

func (a *ADAM4000) SyncronizeRead() error {
	//Stub
	return nil
}

func (a *ADAM4000) SyncronizedValue() ([]float64, error) {
	//Stub
	return nil, nil
}

func (a *ADAM4000) GetConfig() error {
	resp, err := a.comResF("$%02X2\r", a.address)
	if err != nil {
		return err
	}

	addr := make([]byte, 1)
	typecode := make([]byte, 1)
	baud := make([]byte, 1)
	data := make([]byte, 1)

	hex.Decode(addr, resp[1:3])
	hex.Decode(typecode, resp[3:5])
	hex.Decode(baud, resp[5:7])
	hex.Decode(data, resp[7:9])

	a.Address = addr[0]
	a.InputRange = InputRangeCode(typecode[0])
	a.BaudRate = BaudRateCode(baud[0])
	fmt.Printf("%X\n", data)
	a.Integration_time = data[0]&byte(1<<7) > 0
	a.Checksum = data[0]&byte(1<<6) > 0
	a.DataFormat = DataFormatCode(data[0] & byte(2))
	if a.Address != a.address {
		fmt.Printf("Warning: Configured address (%d) differs from connected address (%d), in init mode?\n", a.Address, a.address)
	}
	return nil
}

func (a *ADAM4000) SetConfig() error {
	data := byte(a.DataFormat)
	if a.Integration_time {
		data |= byte(1 << 7)
	}
	if a.Checksum {
		data |= byte(1 << 6)
	}

	_, err := a.comResF("%%%02X%02XFF%02X%02X\r", a.address, a.Address, byte(a.BaudRate), data)
	return err
}
<commit_msg>Add input range to setconfig<commit_after>// Copyright 2012 Thomas Jager <mail@jager.no> All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Driver for ADAM-4000 series I/O Modules from Advantech

package adam4000

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
)

func NewADAM4000(addr byte, rc *bufio.Reader, wc *bufio.Writer) *ADAM4000 {
	var a ADAM4000
	a.address = addr
	a.rc = rc
	a.wc = wc
	a.Value = make([]float64, 8)
	a.Retries = 3
	go a.startReader()
	return &a
}

func (a *ADAM4000) GetName() (string, error) {
	resp, err := a.comResF("$%02XM\r", a.address)
	if err != nil {
		return "", err
	}
	a.Name = strings.Trim(string(resp[3:]), "\r ")
	return a.Name, nil
}

func (a *ADAM4000) GetAllValue() ([]float64, error) {
	resp, err := a.comResF("#%02X\r", a.address)
	if err != nil {
		return nil, err
	}
	values := string(resp[1:])
	fmt.Printf("%d\n", len(values))
	if len(values) == 57 {
		a.Value[0], err = strconv.ParseFloat(values[0:7], 64)
		a.Value[1], err = strconv.ParseFloat(values[7:14], 64)
		a.Value[2], err = strconv.ParseFloat(values[14:21], 64)
		a.Value[3], err = strconv.ParseFloat(values[21:28], 64)
		a.Value[4], err = strconv.ParseFloat(values[28:35], 64)
		a.Value[5], err = strconv.ParseFloat(values[35:42], 64)
		a.Value[6], err = strconv.ParseFloat(values[42:49], 64)
		a.Value[7], err = strconv.ParseFloat(values[49:56], 64)
	} else {
		intvals := make([]int64, 8)
		intvals[0], err = strconv.ParseInt(values[0:4], 16, 64)
		intvals[1], err = strconv.ParseInt(values[4:8], 16, 64)
		intvals[2], err = strconv.ParseInt(values[8:12], 16, 64)
		intvals[3], err = strconv.ParseInt(values[12:16], 16, 64)
		intvals[4], err = strconv.ParseInt(values[16:20], 16, 64)
		intvals[5], err = strconv.ParseInt(values[20:24], 16, 64)
		intvals[6], err = strconv.ParseInt(values[24:28], 16, 64)
		intvals[7], err = strconv.ParseInt(values[28:32], 16, 64)
		a.Value[0] = float64(intvals[0])
		a.Value[1] = float64(intvals[1])
		a.Value[2] = float64(intvals[2])
		a.Value[3] = float64(intvals[3])
		a.Value[4] = float64(intvals[4])
		a.Value[5] = float64(intvals[5])
		a.Value[6] = float64(intvals[6])
		a.Value[7] = float64(intvals[7])
	}
	return a.Value, err
}

func (a *ADAM4000) GetChannelValue(n int) (float64, error) {
	resp, err := a.comResF("#%02X%d\r", a.address, n)
	if err != nil {
		return float64(0), err
	}
	values := string(resp[1:])
	if len(values) == 7 {
		a.Value[n], err = strconv.ParseFloat(values[0:7], 64)
	} else {
		intval, _ := strconv.ParseInt(values[0:4], 16, 64)
		a.Value[n] = float64(intval)
	}
	return a.Value[n], err
}

func (a *ADAM4000) GetVersion() (string, error) {
	resp, err := a.comResF("$%02XF\r", a.address)
	if err != nil {
		return "", err
	}
	a.Version = strings.Trim(string(resp[3:]), "\r ")
	return a.Version, nil
}

func (a *ADAM4000) SetChannelRange(channel int, rangec InputRangeCode) error {
	_, err := a.comResF("$%02X7C%dR%02X\r", a.address, channel, byte(rangec))
	if err != nil {
		return err
	}
	return nil
}

func (a *ADAM4000) GetChannelRange(channel int) (InputRangeCode, error) {
	resp, err := a.comResF("$%02X8C%d\r", a.address, channel)
	if err != nil {
		return 0, err
	}
	rangec := make([]byte, 1)
	hex.Decode(rangec, resp[6:8])
	return InputRangeCode(rangec[0]), nil
}

func (a *ADAM4000) SyncronizeRead() error {
	//Stub
	return nil
}

func (a *ADAM4000) SyncronizedValue() ([]float64, error) {
	//Stub
	return nil, nil
}

func (a *ADAM4000) GetConfig() error {
	resp, err := a.comResF("$%02X2\r", a.address)
	if err != nil {
		return err
	}

	addr := make([]byte, 1)
	typecode := make([]byte, 1)
	baud := make([]byte, 1)
	data := make([]byte, 1)

	hex.Decode(addr, resp[1:3])
	hex.Decode(typecode, resp[3:5])
	hex.Decode(baud, resp[5:7])
	hex.Decode(data, resp[7:9])

	a.Address = addr[0]
	a.InputRange = InputRangeCode(typecode[0])
	a.BaudRate = BaudRateCode(baud[0])
	fmt.Printf("%X\n", data)
	a.Integration_time = data[0]&byte(1<<7) > 0
	a.Checksum = data[0]&byte(1<<6) > 0
	a.DataFormat = DataFormatCode(data[0] & byte(2))
	if a.Address != a.address {
		fmt.Printf("Warning: Configured address (%d) differs from connected address (%d), in init mode?\n", a.Address, a.address)
	}
	return nil
}

func (a *ADAM4000) SetConfig() error {
	data := byte(a.DataFormat)
	if a.Integration_time {
		data |= byte(1 << 7)
	}
	if a.Checksum {
		data |= byte(1 << 6)
	}

	_, err := a.comResF("%%%02X%02X%02X%02X%02X\r", a.address, a.Address, byte(a.InputRange), byte(a.BaudRate), data)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

const pvpFeatsOfStrengthCategory int = 15270

var achievementIDs = []int{
	// Arena achievements
	399, 400, 401, 402, 403, 405, 404, 1159, 1160, 1161, 5266, 5267, 876, 2090, 2093, 2092, 2091,
	// RBG achievements
	5329, 5326, 5339, 5353, 5341, 5355, 5343, 5356, 6942, 6941}

var realmRegions = []string{"EU", "US", "KR", "TW"}

func importStaticData() {
	logger.Println("Beginning import of static data")
	for _, r := range realmRegions {
		importRealms(r)
	}
	importRaces()
	importClasses()
	importSpecsAndTalents()
	importPvPTalents()
	importAchievements()
	importCovenants()
	importSoulbinds()
	importConduits()

	logger.Println("Static data import complete")
}

func parseRealms(data *[]byte) []realm {
	type Realms struct {
		Realms []realm
	}
	var realms Realms
	err := safeUnmarshal(data, &realms)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]realm, 0)
	}
	return realms.Realms
}

func importRealms(region string) {
	var realmJSON *[]byte = getDynamic(region, "realm/index")
	var realms []realm = parseRealms(realmJSON)
	logger.Printf("Found %d %s realms", len(realms), region)
	addRealms(&realms, region)
}

func parseRaces(data *[]byte) []race {
	type Races struct {
		Races []race
	}
	var races Races
	err := safeUnmarshal(data, &races)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]race, 0)
	}
	return races.Races
}

func importRaces() {
	var racesJSON *[]byte = getStatic(region, "playable-race/index")
	var races []race = parseRaces(racesJSON)
	logger.Printf("Found %d races", len(races))
	addRaces(&races)
}

func parseClasses(data *[]byte) []class {
	type Classes struct {
		Classes []class
	}
	var classes Classes
	err := safeUnmarshal(data, &classes)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]class, 0)
	}
	return classes.Classes
}

func importClasses() {
	var classesJSON *[]byte = getStatic(region, "playable-class/index")
	var classes []class = parseClasses(classesJSON)
	logger.Printf("Found %d classes", len(classes))
	addClasses(&classes)
}

func importSpecsAndTalents() {
	var specsJSON *[]byte = getStatic(region, "playable-specialization/index")
	var specs []spec = parseSpecs(specsJSON)
	logger.Printf("Found %d specializations", len(specs))
	addSpecs(&specs)
	var talents []talent = make([]talent, 0)
	for _, spec := range specs {
		talents = append(talents, spec.Talents...)
	}
	addTalents(&talents)
}

func parseSpecs(data *[]byte) []spec {
	type CharacterSpecializationJSON struct {
		ID int
	}
	type SpecsJSON struct {
		CharacterSpecializations []CharacterSpecializationJSON `json:"character_specializations"`
	}
	var specsJSON SpecsJSON
	err := safeUnmarshal(data, &specsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]spec, 0)
	}
	var specIDs []int = make([]int, 0)
	for _, cs := range specsJSON.CharacterSpecializations {
		specIDs = append(specIDs, cs.ID)
	}
	return getFullSpecInfo(specIDs)
}

func getFullSpecInfo(specIDs []int) []spec {
	var specs []spec = make([]spec, 0)
	var ch chan spec = make(chan spec, len(specIDs))
	for _, i := range specIDs {
		go getSpec(ch, i)
	}
	for range specIDs {
		specs = append(specs, <-ch)
	}
	return specs
}

func getSpec(ch chan spec, specID int) {
	type RoleJSON struct {
		Role string `json:"type"`
	}
	type SpecJSON struct {
		ID            int
		PlayableClass class `json:"playable_class"`
		Name          string
		Media         keyedValue
		Role          RoleJSON
		TalentTiers   []talentTierJSON `json:"talent_tiers"`
	}
	var path string = fmt.Sprintf("playable-specialization/%d", specID)
	var icon = getIcon(region, path)
	var specJSON *[]byte = getStatic(region, path)
	var s SpecJSON
	safeUnmarshal(specJSON, &s)
	ch <- spec{
		s.ID,
		s.PlayableClass.ID,
		s.Name,
		s.Role.Role,
		icon,
		getFullSpecTalents(specID, s.TalentTiers)}
}

func getFullSpecTalents(specID int, talentTiers []talentTierJSON) []talent {
	var talents []talent = make([]talent, 0)
	type TalentJSON struct {
		ID            int
		Spell         keyedValue
		PlayableClass class `json:"playable_class"`
	}
	for _, t := range talentTiers {
		tier := t.TierIndex
		for _, talentEntry := range t.Talents {
			col := talentEntry.ColumnIndex
			id := talentEntry.Talent.ID
			var talentJSON *[]byte = getStatic(region, fmt.Sprintf("talent/%d", id))
			var talentDetails TalentJSON
			safeUnmarshal(talentJSON, &talentDetails)
			icon := getIcon(region, fmt.Sprintf("spell/%d", talentDetails.Spell.ID))
			talent := talent{
				id,
				talentDetails.Spell.ID,
				talentDetails.PlayableClass.ID,
				specID,
				talentDetails.Spell.Name,
				icon,
				tier,
				col}
			talents = append(talents, talent)
		}
	}
	return talents
}

func importPvPTalents() {
	var talentsJSON *[]byte = getStatic(region, "pvp-talent/index")
	var pvpTalents []pvpTalent = parsePvPTalents(talentsJSON)
	logger.Printf("Found %d PvP Talents", len(pvpTalents))
	addPvPTalents(&pvpTalents)
}

func parsePvPTalents(data *[]byte) []pvpTalent {
	type PvPTalentsJSON struct {
		PvPTalents []keyedValue `json:"pvp_talents"`
	}
	var pvpTalentsJSON PvPTalentsJSON
	err := safeUnmarshal(data, &pvpTalentsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]pvpTalent, 0)
	}
	var pvpTalents []pvpTalent = make([]pvpTalent, 0)
	var ch chan pvpTalent = make(chan pvpTalent, len(pvpTalentsJSON.PvPTalents))
	for _, keyedValue := range pvpTalentsJSON.PvPTalents {
		go getPvPTalent(ch, keyedValue.ID)
	}
	for range pvpTalentsJSON.PvPTalents {
		pvpTalents = append(pvpTalents, <-ch)
	}
	return pvpTalents
}

func getPvPTalent(ch chan pvpTalent, id int) {
	type PvPTalentJSON struct {
		Spell                  keyedValue
		PlayableSpecialization keyedValue `json:"playable_specialization"`
	}
	var pvpTalentJSON *[]byte = getStatic(region, fmt.Sprintf("pvp-talent/%d", id))
	var talentDetails PvPTalentJSON
	safeUnmarshal(pvpTalentJSON, &talentDetails)
	icon := getIcon(region, fmt.Sprintf("spell/%d", talentDetails.Spell.ID))
	ch <- pvpTalent{
		id,
		talentDetails.Spell.Name,
		talentDetails.Spell.ID,
		talentDetails.PlayableSpecialization.ID,
		icon}
}

func parseAchievements(data *[]byte) []achievement {
	type Achievements struct {
		ID           int
		Name         string
		Achievements []keyedValue
	}

	var achievements Achievements
	err := safeUnmarshal(data, &achievements)
	var pvpAchievements []achievement = make([]achievement, 0)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return pvpAchievements
	}

	var achievementIDs []int = make([]int, 0)
	for _, ac := range achievements.Achievements {
		if strings.Contains(ac.Name, "Season") {
			achievementIDs = append(achievementIDs, ac.ID)
		}
	}
	var ch chan achievement = make(chan achievement, len(achievementIDs))
	for _, id := range achievementIDs {
		go getPvPAchievement(ch, id)
	}
	for range achievementIDs {
		pvpAchievements = append(pvpAchievements, <-ch)
	}

	return pvpAchievements
}

func getPvPAchievement(ch chan achievement, id int) {
	ch <- getAchievement(id)
}

func getAchievement(id int) achievement {
	type PvPAchievementJSON struct {
		ID          int
		Name        string
		Description string
	}
	var pvpAchievementJSON *[]byte = getStatic(region, fmt.Sprintf("achievement/%d", id))
	var pvpAchievementJSONDetails PvPAchievementJSON
	safeUnmarshal(pvpAchievementJSON, &pvpAchievementJSONDetails)
	return achievement{
		id,
		pvpAchievementJSONDetails.Name,
		pvpAchievementJSONDetails.Description}
}

func importAchievements() {
	var achievementsJSON *[]byte = getStatic(region, fmt.Sprintf("achievement-category/%d", pvpFeatsOfStrengthCategory))
	var achievements []achievement = parseAchievements(achievementsJSON)
	var seasonalCount int = len(achievements)
	logger.Printf("Found %d seasonal achievements", seasonalCount)
	for _, id := range achievementIDs {
		achievement := getAchievement(id)
		achievements = append(achievements, achievement)
	}
	logger.Printf("Found %d non-seasonal achievements", len(achievements)-seasonalCount)
	addAchievements(&achievements)
}

func importCovenants() {
	var covenantsJSON *[]byte = getStatic(region, "covenant/index")
	var covenants []covenant = parseCovenants(covenantsJSON)
	logger.Printf("Found %d covenants", len(covenants))
	addCovenants(&covenants)
}

func parseCovenants(data *[]byte) []covenant {
	type Covenants struct {
		Covenants []keyedValue
	}
	var covenantsJSON Covenants
	var covenants []covenant = make([]covenant, 0)
	err := safeUnmarshal(data, &covenantsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return covenants
	}
	for _, c := range covenantsJSON.Covenants {
		icon := getIcon(region, fmt.Sprintf("covenant/%d", c.ID))
		covenants = append(covenants, covenant{c.ID, c.Name, icon})
	}
	return covenants
}

func importSoulbinds() {
	var soulbindsJSON *[]byte = getStatic(region, "covenant/soulbind/index")
	var soulbinds []soulbind = parseSoulbinds(soulbindsJSON)
	logger.Printf("Found %d soulbinds", len(soulbinds))
	addSoulbinds(&soulbinds)
}

func parseSoulbinds(data *[]byte) []soulbind {
	type Soulbinds struct {
		Soulbinds []keyedValue
	}
	var soulbindsJSON Soulbinds
	var soulbinds []soulbind = make([]soulbind, 0)
	err := safeUnmarshal(data, &soulbindsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return soulbinds
	}
	for _, sb := range soulbindsJSON.Soulbinds {
		soulbinds = append(soulbinds, soulbind{sb.ID, sb.Name})
	}
	return soulbinds
}

func importConduits() {
	var conduitsJSON *[]byte = getStatic(region, "covenant/conduit/index")
	var conduits []conduit = parseConduits(conduitsJSON)
	logger.Printf("Found %d conduits", len(conduits))
	addConduits(&conduits)
}

func parseConduits(data *[]byte) []conduit {
	type Conduits struct {
		Conduits []keyedValue
	}
	var conduitsJSON Conduits
	var conduits []conduit = make([]conduit, 0)
	err := safeUnmarshal(data, &conduitsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return conduits
	}
	for _, c := range conduitsJSON.Conduits {
		spellID := getConduitSpellID(c.ID)
		conduits = append(conduits, conduit{c.ID, spellID, c.Name})
	}
	return conduits
}

func getConduitSpellID(conduitID int) int {
	type ConduitSpellToolTip struct {
		Spell keyedValue
	}
	type ConduitRank struct {
		ID           int
		Tier         int
		SpellTooltip ConduitSpellToolTip `json:"spell_tooltip"`
	}
	type Conduit struct {
		ID    int
		Name  string
		Ranks []ConduitRank
	}
	var conduitJSON Conduit
	var data *[]byte = getStatic(region, fmt.Sprintf("covenant/conduit/%d", conduitID))
	err := safeUnmarshal(data, &conduitJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return 0
	}
	if conduitJSON.Ranks == nil || len(conduitJSON.Ranks) == 0 {
		return 0
	}
	return conduitJSON.Ranks[0].SpellTooltip.Spell.ID
}
<commit_msg>clean up achievements<commit_after>package main

import (
	"fmt"
	"strings"
)

const pvpFeatsOfStrengthCategory int = 15270

var achievementIDs = []int{
	// 2v2
	399, 400, 401, 1159,
	// 3v3
	402, 403, 405, 1160, 5266, 5267, 2091,
	// RBG achievements
	5341, 5355, 5343, 5356, 6942, 6941,
}

var realmRegions = []string{"EU", "US", "KR", "TW"}

func importStaticData() {
	logger.Println("Beginning import of static data")
	for _, r := range realmRegions {
		importRealms(r)
	}
	importRaces()
	importClasses()
	importSpecsAndTalents()
	importPvPTalents()
	importAchievements()
	importCovenants()
	importSoulbinds()
	importConduits()

	logger.Println("Static data import complete")
}

func parseRealms(data *[]byte) []realm {
	type Realms struct {
		Realms []realm
	}
	var realms Realms
	err := safeUnmarshal(data, &realms)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]realm, 0)
	}
	return realms.Realms
}

func importRealms(region string) {
	var realmJSON *[]byte = getDynamic(region, "realm/index")
	var realms []realm = parseRealms(realmJSON)
	logger.Printf("Found %d %s realms", len(realms), region)
	addRealms(&realms, region)
}

func parseRaces(data *[]byte) []race {
	type Races struct {
		Races []race
	}
	var races Races
	err := safeUnmarshal(data, &races)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]race, 0)
	}
	return races.Races
}

func importRaces() {
	var racesJSON *[]byte = getStatic(region, "playable-race/index")
	var races []race = parseRaces(racesJSON)
	logger.Printf("Found %d races", len(races))
	addRaces(&races)
}

func parseClasses(data *[]byte) []class {
	type Classes struct {
		Classes []class
	}
	var classes Classes
	err := safeUnmarshal(data, &classes)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]class, 0)
	}
	return classes.Classes
}

func importClasses() {
	var classesJSON *[]byte = getStatic(region, "playable-class/index")
	var classes []class = parseClasses(classesJSON)
	logger.Printf("Found %d classes", len(classes))
	addClasses(&classes)
}

func importSpecsAndTalents() {
	var specsJSON *[]byte = getStatic(region, "playable-specialization/index")
	var specs []spec = parseSpecs(specsJSON)
	logger.Printf("Found %d specializations", len(specs))
	addSpecs(&specs)
	var talents []talent = make([]talent, 0)
	for _, spec := range specs {
		talents = append(talents, spec.Talents...)
	}
	addTalents(&talents)
}

func parseSpecs(data *[]byte) []spec {
	type CharacterSpecializationJSON struct {
		ID int
	}
	type SpecsJSON struct {
		CharacterSpecializations []CharacterSpecializationJSON `json:"character_specializations"`
	}
	var specsJSON SpecsJSON
	err := safeUnmarshal(data, &specsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]spec, 0)
	}
	var specIDs []int = make([]int, 0)
	for _, cs := range specsJSON.CharacterSpecializations {
		specIDs = append(specIDs, cs.ID)
	}
	return getFullSpecInfo(specIDs)
}

func getFullSpecInfo(specIDs []int) []spec {
	var specs []spec = make([]spec, 0)
	var ch chan spec = make(chan spec, len(specIDs))
	for _, i := range specIDs {
		go getSpec(ch, i)
	}
	for range specIDs {
		specs = append(specs, <-ch)
	}
	return specs
}

func getSpec(ch chan spec, specID int) {
	type RoleJSON struct {
		Role string `json:"type"`
	}
	type SpecJSON struct {
		ID            int
		PlayableClass class `json:"playable_class"`
		Name          string
		Media         keyedValue
		Role          RoleJSON
		TalentTiers   []talentTierJSON `json:"talent_tiers"`
	}
	var path string = fmt.Sprintf("playable-specialization/%d", specID)
	var icon = getIcon(region, path)
	var specJSON *[]byte = getStatic(region, path)
	var s SpecJSON
	safeUnmarshal(specJSON, &s)
	ch <- spec{
		s.ID,
		s.PlayableClass.ID,
		s.Name,
		s.Role.Role,
		icon,
		getFullSpecTalents(specID, s.TalentTiers)}
}

func getFullSpecTalents(specID int, talentTiers []talentTierJSON) []talent {
	var talents []talent = make([]talent, 0)
	type TalentJSON struct {
		ID            int
		Spell         keyedValue
		PlayableClass class `json:"playable_class"`
	}
	for _, t := range talentTiers {
		tier := t.TierIndex
		for _, talentEntry := range t.Talents {
			col := talentEntry.ColumnIndex
			id := talentEntry.Talent.ID
			var talentJSON *[]byte = getStatic(region, fmt.Sprintf("talent/%d", id))
			var talentDetails TalentJSON
			safeUnmarshal(talentJSON, &talentDetails)
			icon := getIcon(region, fmt.Sprintf("spell/%d", talentDetails.Spell.ID))
			talent := talent{
				id,
				talentDetails.Spell.ID,
				talentDetails.PlayableClass.ID,
				specID,
				talentDetails.Spell.Name,
				icon,
				tier,
				col}
			talents = append(talents, talent)
		}
	}
	return talents
}

func importPvPTalents() {
	var talentsJSON *[]byte = getStatic(region, "pvp-talent/index")
	var pvpTalents []pvpTalent = parsePvPTalents(talentsJSON)
	logger.Printf("Found %d PvP Talents", len(pvpTalents))
	addPvPTalents(&pvpTalents)
}

func parsePvPTalents(data *[]byte) []pvpTalent {
	type PvPTalentsJSON struct {
		PvPTalents []keyedValue `json:"pvp_talents"`
	}
	var pvpTalentsJSON PvPTalentsJSON
	err := safeUnmarshal(data, &pvpTalentsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return make([]pvpTalent, 0)
	}
	var pvpTalents []pvpTalent = make([]pvpTalent, 0)
	var ch chan pvpTalent = make(chan pvpTalent, len(pvpTalentsJSON.PvPTalents))
	for _, keyedValue := range pvpTalentsJSON.PvPTalents {
		go getPvPTalent(ch, keyedValue.ID)
	}
	for range pvpTalentsJSON.PvPTalents {
		pvpTalents = append(pvpTalents, <-ch)
	}
	return pvpTalents
}

func getPvPTalent(ch chan pvpTalent, id int) {
	type PvPTalentJSON struct {
		Spell                  keyedValue
		PlayableSpecialization keyedValue `json:"playable_specialization"`
	}
	var pvpTalentJSON *[]byte = getStatic(region, fmt.Sprintf("pvp-talent/%d", id))
	var talentDetails PvPTalentJSON
	safeUnmarshal(pvpTalentJSON, &talentDetails)
	icon := getIcon(region, fmt.Sprintf("spell/%d", talentDetails.Spell.ID))
	ch <- pvpTalent{
		id,
		talentDetails.Spell.Name,
		talentDetails.Spell.ID,
		talentDetails.PlayableSpecialization.ID,
		icon}
}

func parseAchievements(data *[]byte) []achievement {
	type Achievements struct {
		ID           int
		Name         string
		Achievements []keyedValue
	}

	var achievements Achievements
	err := safeUnmarshal(data, &achievements)
	var pvpAchievements []achievement = make([]achievement, 0)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return pvpAchievements
	}

	var achievementIDs []int = make([]int, 0)
	for _, ac := range achievements.Achievements {
		if strings.Contains(ac.Name, "Season") {
			achievementIDs = append(achievementIDs, ac.ID)
		}
	}
	var ch chan achievement = make(chan achievement, len(achievementIDs))
	for _, id := range achievementIDs {
		go getPvPAchievement(ch, id)
	}
	for range achievementIDs {
		pvpAchievements = append(pvpAchievements, <-ch)
	}

	return pvpAchievements
}

func getPvPAchievement(ch chan achievement, id int) {
	ch <- getAchievement(id)
}

func getAchievement(id int) achievement {
	type PvPAchievementJSON struct {
		ID          int
		Name        string
		Description string
	}
	var pvpAchievementJSON *[]byte = getStatic(region, fmt.Sprintf("achievement/%d", id))
	var pvpAchievementJSONDetails PvPAchievementJSON
	safeUnmarshal(pvpAchievementJSON, &pvpAchievementJSONDetails)
	return achievement{
		id,
		pvpAchievementJSONDetails.Name,
		pvpAchievementJSONDetails.Description}
}

func importAchievements() {
	var achievementsJSON *[]byte = getStatic(region, fmt.Sprintf("achievement-category/%d", pvpFeatsOfStrengthCategory))
	var achievements []achievement = parseAchievements(achievementsJSON)
	var seasonalCount int = len(achievements)
	logger.Printf("Found %d seasonal achievements", seasonalCount)
	for _, id := range achievementIDs {
		achievement := getAchievement(id)
		achievements = append(achievements, achievement)
	}
	logger.Printf("Found %d non-seasonal achievements", len(achievements)-seasonalCount)
	addAchievements(&achievements)
}

func importCovenants() {
	var covenantsJSON *[]byte = getStatic(region, "covenant/index")
	var covenants []covenant = parseCovenants(covenantsJSON)
	logger.Printf("Found %d covenants", len(covenants))
	addCovenants(&covenants)
}

func parseCovenants(data *[]byte) []covenant {
	type Covenants struct {
		Covenants []keyedValue
	}
	var covenantsJSON Covenants
	var covenants []covenant = make([]covenant, 0)
	err := safeUnmarshal(data, &covenantsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return covenants
	}
	for _, c := range covenantsJSON.Covenants {
		icon := getIcon(region, fmt.Sprintf("covenant/%d", c.ID))
		covenants = append(covenants, covenant{c.ID, c.Name, icon})
	}
	return covenants
}

func importSoulbinds() {
	var soulbindsJSON *[]byte = getStatic(region, "covenant/soulbind/index")
	var soulbinds []soulbind = parseSoulbinds(soulbindsJSON)
	logger.Printf("Found %d soulbinds", len(soulbinds))
	addSoulbinds(&soulbinds)
}

func parseSoulbinds(data *[]byte) []soulbind {
	type Soulbinds struct {
		Soulbinds []keyedValue
	}
	var soulbindsJSON Soulbinds
	var soulbinds []soulbind = make([]soulbind, 0)
	err := safeUnmarshal(data, &soulbindsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return soulbinds
	}
	for _, sb := range soulbindsJSON.Soulbinds {
		soulbinds = append(soulbinds, soulbind{sb.ID, sb.Name})
	}
	return soulbinds
}

func importConduits() {
	var conduitsJSON *[]byte = getStatic(region, "covenant/conduit/index")
	var conduits []conduit = parseConduits(conduitsJSON)
	logger.Printf("Found %d conduits", len(conduits))
	addConduits(&conduits)
}

func parseConduits(data *[]byte) []conduit {
	type Conduits struct {
		Conduits []keyedValue
	}
	var conduitsJSON Conduits
	var conduits []conduit = make([]conduit, 0)
	err := safeUnmarshal(data, &conduitsJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return conduits
	}
	for _, c := range conduitsJSON.Conduits {
		spellID := getConduitSpellID(c.ID)
		conduits = append(conduits, conduit{c.ID, spellID, c.Name})
	}
	return conduits
}

func getConduitSpellID(conduitID int) int {
	type ConduitSpellToolTip struct {
		Spell keyedValue
	}
	type ConduitRank struct {
		ID           int
		Tier         int
		SpellTooltip ConduitSpellToolTip `json:"spell_tooltip"`
	}
	type Conduit struct {
		ID    int
		Name  string
		Ranks []ConduitRank
	}
	var conduitJSON Conduit
	var data *[]byte = getStatic(region, fmt.Sprintf("covenant/conduit/%d", conduitID))
	err := safeUnmarshal(data, &conduitJSON)
	if err != nil {
		logger.Printf("%s json parsing failed: %s", errPrefix, err)
		return 0
	}
	if conduitJSON.Ranks == nil || len(conduitJSON.Ranks) == 0 {
		return 0
	}
	return conduitJSON.Ranks[0].SpellTooltip.Spell.ID
}
<|endoftext|>


<commit_before>package mpjson

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// JSONPlugin plugin for JSON
type JSONPlugin struct {
	Target             string
	Tempfile           string
	URL                string
	Prefix             string
	InsecureSkipVerify bool
	ShowOnlyNum        bool
	ExcludeExp         *regexp.Regexp
	IncludeExp         *regexp.Regexp
}

func (p JSONPlugin) traverseMap(content interface{}, path []string) (map[string]float64, error) {
	stat := make(map[string]float64)
	if reflect.TypeOf(content).Kind() == reflect.Slice {
		for i, c := range content.([]interface{}) {
			ts, _ := p.traverseMap(c, append(path, strconv.Itoa(i)))
			for tk, tv := range ts {
				stat[tk] = tv
			}
		}
	} else {
		for k, v := range content.(map[string]interface{}) {
			if v != nil {
				if reflect.TypeOf(v).Kind() == reflect.Map {
					ts, _ := p.traverseMap(v, append(path, k))
					for tk, tv := range ts {
						stat[tk] = tv
					}
				} else if reflect.TypeOf(v).Kind() == reflect.Slice {
					for i, c := range v.([]interface{}) {
						ts, _ := p.traverseMap(c, append(path, strconv.Itoa(i)))
						for tk, tv := range ts {
							stat[tk] = tv
						}
					}
				} else {
					tk, tv := p.outputMetric(strings.Join(append(path, k), "."), v)
					if tk != "" {
						stat[tk] = tv
					}
				}
			}
		}
	}

	return stat, nil
}

func (p JSONPlugin) outputMetric(path string, value interface{}) (string, float64) {
	if p.IncludeExp.MatchString(path) && !p.ExcludeExp.MatchString(path) {
		if reflect.TypeOf(value).Kind() == reflect.Float64 {
			return path, value.(float64)
		}
	}

	return "", 0
}

// FetchMetrics interface for mackerel-plugin
func (p JSONPlugin) FetchMetrics() (map[string]float64, error) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: p.InsecureSkipVerify},
	}
	client := &http.Client{Transport: tr}
	response, _ := client.Get(p.URL)
	bytes, _ := ioutil.ReadAll(response.Body)

	var content interface{}
	err := json.Unmarshal(bytes, &content)
	if err != nil {
		panic(err)
	}

	return p.traverseMap(content, []string{p.Prefix})
}

// Do do doo
func Do() {
	url := flag.String("url", "", "URL to get a JSON")
	prefix := flag.String("prefix", "custom", "Prefix for metric names")
	insecure := flag.Bool("insecure", false, "Skip certificate verifications")
	exclude := flag.String("exclude", `^$`, "Exclude metrics that matches the expression")
	include := flag.String("include", ``, "Output metrics that matches the expression")
	flag.Parse()

	var jsonplugin JSONPlugin

	jsonplugin.URL = *url
	jsonplugin.Prefix = *prefix
	jsonplugin.InsecureSkipVerify = *insecure
	var err error
	jsonplugin.ExcludeExp, err = regexp.Compile(*exclude)
	if err != nil {
		fmt.Printf("exclude expression is invalid: %s", err)
		os.Exit(1)
	}
	jsonplugin.IncludeExp, err = regexp.Compile(*include)
	if err != nil {
		fmt.Printf("include expression is invalid: %s", err)
		os.Exit(1)
	}

	metrics, _ := jsonplugin.FetchMetrics()
	for k, v := range metrics {
		fmt.Printf("%s\t%f\t%d\n", k, v, time.Now().Unix())
	}
}
<commit_msg>Do error handling<commit_after>package mpjson

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// JSONPlugin plugin for JSON
type JSONPlugin struct {
	Target             string
	Tempfile           string
	URL                string
	Prefix             string
	InsecureSkipVerify bool
	ShowOnlyNum        bool
	ExcludeExp         *regexp.Regexp
	IncludeExp         *regexp.Regexp
}

func (p JSONPlugin) traverseMap(content interface{}, path []string) (map[string]float64, error) {
	stat := make(map[string]float64)
	if reflect.TypeOf(content).Kind() == reflect.Slice {
		for i, c := range content.([]interface{}) {
			ts, _ := p.traverseMap(c, append(path, strconv.Itoa(i)))
			for tk, tv := range ts {
				stat[tk] = tv
			}
		}
	} else {
		for k, v := range content.(map[string]interface{}) {
			if v != nil {
				if reflect.TypeOf(v).Kind() == reflect.Map {
					ts, _ := p.traverseMap(v, append(path, k))
					for tk, tv := range ts {
						stat[tk] = tv
					}
				} else if reflect.TypeOf(v).Kind() == reflect.Slice {
					for i, c := range v.([]interface{}) {
						ts, _ := p.traverseMap(c, append(path, strconv.Itoa(i)))
						for tk, tv := range ts {
							stat[tk] = tv
						}
					}
				} else {
					tk, tv := p.outputMetric(strings.Join(append(path, k), "."), v)
					if tk != "" {
						stat[tk] = tv
					}
				}
			}
		}
	}

	return stat, nil
}

func (p JSONPlugin) outputMetric(path string, value interface{}) (string, float64) {
	if p.IncludeExp.MatchString(path) && !p.ExcludeExp.MatchString(path) {
		if reflect.TypeOf(value).Kind() == reflect.Float64 {
			return path, value.(float64)
		}
	}

	return "", 0
}

// FetchMetrics interface for mackerel-plugin
func (p JSONPlugin) FetchMetrics() (map[string]float64, error) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: p.InsecureSkipVerify},
	}
	client := &http.Client{Transport: tr}
	response, err := client.Get(p.URL)
	if err != nil {
		return nil, err
	}
	bytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	var content interface{}
	if err := json.Unmarshal(bytes, &content); err != nil {
		return nil, err
	}

	return p.traverseMap(content, []string{p.Prefix})
}

// Do do doo
func Do() {
	url := flag.String("url", "", "URL to get a JSON")
	prefix := flag.String("prefix", "custom", "Prefix for metric names")
	insecure := flag.Bool("insecure", false, "Skip certificate verifications")
	exclude := flag.String("exclude", `^$`, "Exclude metrics that matches the expression")
	include := flag.String("include", ``, "Output metrics that matches the expression")
	flag.Parse()

	var jsonplugin JSONPlugin

	jsonplugin.URL = *url
	jsonplugin.Prefix = *prefix
	jsonplugin.InsecureSkipVerify = *insecure
	var err error
	jsonplugin.ExcludeExp, err = regexp.Compile(*exclude)
	if err != nil {
		fmt.Printf("exclude expression is invalid: %s", err)
		os.Exit(1)
	}
	jsonplugin.IncludeExp, err = regexp.Compile(*include)
	if err != nil {
		fmt.Printf("include expression is invalid: %s", err)
		os.Exit(1)
	}

	metrics, _ := jsonplugin.FetchMetrics()
	for k, v := range metrics {
		fmt.Printf("%s\t%f\t%d\n", k, v, time.Now().Unix())
	}
}
<|endoftext|>


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

package acceptance

import "testing"

func TestDockerReadWriteReferenceVersion(t *testing.T) {
	func() { // OneShot assumes it is at specific call depth from TestFoo.
		if testDockerSingleNode(t, "reference", []string{"/cockroach", "version"}) != nil {
			t.Skip(`TODO(dt): No /cockroach binary in one-shot container. See #6086.`)
		}
	}()
	testDockerSuccess(t, "reference", []string{"/bin/bash", "-c", `
set -xe
mkdir /old
cd /old

export PGHOST=localhost
export PGPORT=""

bin=/reference-version/cockroach
# TODO(bdarnell): when --background is in reference-version, use it here and below.
$bin start &
sleep 1
echo "Use the reference binary to write a couple rows, then render its output to a file and shut down."
$bin sql -e "CREATE DATABASE old"
$bin sql -d old -e "CREATE TABLE testing (i int primary key, b bool, s string unique, d decimal, f float, t timestamp, v interval, index sb (s, b))"
$bin sql -d old -e "INSERT INTO testing values (1, true, 'hello', decimal '3.14159', 3.14159, NOW(), interval '1h')"
$bin sql -d old -e "INSERT INTO testing values (2, false, 'world', decimal '0.14159', 0.14159, NOW(), interval '234h45m2s234ms')"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > old.everything
$bin quit && wait # wait will block until all background jobs finish.

bin=/cockroach
$bin start --background
echo "Read data written by reference version using new binary"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > new.everything
# diff returns non-zero if different. With set -e above, that would exit here.
diff new.everything old.everything

echo "Add a row with the new binary and render the updated data before shutting down."
$bin sql -d old -e "INSERT INTO testing values (3, false, '!', decimal '2.14159', 2.14159, NOW(), interval '3h')"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > new.everything
$bin quit
# Let it close its listening sockets.
sleep 1

echo "Read the modified data using the reference binary again."
bin=/reference-version/cockroach
$bin start &
sleep 1
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > old.everything
# diff returns non-zero if different. With set -e above, that would exit here.
diff new.everything old.everything
$bin quit && wait
`})
}
<commit_msg>acceptance: bidir and forward reference tests<commit_after>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: David Taylor (david@cockroachlabs.com)

package acceptance

import (
	"fmt"
	"testing"
)

func runReadWriteReferenceTest(
	t *testing.T,
	referenceBinPath string,
	backwardReferenceTest string,
) {
	if err := testDockerSingleNode(t, "reference", []string{"/cockroach", "version"}); err != nil {
		t.Skipf(`TODO(dt): No /cockroach binary in one-shot container, see #6086: %s`, err)
	}
	referenceTestScript := fmt.Sprintf(`
set -xe
mkdir /old
cd /old

export PGHOST=localhost
export PGPORT=""

bin=/%s/cockroach
# TODO(bdarnell): when --background is in referenceBinPath, use it here and below.
$bin start &
sleep 1
echo "Use the reference binary to write a couple rows, then render its output to a file and shut down."
$bin sql -e "CREATE DATABASE old"
$bin sql -d old -e "CREATE TABLE testing (i int primary key, b bool, s string unique, d decimal, f float, t timestamp, v interval, index sb (s, b))"
$bin sql -d old -e "INSERT INTO testing values (1, true, 'hello', decimal '3.14159', 3.14159, NOW(), interval '1h')"
$bin sql -d old -e "INSERT INTO testing values (2, false, 'world', decimal '0.14159', 0.14159, NOW(), interval '234h45m2s234ms')"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > old.everything
$bin quit && wait # wait will block until all background jobs finish.

bin=/cockroach
$bin start --background
echo "Read data written by reference version using new binary"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > new.everything
# diff returns non-zero if different. With set -e above, that would exit here.
diff new.everything old.everything

echo "Add a row with the new binary and render the updated data before shutting down."
$bin sql -d old -e "INSERT INTO testing values (3, false, '!', decimal '2.14159', 2.14159, NOW(), interval '3h')"
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > new.everything
$bin quit
# Let it close its listening sockets.
sleep 1

echo "Read the modified data using the reference binary again."
bin=/%s/cockroach
%s
`, referenceBinPath, referenceBinPath, backwardReferenceTest)
	err := testDockerSingleNode(t, "reference", []string{"/bin/bash", "-c", referenceTestScript})
	if err != nil {
		t.Errorf("expected success: %s", err)
	}
}

func TestDockerReadWriteBidirectionalReferenceVersion(t *testing.T) {
	backwardReferenceTest := `
$bin start &
sleep 1
$bin sql -d old -e "SELECT i, b, s, d, f, v, extract(epoch FROM t) FROM testing" > old.everything
# diff returns non-zero if different. With set -e above, that would exit here.
diff new.everything old.everything
$bin quit && wait
`
	runReadWriteReferenceTest(t, `bidirectional-reference-version`, backwardReferenceTest)
}

func TestDockerReadWriteForwardReferenceVersion(t *testing.T) {
	backwardReferenceTest := `
# TODO(dan): Once we have a version that's only forward compatible, test the failure message.
`
	runReadWriteReferenceTest(t, `forward-reference-version`, backwardReferenceTest)
}
<|endoftext|>


<commit_before>package app

import (
	"bytes"
	"encoding/json"
	"github.com/timeredbull/tsuru/db"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/mgo/bson"
	"net/http"
	"net/http/httptest"
	"strings"
)

func (s *S) TestUpload(c *C) {
	fileApplicationContents, _ := ioutil.ReadFile("testdata/example.zip")
	message := `
--MyBoundary
Content-Disposition: form-data; name="application"; filename="application.zip"
Content-Type: application/zip

` + string(fileApplicationContents) + `
--MyBoundary--
`

	myApp := App{Name: "myApp", Framework: "django"}
	myApp.Create()

	b := bytes.NewBufferString(message)
	request, err := http.NewRequest("POST", "/apps"+myApp.Name+"/application?:name="+myApp.Name, b)
	c.Assert(err, IsNil)

	ctype := `multipart/form-data; boundary="MyBoundary"`
	request.Header.Set("Content-type", ctype)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = Upload(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)
	c.Assert(recorder.Body.String(), Equals, "success")

	myApp.Destroy()
}

func (s *S) TestUploadReturns404WhenAppDoesNotExist(c *C) {
	myApp := App{Name: "myAppThatDoestNotExists", Framework: "django"}
	request, err := http.NewRequest("POST", "/apps"+myApp.Name+"/application?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = Upload(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 404)
}

func (s *S) TestAppList(c *C) {
	apps := make([]App, 0)
	expected := make([]App, 0)
	app1 := App{Name: "app1"}
	app1.Create()
	expected = append(expected, app1)
	app2 := App{Name: "app2"}
	app2.Create()
	expected = append(expected, app2)
	app3 := App{Name: "app3", Framework: "django", Ip: "122222"}
	app3.Create()
	expected = append(expected, app3)

	request, err := http.NewRequest("GET", "/apps/", nil)
	c.Assert(err, IsNil)

	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	err = AppList(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	err = json.Unmarshal(body, &apps)
	c.Assert(err, IsNil)
	c.Assert(apps, DeepEquals, expected)

	app1.Destroy()
	app2.Destroy()
	app3.Destroy()
}

func (s *S) TestDelete(c *C) {
	myApp := App{Name: "MyAppToDelete", Framework: "django"}
	myApp.Create()
	request, err := http.NewRequest("DELETE", "/apps/"+myApp.Name+"?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = AppDelete(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)
}

func (s *S) TestAppInfo(c *C) {

	exptectedApp := App{Name: "NewApp", Framework: "django"}
	exptectedApp.Create()

	var myApp App

	request, err := http.NewRequest("GET", "/apps/"+exptectedApp.Name+"?:name="+exptectedApp.Name, nil)
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	c.Assert(err, IsNil)

	err = AppInfo(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	err = json.Unmarshal(body, &myApp)
	c.Assert(err, IsNil)
	c.Assert(myApp, Equals, exptectedApp)

	exptectedApp.Destroy()

}

func (s *S) TestAppInfoReturns404WhenAppDoesNotExist(c *C) {
	myApp := App{Name: "SomeApp"}
	request, err := http.NewRequest("GET", "/apps/"+myApp.Name+"?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	err = AppInfo(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 404)
}

func (s *S) TestCreateApp(c *C) {
	a := App{Name: "someApp"}
	defer a.Destroy()

	b := strings.NewReader(`{"name":"someApp", "framework":"django"}`)
	request, err := http.NewRequest("POST", "/apps", b)
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()

	c.Assert(err, IsNil)

	err = CreateAppHandler(recorder, request)
	c.Assert(err, IsNil)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	repoUrl := GetRepositoryUrl(&a)
	var obtained map[string]string
	expected := map[string]string{
		"status":         "success",
		"repository_url": repoUrl,
	}
	err = json.Unmarshal(body, &obtained)

	c.Assert(obtained, DeepEquals, expected)
	c.Assert(recorder.Code, Equals, 200)

	qtd, err := db.Session.Apps().Find(bson.M{"name": "someApp"}).Count()
	c.Assert(err, IsNil)
	c.Assert(qtd, Equals, 1)

}
<commit_msg>api/auth: typo on test<commit_after>package app

import (
	"bytes"
	"encoding/json"
	"github.com/timeredbull/tsuru/db"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/mgo/bson"
	"net/http"
	"net/http/httptest"
	"strings"
)

func (s *S) TestUpload(c *C) {
	fileApplicationContents, _ := ioutil.ReadFile("testdata/example.zip")
	message := `
--MyBoundary
Content-Disposition: form-data; name="application"; filename="application.zip"
Content-Type: application/zip

` + string(fileApplicationContents) + `
--MyBoundary--
`

	myApp := App{Name: "myApp", Framework: "django"}
	myApp.Create()

	b := bytes.NewBufferString(message)
	request, err := http.NewRequest("POST", "/apps"+myApp.Name+"/application?:name="+myApp.Name, b)
	c.Assert(err, IsNil)

	ctype := `multipart/form-data; boundary="MyBoundary"`
	request.Header.Set("Content-type", ctype)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = Upload(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)
	c.Assert(recorder.Body.String(), Equals, "success")

	myApp.Destroy()
}

func (s *S) TestUploadReturns404WhenAppDoesNotExist(c *C) {
	myApp := App{Name: "myAppThatDoestNotExists", Framework: "django"}
	request, err := http.NewRequest("POST", "/apps"+myApp.Name+"/application?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = Upload(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 404)
}

func (s *S) TestAppList(c *C) {
	apps := make([]App, 0)
	expected := make([]App, 0)
	app1 := App{Name: "app1"}
	app1.Create()
	expected = append(expected, app1)
	app2 := App{Name: "app2"}
	app2.Create()
	expected = append(expected, app2)
	app3 := App{Name: "app3", Framework: "django", Ip: "122222"}
	app3.Create()
	expected = append(expected, app3)

	request, err := http.NewRequest("GET", "/apps/", nil)
	c.Assert(err, IsNil)

	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	err = AppList(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	err = json.Unmarshal(body, &apps)
	c.Assert(err, IsNil)
	c.Assert(apps, DeepEquals, expected)

	app1.Destroy()
	app2.Destroy()
	app3.Destroy()
}

func (s *S) TestDelete(c *C) {
	myApp := App{Name: "MyAppToDelete", Framework: "django"}
	myApp.Create()
	request, err := http.NewRequest("DELETE", "/apps/"+myApp.Name+"?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	recorder := httptest.NewRecorder()
	err = AppDelete(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)
}

func (s *S) TestAppInfo(c *C) {

	expectedApp := App{Name: "NewApp", Framework: "django"}
	expectedApp.Create()

	var myApp App

	request, err := http.NewRequest("GET", "/apps/"+expectedApp.Name+"?:name="+expectedApp.Name, nil)
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	c.Assert(err, IsNil)

	err = AppInfo(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 200)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	err = json.Unmarshal(body, &myApp)
	c.Assert(err, IsNil)
	c.Assert(myApp, Equals, expectedApp)

	expectedApp.Destroy()

}

func (s *S) TestAppInfoReturns404WhenAppDoesNotExist(c *C) {
	myApp := App{Name: "SomeApp"}
	request, err := http.NewRequest("GET", "/apps/"+myApp.Name+"?:name="+myApp.Name, nil)
	c.Assert(err, IsNil)

	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	err = AppInfo(recorder, request)
	c.Assert(err, IsNil)
	c.Assert(recorder.Code, Equals, 404)
}

func (s *S) TestCreateApp(c *C) {
	a := App{Name: "someApp"}
	defer a.Destroy()

	b := strings.NewReader(`{"name":"someApp", "framework":"django"}`)
	request, err := http.NewRequest("POST", "/apps", b)
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()

	c.Assert(err, IsNil)

	err = CreateAppHandler(recorder, request)
	c.Assert(err, IsNil)

	body, err := ioutil.ReadAll(recorder.Body)
	c.Assert(err, IsNil)

	repoUrl := GetRepositoryUrl(&a)
	var obtained map[string]string
	expected := map[string]string{
		"status":         "success",
		"repository_url": repoUrl,
	}
	err = json.Unmarshal(body, &obtained)

	c.Assert(obtained, DeepEquals, expected)
	c.Assert(recorder.Code, Equals, 200)

	qtd, err := db.Session.Apps().Find(bson.M{"name": "someApp"}).Count()
	c.Assert(err, IsNil)
	c.Assert(qtd, Equals, 1)

}
<|endoftext|>


<commit_before>package api

import (
	"log"
	"net/http"
	"time"

	//"github.com/MyHomeworkSpace/api-server/auth"

	"github.com/labstack/echo"
)

// structs for data
type PlannerAnnouncement struct {
	ID int `json:"id"`
	Date string `json:"date"`
	Text string `json:"text"`
}

type PlannerFriday struct {
	ID int `json:"id"`
	Date string `json:"date"`
	Index int `json:"index"`
}

// responses
type MultiPlannerAnnouncementResponse struct {
	Status string `json:"status"`
	Announcements []PlannerAnnouncement `json:"announcements"`
}

type PlannerFridayResponse struct {
	Status string `json:"status"`
	Friday PlannerFriday `json:"friday"`
}

func InitPlannerAPI(e *echo.Echo) {
	e.GET("/planner/announcements/getWeek/:date", func(c echo.Context) error {
		startDate, err := time.Parse("2006-01-02", c.Param("date"))
		if err != nil {
			jsonResp := ErrorResponse{"error", "Invalid date."}
			return c.JSON(http.StatusBadRequest, jsonResp)
		}
		endDate := startDate.Add(time.Hour * 24 * 7)
		rows, err := DB.Query("SELECT id, date, text FROM announcements WHERE date >= ? AND date <= ?", startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
		if err != nil {
			log.Println("Error while getting announcement information: ")
			log.Println(err)
			jsonResp := StatusResponse{"error"}
			return c.JSON(http.StatusInternalServerError, jsonResp)
		}
		defer rows.Close()
		announcements := []PlannerAnnouncement{}
		for rows.Next() {
			resp := PlannerAnnouncement{-1, "", ""}
			rows.Scan(&resp.ID, &resp.Date, &resp.Text)
			announcements = append(announcements, resp)
		}
		jsonResp := MultiPlannerAnnouncementResponse{"ok", announcements}
		return c.JSON(http.StatusOK, jsonResp)
	})

	e.GET("/planner/fridays/get/:date", func(c echo.Context) error {
		rows, err := DB.Query("SELECT * FROM fridays WHERE date = ?", c.Param("date"))
		if err != nil {
			log.Println("Error while getting friday information: ")
			log.Println(err)
			jsonResp := StatusResponse{"error"}
			return c.JSON(http.StatusInternalServerError, jsonResp)
		}
		defer rows.Close()
		if rows.Next() {
			resp := PlannerFriday{-1, "", -1}
			rows.Scan(&resp.ID, &resp.Date, &resp.Index)
			jsonResp := PlannerFridayResponse{"ok", resp}
			return c.JSON(http.StatusOK, jsonResp)
		} else {
			jsonResp := StatusResponse{"ok"}
			return c.JSON(http.StatusOK, jsonResp)
		}
	})
}
<commit_msg>fix bug where wrong announcements would be displayed for a week<commit_after>package api

import (
	"log"
	"net/http"
	"time"

	//"github.com/MyHomeworkSpace/api-server/auth"

	"github.com/labstack/echo"
)

// structs for data
type PlannerAnnouncement struct {
	ID int `json:"id"`
	Date string `json:"date"`
	Text string `json:"text"`
}

type PlannerFriday struct {
	ID int `json:"id"`
	Date string `json:"date"`
	Index int `json:"index"`
}

// responses
type MultiPlannerAnnouncementResponse struct {
	Status string `json:"status"`
	Announcements []PlannerAnnouncement `json:"announcements"`
}

type PlannerFridayResponse struct {
	Status string `json:"status"`
	Friday PlannerFriday `json:"friday"`
}

func InitPlannerAPI(e *echo.Echo) {
	e.GET("/planner/announcements/getWeek/:date", func(c echo.Context) error {
		startDate, err := time.Parse("2006-01-02", c.Param("date"))
		if err != nil {
			jsonResp := ErrorResponse{"error", "Invalid date."}
			return c.JSON(http.StatusBadRequest, jsonResp)
		}
		endDate := startDate.Add(time.Hour * 24 * 7)
		rows, err := DB.Query("SELECT id, date, text FROM announcements WHERE date >= ? AND date < ?", startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
		if err != nil {
			log.Println("Error while getting announcement information: ")
			log.Println(err)
			jsonResp := StatusResponse{"error"}
			return c.JSON(http.StatusInternalServerError, jsonResp)
		}
		defer rows.Close()
		announcements := []PlannerAnnouncement{}
		for rows.Next() {
			resp := PlannerAnnouncement{-1, "", ""}
			rows.Scan(&resp.ID, &resp.Date, &resp.Text)
			announcements = append(announcements, resp)
		}
		jsonResp := MultiPlannerAnnouncementResponse{"ok", announcements}
		return c.JSON(http.StatusOK, jsonResp)
	})

	e.GET("/planner/fridays/get/:date", func(c echo.Context) error {
		rows, err := DB.Query("SELECT * FROM fridays WHERE date = ?", c.Param("date"))
		if err != nil {
			log.Println("Error while getting friday information: ")
			log.Println(err)
			jsonResp := StatusResponse{"error"}
			return c.JSON(http.StatusInternalServerError, jsonResp)
		}
		defer rows.Close()
		if rows.Next() {
			resp := PlannerFriday{-1, "", -1}
			rows.Scan(&resp.ID, &resp.Date, &resp.Index)
			jsonResp := PlannerFridayResponse{"ok", resp}
			return c.JSON(http.StatusOK, jsonResp)
		} else {
			jsonResp := StatusResponse{"ok"}
			return c.JSON(http.StatusOK, jsonResp)
		}
	})
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/bridge"
	"github.com/FederationOfFathers/dashboard/streams"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/uber-go/zap"
)

func init() {
	Router.Path("/api/v0/streams").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			enc := json.NewEncoder(w)
			enc.Encode(streams.Streams)
		},
	))

	Router.Path("/api/v0/streams/{memberID}/{type}").Methods("DELETE").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getSlackUserID(r)
			admin, _ := bridge.Data.Slack.IsUserIDAdmin(id)
			member, err := DB.MemberBySlackID(mux.Vars(r)["memberID"])
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if strings.ToLower(member.Slack) != strings.ToLower(id) && !admin {
				http.NotFound(w, r)
				return
			}
			stream, err := DB.StreamByMemberID(member.ID)
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if err := streams.Remove(stream.ID, mux.Vars(r)["type"]); err != nil {
				logger.Error("Error removing stream", zap.String("uri", r.URL.RawPath), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
			}
		},
	))

	Router.Path("/api/v0/streams/{memberID}").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err == gorm.ErrRecordNotFound {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			stream, err := DB.StreamByMemberID(member.ID)
			if err == gorm.ErrRecordNotFound {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			json.NewEncoder(w).Encode(stream)
		},
	))

	streamSetHandler := jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			defer r.Body.Close()
			var kind string
			var id string
			var userID string
			if strings.Contains(strings.ToLower(r.Header.Get("Content-Type")), "json") {
				var form = struct {
					Kind   string `json:"kind"`
					ID     string `json:"id"`
					UserID string `json:"userID"`
				}{}
				err := json.NewDecoder(r.Body).Decode(&form)
				if err != nil {
					logger.Error("Error decoding JSON", zap.String("uri", r.URL.RawPath), zap.Error(err))
				}
				kind = form.Kind
				id = form.ID
				userID = form.UserID
			} else {
				kind = r.FormValue("kind")
				id = r.FormValue("id")
				userID = r.FormValue("userID")
			}
			w.Header().Set("Content-Type", "application/json")
			if userID == "" {
				userID = getSlackUserID(r)
			}
			err := streams.Add(kind, id, userID)
			if err != nil {
				logger.Error(
					"Error adding stream",
					zap.String("uri", r.URL.RawPath),
					zap.String("kind", kind),
					zap.String("id", id),
					zap.String("userID", userID),
					zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
			}
		},
	)
	Router.Path("/api/v0/streams").Methods("PUT").Handler(streamSetHandler)
	Router.Path("/api/v0/streams").Methods("POST").Handler(streamSetHandler)
}
<commit_msg>logging.. to make sure of some things...<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/bridge"
	"github.com/FederationOfFathers/dashboard/streams"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/uber-go/zap"
)

func init() {
	Router.Path("/api/v0/streams").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			enc := json.NewEncoder(w)
			enc.Encode(streams.Streams)
		},
	))

	Router.Path("/api/v0/streams/{memberID}/{type}").Methods("DELETE").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getSlackUserID(r)
			admin, _ := bridge.Data.Slack.IsUserIDAdmin(id)
			member, err := DB.MemberBySlackID(mux.Vars(r)["memberID"])
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if strings.ToLower(member.Slack) != strings.ToLower(id) && !admin {
				http.NotFound(w, r)
				return
			}
			stream, err := DB.StreamByMemberID(member.ID)
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if err := streams.Remove(stream.ID, mux.Vars(r)["type"]); err != nil {
				logger.Error("Error removing stream", zap.String("uri", r.URL.RawPath), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
			}
		},
	))

	Router.Path("/api/v0/streams/{memberID}").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err == gorm.ErrRecordNotFound {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			stream, err := DB.StreamByMemberID(member.ID)
			if err == gorm.ErrRecordNotFound {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			json.NewEncoder(w).Encode(stream)
		},
	))

	streamSetHandler := jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			defer r.Body.Close()
			var kind string
			var id string
			var userID string
			if strings.Contains(strings.ToLower(r.Header.Get("Content-Type")), "json") {
				var form = struct {
					Kind   string `json:"kind"`
					ID     string `json:"id"`
					UserID string `json:"userID"`
				}{}
				err := json.NewDecoder(r.Body).Decode(&form)
				if err != nil {
					logger.Error("Error decoding JSON", zap.String("uri", r.URL.RawPath), zap.Error(err))
				}
				kind = form.Kind
				id = form.ID
				userID = form.UserID
			} else {
				kind = r.FormValue("kind")
				id = r.FormValue("id")
				userID = r.FormValue("userID")
			}
			w.Header().Set("Content-Type", "application/json")
			if userID == "" {
				userID = getSlackUserID(r)
			}

			sid := getSlackUserID(r)
			admin, _ := bridge.Data.Slack.IsUserIDAdmin(id)

			logger.Info(
				"streamSetHandler",
				zap.String("kind", kind),
				zap.String("id", id),
				zap.String("userID", userID),
				zap.String("sid", sid),
				zap.Bool("admin", admin))

			err := streams.Add(kind, id, userID)
			if err != nil {
				logger.Error(
					"Error adding stream",
					zap.String("uri", r.URL.RawPath),
					zap.String("kind", kind),
					zap.String("id", id),
					zap.String("userID", userID),
					zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
			}
		},
	)
	Router.Path("/api/v0/streams").Methods("PUT").Handler(streamSetHandler)
	Router.Path("/api/v0/streams").Methods("POST").Handler(streamSetHandler)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"log"
	"os"
)

var (
	// If true, output the verbose information.
	Debug   bool
	_logger *logger
)

func init() {
	_logger = &logger{l: log.New(os.Stderr, "[TCP Server] ", log.LstdFlags)}
}

type logger struct {
	l *log.Logger
}

func (l logger) Debug(format string, args ...interface{}) {
	l.Output(10, format, args...)
}

func (l logger) Output(level int, format string, args ...interface{}) {
	if !Debug {
		return
	}

	var prefix string
	if level <= 10 {
		prefix = "Debug"
	} else if level <= 20 {
		prefix = "Info"
	} else if level <= 30 {
		prefix = "Warning"
	} else if level <= 40 {
		prefix = "Error"
	}

	f := fmt.Sprintf("[%v] %v\n", prefix, format)
	fmt.Printf(f, args...)
}

func (l logger) Info(format string, args ...interface{}) {
	l.Output(20, format, args...)
}

func (l logger) Warn(format string, args ...interface{}) {
	l.Output(30, format, args...)
}

func (l logger) Error(format string, args ...interface{}) {
	l.Output(40, format, args...)
}

func SetLogger(logger *log.Logger) {
	_logger.l = logger
}

func GetLogger() *log.Logger {
	return _logger.l
}
<commit_msg>Fix a buf of the logger in server<commit_after>package server

import (
	"fmt"
	"log"
	"os"
)

var (
	// If true, output the verbose information.
	Debug   bool
	_logger *logger
)

func init() {
	_logger = &logger{_logger: log.New(os.Stderr, "[TCP Server] ", log.LstdFlags)}
}

type logger struct {
	_logger *log.Logger
}

func (l logger) Debug(format string, args ...interface{}) {
	l.Output(10, format, args...)
}

func (l logger) Output(level int, format string, args ...interface{}) {
	if !Debug {
		return
	}

	var prefix string
	if level <= 10 {
		prefix = "Debug"
	} else if level <= 20 {
		prefix = "Info"
	} else if level <= 30 {
		prefix = "Warning"
	} else if level <= 40 {
		prefix = "Error"
	}

	f := fmt.Sprintf("[%v] %v\n", prefix, format)
	l._logger.Printf(f, args...)
}

func (l logger) Info(format string, args ...interface{}) {
	l.Output(20, format, args...)
}

func (l logger) Warn(format string, args ...interface{}) {
	l.Output(30, format, args...)
}

func (l logger) Error(format string, args ...interface{}) {
	l.Output(40, format, args...)
}

func SetLogger(logger *log.Logger) {
	_logger._logger = logger
}

func GetLogger() *log.Logger {
	return _logger._logger
}
<|endoftext|>


<commit_before>package main

import (
	"time"
)

func ping(ch chan int) {
	for {
		ball := <-ch
		ball++
		print(ball)
		ch <- ball
	}
}
func pong(ch chan int) {
	for {
		ball := <-ch
		ball--
		print(ball)
		ch <- ball
	}
}

func main() {
	ch := make(chan int)
	go ping(ch)
	go pong(ch)
	ch <- 1
	time.Sleep(time.Second)
}
<commit_msg>sync Fri May  3 01:54:21 CEST 2019<commit_after>package main

import (
	"time"
)

func main() {
	ch := make(chan int)
	go player(ch)
	go player(ch)

	ch <- 1
	time.Sleep(time.Minute)
}

func player(ch chan int) {
	for {
		<-ch
		println("ping")
		time.Sleep(time.Second)
		ch <- 1
		println("pong\n")
	}
}
<|endoftext|>