<commit_before>package terraform

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/config"
)

// EvalApply is an EvalNode implementation that writes the diff to
// the full diff.
type EvalApply struct {
	Info      *InstanceInfo
	State     **InstanceState
	Diff      **InstanceDiff
	Provider  *ResourceProvider
	Output    **InstanceState
	CreateNew *bool
	Error     *error
}

// TODO: test
func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
	diff := *n.Diff
	provider := *n.Provider
	state := *n.State

	// If we have no diff, we have nothing to do!
	if diff.Empty() {
		log.Printf(
			"[DEBUG] apply: %s: diff is empty, doing nothing.", n.Info.Id)
		return nil, nil
	}

	// Remove any output values from the diff
	for k, ad := range diff.CopyAttributes() {
		if ad.Type == DiffAttrOutput {
			diff.DelAttribute(k)
		}
	}

	// If the state is nil, make it non-nil
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	// Flag if we're creating a new instance
	if n.CreateNew != nil {
		*n.CreateNew = state.ID == "" && !diff.GetDestroy() || diff.RequiresNew()
	}

	// With the completed diff, apply!
	log.Printf("[DEBUG] apply: %s: executing Apply", n.Info.Id)
	state, err := provider.Apply(n.Info, state, diff)
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	// Force the "id" attribute to be our ID
	if state.ID != "" {
		state.Attributes["id"] = state.ID
	}

	// If the value is the unknown variable value, then it is an error.
	// In this case we record the error and remove it from the state
	for ak, av := range state.Attributes {
		if av == config.UnknownVariableValue {
			err = multierror.Append(err, fmt.Errorf(
				"Attribute with unknown value: %s", ak))
			delete(state.Attributes, ak)
		}
	}

	// Write the final state
	if n.Output != nil {
		*n.Output = state
	}

	// If there are no errors, then we append it to our output error
	// if we have one, otherwise we just output it.
	if err != nil {
		if n.Error != nil {
			helpfulErr := fmt.Errorf("%s: %s", n.Info.Id, err.Error())
			*n.Error = multierror.Append(*n.Error, helpfulErr)
		} else {
			return nil, err
		}
	}

	return nil, nil
}

// EvalApplyPre is an EvalNode implementation that does the pre-Apply work
type EvalApplyPre struct {
	Info  *InstanceInfo
	State **InstanceState
	Diff  **InstanceDiff
}

// TODO: test
func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State
	diff := *n.Diff

	// If the state is nil, make it non-nil
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	{
		// Call post-apply hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PreApply(n.Info, state, diff)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, nil
}

// EvalApplyPost is an EvalNode implementation that does the post-Apply work
type EvalApplyPost struct {
	Info  *InstanceInfo
	State **InstanceState
	Error *error
}

// TODO: test
func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	{
		// Call post-apply hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostApply(n.Info, state, *n.Error)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, *n.Error
}

// EvalApplyProvisioners is an EvalNode implementation that executes
// the provisioners for a resource.
//
// TODO(mitchellh): This should probably be split up into a more fine-grained
// ApplyProvisioner (single) that is looped over.
type EvalApplyProvisioners struct {
	Info           *InstanceInfo
	State          **InstanceState
	Resource       *config.Resource
	InterpResource *Resource
	CreateNew      *bool
	Error          *error

	// When is the type of provisioner to run at this point
	When config.ProvisionerWhen
}

// TODO: test
func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	if n.CreateNew != nil && !*n.CreateNew {
		// If we're not creating a new resource, then don't run provisioners
		return nil, nil
	}

	provs := n.filterProvisioners()
	if len(provs) == 0 {
		// We have no provisioners, so don't do anything
		return nil, nil
	}

	// taint tells us whether to enable tainting.
	taint := n.When == config.ProvisionerWhenCreate

	if n.Error != nil && *n.Error != nil {
		if taint {
			state.Tainted = true
		}

		// We're already tainted, so just return out
		return nil, nil
	}

	{
		// Call pre hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PreProvisionResource(n.Info, state)
		})
		if err != nil {
			return nil, err
		}
	}

	// If there are no errors, then we append it to our output error
	// if we have one, otherwise we just output it.
	err := n.apply(ctx, provs)
	if err != nil {
		if taint {
			state.Tainted = true
		}

		if n.Error != nil {
			*n.Error = multierror.Append(*n.Error, err)
		} else {
			return nil, err
		}
	}

	{
		// Call post hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostProvisionResource(n.Info, state)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, nil
}

// filterProvisioners filters the provisioners on the resource to only
// the provisioners specified by the "when" option.
func (n *EvalApplyProvisioners) filterProvisioners() []*config.Provisioner {
	// Fast path the zero case
	if n.Resource == nil {
		return nil
	}

	if len(n.Resource.Provisioners) == 0 {
		return nil
	}

	result := make([]*config.Provisioner, 0, len(n.Resource.Provisioners))
	for _, p := range n.Resource.Provisioners {
		if p.When == n.When {
			result = append(result, p)
		}
	}

	return result
}

func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*config.Provisioner) error {
	state := *n.State

	// Store the original connection info, restore later
	origConnInfo := state.Ephemeral.ConnInfo
	defer func() {
		state.Ephemeral.ConnInfo = origConnInfo
	}()

	for _, prov := range provs {
		// Get the provisioner
		provisioner := ctx.Provisioner(prov.Type)

		// Interpolate the provisioner config
		provConfig, err := ctx.Interpolate(prov.RawConfig.Copy(), n.InterpResource)
		if err != nil {
			return err
		}

		// Interpolate the conn info, since it may contain variables
		connInfo, err := ctx.Interpolate(prov.ConnInfo.Copy(), n.InterpResource)
		if err != nil {
			return err
		}

		// Merge the connection information
		overlay := make(map[string]string)
		if origConnInfo != nil {
			for k, v := range origConnInfo {
				overlay[k] = v
			}
		}
		for k, v := range connInfo.Config {
			switch vt := v.(type) {
			case string:
				overlay[k] = vt
			case int64:
				overlay[k] = strconv.FormatInt(vt, 10)
			case int32:
				overlay[k] = strconv.FormatInt(int64(vt), 10)
			case int:
				overlay[k] = strconv.FormatInt(int64(vt), 10)
			case float32:
				overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32)
			case float64:
				overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64)
			case bool:
				overlay[k] = strconv.FormatBool(vt)
			default:
				overlay[k] = fmt.Sprintf("%v", vt)
			}
		}
		state.Ephemeral.ConnInfo = overlay

		{
			// Call pre hook
			err := ctx.Hook(func(h Hook) (HookAction, error) {
				return h.PreProvision(n.Info, prov.Type)
			})
			if err != nil {
				return err
			}
		}

		// The output function
		outputFn := func(msg string) {
			ctx.Hook(func(h Hook) (HookAction, error) {
				h.ProvisionOutput(n.Info, prov.Type, msg)
				return HookActionContinue, nil
			})
		}

		// Invoke the Provisioner
		output := CallbackUIOutput{OutputFn: outputFn}
		applyErr := provisioner.Apply(&output, state, provConfig)

		// Call post hook
		hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostProvision(n.Info, prov.Type, applyErr)
		})

		// Handle the error before we deal with the hook
		if applyErr != nil {
			// Determine failure behavior
			switch prov.OnFailure {
			case config.ProvisionerOnFailureContinue:
				log.Printf(
					"[INFO] apply: %s [%s]: error during provision, continue requested",
					n.Info.Id, prov.Type)

			case config.ProvisionerOnFailureFail:
				return applyErr
			}
		}

		// Deal with the hook
		if hookErr != nil {
			return hookErr
		}
	}

	return nil

}
<commit_msg>core: don't advertise data source destroy via hooks<commit_after>package terraform

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/config"
)

// EvalApply is an EvalNode implementation that writes the diff to
// the full diff.
type EvalApply struct {
	Info      *InstanceInfo
	State     **InstanceState
	Diff      **InstanceDiff
	Provider  *ResourceProvider
	Output    **InstanceState
	CreateNew *bool
	Error     *error
}

// TODO: test
func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
	diff := *n.Diff
	provider := *n.Provider
	state := *n.State

	// If we have no diff, we have nothing to do!
	if diff.Empty() {
		log.Printf(
			"[DEBUG] apply: %s: diff is empty, doing nothing.", n.Info.Id)
		return nil, nil
	}

	// Remove any output values from the diff
	for k, ad := range diff.CopyAttributes() {
		if ad.Type == DiffAttrOutput {
			diff.DelAttribute(k)
		}
	}

	// If the state is nil, make it non-nil
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	// Flag if we're creating a new instance
	if n.CreateNew != nil {
		*n.CreateNew = state.ID == "" && !diff.GetDestroy() || diff.RequiresNew()
	}

	// With the completed diff, apply!
	log.Printf("[DEBUG] apply: %s: executing Apply", n.Info.Id)
	state, err := provider.Apply(n.Info, state, diff)
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	// Force the "id" attribute to be our ID
	if state.ID != "" {
		state.Attributes["id"] = state.ID
	}

	// If the value is the unknown variable value, then it is an error.
	// In this case we record the error and remove it from the state
	for ak, av := range state.Attributes {
		if av == config.UnknownVariableValue {
			err = multierror.Append(err, fmt.Errorf(
				"Attribute with unknown value: %s", ak))
			delete(state.Attributes, ak)
		}
	}

	// Write the final state
	if n.Output != nil {
		*n.Output = state
	}

	// If there are no errors, then we append it to our output error
	// if we have one, otherwise we just output it.
	if err != nil {
		if n.Error != nil {
			helpfulErr := fmt.Errorf("%s: %s", n.Info.Id, err.Error())
			*n.Error = multierror.Append(*n.Error, helpfulErr)
		} else {
			return nil, err
		}
	}

	return nil, nil
}

// EvalApplyPre is an EvalNode implementation that does the pre-Apply work
type EvalApplyPre struct {
	Info  *InstanceInfo
	State **InstanceState
	Diff  **InstanceDiff
}

// TODO: test
func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State
	diff := *n.Diff

	// If the state is nil, make it non-nil
	if state == nil {
		state = new(InstanceState)
	}
	state.init()

	if resourceHasUserVisibleApply(n.Info) {
		// Call post-apply hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PreApply(n.Info, state, diff)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, nil
}

// EvalApplyPost is an EvalNode implementation that does the post-Apply work
type EvalApplyPost struct {
	Info  *InstanceInfo
	State **InstanceState
	Error *error
}

// TODO: test
func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	if resourceHasUserVisibleApply(n.Info) {
		// Call post-apply hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostApply(n.Info, state, *n.Error)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, *n.Error
}

// resourceHasUserVisibleApply returns true if the given resource is one where
// apply actions should be exposed to the user.
//
// Certain resources do apply actions only as an implementation detail, so
// these should not be advertised to code outside of this package.
func resourceHasUserVisibleApply(info *InstanceInfo) bool {
	addr := info.ResourceAddress()

	// Only managed resources have user-visible apply actions.
	// In particular, this excludes data resources since we "apply" these
	// only as an implementation detail of removing them from state when
	// they are destroyed. (When reading, they don't get here at all because
	// we present them as "Refresh" actions.)
	return addr.Mode == config.ManagedResourceMode
}

// EvalApplyProvisioners is an EvalNode implementation that executes
// the provisioners for a resource.
//
// TODO(mitchellh): This should probably be split up into a more fine-grained
// ApplyProvisioner (single) that is looped over.
type EvalApplyProvisioners struct {
	Info           *InstanceInfo
	State          **InstanceState
	Resource       *config.Resource
	InterpResource *Resource
	CreateNew      *bool
	Error          *error

	// When is the type of provisioner to run at this point
	When config.ProvisionerWhen
}

// TODO: test
func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	if n.CreateNew != nil && !*n.CreateNew {
		// If we're not creating a new resource, then don't run provisioners
		return nil, nil
	}

	provs := n.filterProvisioners()
	if len(provs) == 0 {
		// We have no provisioners, so don't do anything
		return nil, nil
	}

	// taint tells us whether to enable tainting.
	taint := n.When == config.ProvisionerWhenCreate

	if n.Error != nil && *n.Error != nil {
		if taint {
			state.Tainted = true
		}

		// We're already tainted, so just return out
		return nil, nil
	}

	{
		// Call pre hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PreProvisionResource(n.Info, state)
		})
		if err != nil {
			return nil, err
		}
	}

	// If there are no errors, then we append it to our output error
	// if we have one, otherwise we just output it.
	err := n.apply(ctx, provs)
	if err != nil {
		if taint {
			state.Tainted = true
		}

		if n.Error != nil {
			*n.Error = multierror.Append(*n.Error, err)
		} else {
			return nil, err
		}
	}

	{
		// Call post hook
		err := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostProvisionResource(n.Info, state)
		})
		if err != nil {
			return nil, err
		}
	}

	return nil, nil
}

// filterProvisioners filters the provisioners on the resource to only
// the provisioners specified by the "when" option.
func (n *EvalApplyProvisioners) filterProvisioners() []*config.Provisioner {
	// Fast path the zero case
	if n.Resource == nil {
		return nil
	}

	if len(n.Resource.Provisioners) == 0 {
		return nil
	}

	result := make([]*config.Provisioner, 0, len(n.Resource.Provisioners))
	for _, p := range n.Resource.Provisioners {
		if p.When == n.When {
			result = append(result, p)
		}
	}

	return result
}

func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*config.Provisioner) error {
	state := *n.State

	// Store the original connection info, restore later
	origConnInfo := state.Ephemeral.ConnInfo
	defer func() {
		state.Ephemeral.ConnInfo = origConnInfo
	}()

	for _, prov := range provs {
		// Get the provisioner
		provisioner := ctx.Provisioner(prov.Type)

		// Interpolate the provisioner config
		provConfig, err := ctx.Interpolate(prov.RawConfig.Copy(), n.InterpResource)
		if err != nil {
			return err
		}

		// Interpolate the conn info, since it may contain variables
		connInfo, err := ctx.Interpolate(prov.ConnInfo.Copy(), n.InterpResource)
		if err != nil {
			return err
		}

		// Merge the connection information
		overlay := make(map[string]string)
		if origConnInfo != nil {
			for k, v := range origConnInfo {
				overlay[k] = v
			}
		}
		for k, v := range connInfo.Config {
			switch vt := v.(type) {
			case string:
				overlay[k] = vt
			case int64:
				overlay[k] = strconv.FormatInt(vt, 10)
			case int32:
				overlay[k] = strconv.FormatInt(int64(vt), 10)
			case int:
				overlay[k] = strconv.FormatInt(int64(vt), 10)
			case float32:
				overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32)
			case float64:
				overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64)
			case bool:
				overlay[k] = strconv.FormatBool(vt)
			default:
				overlay[k] = fmt.Sprintf("%v", vt)
			}
		}
		state.Ephemeral.ConnInfo = overlay

		{
			// Call pre hook
			err := ctx.Hook(func(h Hook) (HookAction, error) {
				return h.PreProvision(n.Info, prov.Type)
			})
			if err != nil {
				return err
			}
		}

		// The output function
		outputFn := func(msg string) {
			ctx.Hook(func(h Hook) (HookAction, error) {
				h.ProvisionOutput(n.Info, prov.Type, msg)
				return HookActionContinue, nil
			})
		}

		// Invoke the Provisioner
		output := CallbackUIOutput{OutputFn: outputFn}
		applyErr := provisioner.Apply(&output, state, provConfig)

		// Call post hook
		hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
			return h.PostProvision(n.Info, prov.Type, applyErr)
		})

		// Handle the error before we deal with the hook
		if applyErr != nil {
			// Determine failure behavior
			switch prov.OnFailure {
			case config.ProvisionerOnFailureContinue:
				log.Printf(
					"[INFO] apply: %s [%s]: error during provision, continue requested",
					n.Info.Id, prov.Type)

			case config.ProvisionerOnFailureFail:
				return applyErr
			}
		}

		// Deal with the hook
		if hookErr != nil {
			return hookErr
		}
	}

	return nil

}
<|endoftext|>


<commit_before>package versions

import "strconv"

// Version is the ID of a Dwarf Fortress save version.
type Version uint32

// Versions is a map of all known save versions to the corresponding release's
// version number.
var Versions = map[Version]string{
	1107: "0.21.93.19a",
	1108: "0.21.93.19c",
	// => "0.21.95.19a",
	// => "0.21.95.19b",
	1110: "0.21.95.19c",
	// => "0.21.100.19a",
	1113: "0.21.101.19a",
	1114: "0.21.101.19d",
	1117: "0.21.102.19a",
	1119: "0.21.104.19b",
	1121: "0.21.104.19d",
	1123: "0.21.104.21a",
	1125: "0.21.104.21b",
	1128: "0.21.105.21a",
	// => "0.22.107.21a",
	1134: "0.22.110.22e",
	1137: "0.22.110.22f",
	1148: "0.22.110.23a",
	1151: "0.22.120.23a",
	1161: "0.22.121.23b",
	1165: "0.22.123.23a",
	1169: "0.23.130.23a",

	1205: "0.27.169.32a",
	1206: "0.27.169.33a",
	1209: "0.27.169.33b",
	1211: "0.27.169.33c",
	1212: "0.27.169.33d",
	1213: "0.27.169.33e",
	1215: "0.27.169.33f",
	1216: "0.27.169.33g",
	1223: "0.27.173.38a",
	1231: "0.27.176.38a",
	1234: "0.27.176.38b",
	1235: "0.27.176.38c",
	1254: "0.28.181.39a",
	1255: "0.28.181.39b",
	1256: "0.28.181.39c",
	1259: "0.28.181.39d",
	1260: "0.28.181.39e",
	1261: "0.28.181.39f",
	1265: "0.28.181.40a",
	1266: "0.28.181.40b",
	1267: "0.28.181.40c",
	1268: "0.28.181.40d",

	1287: "0.31.01",
	1288: "0.31.02",
	1289: "0.31.03",
	1292: "0.31.04",
	1295: "0.31.05",
	1297: "0.31.06",
	1300: "0.31.08",
	1304: "0.31.09",
	1305: "0.31.10",
	1310: "0.31.11",
	1311: "0.31.12",
	1323: "0.31.13",
	1325: "0.31.14",
	1326: "0.31.15",
	1327: "0.31.16",
	1340: "0.31.17",
	1341: "0.31.18",
	1351: "0.31.19",
	1353: "0.31.20",
	1354: "0.31.21",
	1359: "0.31.22",
	1360: "0.31.23",
	1361: "0.31.24",
	1362: "0.31.25",

	1372: "0.34.01",
	1374: "0.34.02",
	1376: "0.34.03",
	1377: "0.34.04",
	1378: "0.34.05",
	1382: "0.34.06",
	1383: "0.34.07",
	1400: "0.34.08",
	1402: "0.34.09",
	1403: "0.34.10",
	1404: "0.34.11",

	1441: "0.40.01",
	1442: "0.40.02",
	1443: "0.40.03",
	1444: "0.40.04",
	1445: "0.40.05",
	1446: "0.40.06",
	1448: "0.40.07",
	1449: "0.40.08",
	1451: "0.40.09",
	1452: "0.40.10",
	1456: "0.40.11",
	1459: "0.40.12",
	1462: "0.40.13",
	1469: "0.40.14",
	1470: "0.40.15",
	1471: "0.40.16",
	1472: "0.40.17",
	1473: "0.40.18",
	1474: "0.40.19",
	1477: "0.40.20",
	1478: "0.40.21",
	1479: "0.40.22",
	1480: "0.40.23",
	1481: "0.40.24",

	1531: "0.42.01",
	1532: "0.42.02",
	1533: "0.42.03",
	1534: "0.42.04",
}

// IsKnown returns true if v is a known version number.
func (v Version) IsKnown() bool {
	_, ok := Versions[v]
	return ok
}

// String implements fmt.Stringer.
func (v Version) String() string {
	if s, ok := Versions[v]; ok {
		return s
	}

	return strconv.Itoa(int(v))
}
<commit_msg>add 0.42.05 and 0.42.06 to versions<commit_after>package versions

import "strconv"

// Version is the ID of a Dwarf Fortress save version.
type Version uint32

// Versions is a map of all known save versions to the corresponding release's
// version number.
var Versions = map[Version]string{
	1107: "0.21.93.19a",
	1108: "0.21.93.19c",
	// => "0.21.95.19a",
	// => "0.21.95.19b",
	1110: "0.21.95.19c",
	// => "0.21.100.19a",
	1113: "0.21.101.19a",
	1114: "0.21.101.19d",
	1117: "0.21.102.19a",
	1119: "0.21.104.19b",
	1121: "0.21.104.19d",
	1123: "0.21.104.21a",
	1125: "0.21.104.21b",
	1128: "0.21.105.21a",
	// => "0.22.107.21a",
	1134: "0.22.110.22e",
	1137: "0.22.110.22f",
	1148: "0.22.110.23a",
	1151: "0.22.120.23a",
	1161: "0.22.121.23b",
	1165: "0.22.123.23a",
	1169: "0.23.130.23a",

	1205: "0.27.169.32a",
	1206: "0.27.169.33a",
	1209: "0.27.169.33b",
	1211: "0.27.169.33c",
	1212: "0.27.169.33d",
	1213: "0.27.169.33e",
	1215: "0.27.169.33f",
	1216: "0.27.169.33g",
	1223: "0.27.173.38a",
	1231: "0.27.176.38a",
	1234: "0.27.176.38b",
	1235: "0.27.176.38c",
	1254: "0.28.181.39a",
	1255: "0.28.181.39b",
	1256: "0.28.181.39c",
	1259: "0.28.181.39d",
	1260: "0.28.181.39e",
	1261: "0.28.181.39f",
	1265: "0.28.181.40a",
	1266: "0.28.181.40b",
	1267: "0.28.181.40c",
	1268: "0.28.181.40d",

	1287: "0.31.01",
	1288: "0.31.02",
	1289: "0.31.03",
	1292: "0.31.04",
	1295: "0.31.05",
	1297: "0.31.06",
	1300: "0.31.08",
	1304: "0.31.09",
	1305: "0.31.10",
	1310: "0.31.11",
	1311: "0.31.12",
	1323: "0.31.13",
	1325: "0.31.14",
	1326: "0.31.15",
	1327: "0.31.16",
	1340: "0.31.17",
	1341: "0.31.18",
	1351: "0.31.19",
	1353: "0.31.20",
	1354: "0.31.21",
	1359: "0.31.22",
	1360: "0.31.23",
	1361: "0.31.24",
	1362: "0.31.25",

	1372: "0.34.01",
	1374: "0.34.02",
	1376: "0.34.03",
	1377: "0.34.04",
	1378: "0.34.05",
	1382: "0.34.06",
	1383: "0.34.07",
	1400: "0.34.08",
	1402: "0.34.09",
	1403: "0.34.10",
	1404: "0.34.11",

	1441: "0.40.01",
	1442: "0.40.02",
	1443: "0.40.03",
	1444: "0.40.04",
	1445: "0.40.05",
	1446: "0.40.06",
	1448: "0.40.07",
	1449: "0.40.08",
	1451: "0.40.09",
	1452: "0.40.10",
	1456: "0.40.11",
	1459: "0.40.12",
	1462: "0.40.13",
	1469: "0.40.14",
	1470: "0.40.15",
	1471: "0.40.16",
	1472: "0.40.17",
	1473: "0.40.18",
	1474: "0.40.19",
	1477: "0.40.20",
	1478: "0.40.21",
	1479: "0.40.22",
	1480: "0.40.23",
	1481: "0.40.24",

	1531: "0.42.01",
	1532: "0.42.02",
	1533: "0.42.03",
	1534: "0.42.04",
	1537: "0.42.05",
	1542: "0.42.06",
}

// IsKnown returns true if v is a known version number.
func (v Version) IsKnown() bool {
	_, ok := Versions[v]
	return ok
}

// String implements fmt.Stringer.
func (v Version) String() string {
	if s, ok := Versions[v]; ok {
		return s
	}

	return strconv.Itoa(int(v))
}
<|endoftext|>


<commit_before>package fam100

import (
	"sort"
	"strconv"
	"time"

	"github.com/patrickmn/go-cache"
	"github.com/rcrowley/go-metrics"
	"github.com/uber-go/zap"
)

var (
	RoundDuration        = 90 * time.Second
	tickDuration         = 10 * time.Second
	DelayBetweenRound    = 5 * time.Second
	TickAfterWrongAnswer = false
	RoundPerGame         = 3
	DefaultQuestionLimit = 600
	log                  zap.Logger

	gameMsgProcessTimer = metrics.NewRegisteredTimer("game.processedMessage", metrics.DefaultRegistry)
	gameServiceTimer    = metrics.NewRegisteredTimer("game.serviceTimeNS", metrics.DefaultRegistry)
	playerActive        = metrics.NewRegisteredGauge("player.active", metrics.DefaultRegistry)
	playerActiveMap     = cache.New(5*time.Minute, 30*time.Second)
)

func init() {
	log = zap.NewJSON()
	go func() {
		for range time.Tick(30 * time.Second) {
			playerActive.Update(int64(playerActiveMap.ItemCount()))
		}
	}()
}

func SetLogger(l zap.Logger) {
	log = l.With(zap.String("module", "fam100"))
}

// Message to communicate between player and the game
type Message interface{}

// TextMessage represents a chat message
type TextMessage struct {
	ChanID     string
	Player     Player
	Text       string
	ReceivedAt time.Time
}

// StateMessage represents state change in the game
type StateMessage struct {
	ChanID    string
	Round     int
	State     State
	RoundText QNAMessage //question and answer
}

// TickMessage represents time left notification
type TickMessage struct {
	ChanID   string
	TimeLeft time.Duration
}

type WrongAnswerMessage TickMessage

// QNAMessage represents question and answer for a round
type QNAMessage struct {
	ChanID         string
	Round          int
	QuestionText   string
	QuestionID     int
	Answers        []roundAnswers
	ShowUnanswered bool // reveal un-answered question (end of round)
	TimeLeft       time.Duration
}

type roundAnswers struct {
	Text       string
	Score      int
	Answered   bool
	PlayerName string
	Highlight  bool
}
type RankMessage struct {
	ChanID string
	Round  int
	Rank   Rank
	Final  bool
}

// PlayerID is the player ID type
type PlayerID string

// Player of the game
type Player struct {
	ID   PlayerID
	Name string
}

// State represents state of the round
type State string

// Available state
const (
	Created       State = "created"
	Started       State = "started"
	Finished      State = "finished"
	RoundStarted  State = "roundStarted"
	RoundTimeout  State = "RoundTimeout"
	RoundFinished State = "roundFinished"
)

// Game can consists of multiple round
// each round user will be asked question and gain points
type Game struct {
	ChanID           string
	ChanName         string
	State            State
	TotalRoundPlayed int
	players          map[PlayerID]Player
	seed             int64
	rank             Rank
	currentRound     *round

	In  chan Message
	Out chan Message
}

// NewGame create a new round
func NewGame(chanID, chanName string, in, out chan Message) (r *Game, err error) {
	seed, totalRoundPlayed, err := DefaultDB.nextGame(chanID)
	if err != nil {
		return nil, err
	}

	return &Game{
		ChanID:           chanID,
		ChanName:         chanName,
		State:            Created,
		players:          make(map[PlayerID]Player),
		seed:             seed,
		TotalRoundPlayed: totalRoundPlayed,
		In:               in,
		Out:              out,
	}, err
}

// Start the game
func (g *Game) Start() {
	g.State = Started
	log.Info("Game started",
		zap.String("chanID", g.ChanID),
		zap.Int64("seed", g.seed),
		zap.Int("totalRoundPlayed", g.TotalRoundPlayed))

	go func() {
		g.Out <- StateMessage{ChanID: g.ChanID, State: Started}
		DefaultDB.incStats("game_started")
		DefaultDB.incChannelStats(g.ChanID, "game_started")
		for i := 1; i <= RoundPerGame; i++ {
			err := g.startRound(i)
			if err != nil {
				log.Error("starting round failed", zap.String("chanID", g.ChanID), zap.Error(err))
			}
			final := i == RoundPerGame
			g.Out <- RankMessage{ChanID: g.ChanID, Round: i, Rank: g.rank, Final: final}
			if !final {
				time.Sleep(DelayBetweenRound)
			}
		}
		DefaultDB.incStats("game_finished")
		DefaultDB.incChannelStats(g.ChanID, "game_finished")
		g.State = Finished
		g.Out <- StateMessage{ChanID: g.ChanID, State: Finished}
		log.Info("Game finished", zap.String("chanID", g.ChanID))
	}()
}

func (g *Game) startRound(currentRound int) error {
	g.TotalRoundPlayed++
	DefaultDB.incRoundPlayed(g.ChanID)

	questionLimit := DefaultQuestionLimit
	if limitConf, err := DefaultDB.ChannelConfig(g.ChanID, "questionLimit", ""); err == nil && limitConf != "" {
		if limit, err := strconv.ParseInt(limitConf, 10, 64); err == nil {
			questionLimit = int(limit)
		}
	}

	r, err := newRound(g.seed, g.TotalRoundPlayed, g.players, questionLimit)
	if err != nil {
		return err
	}
	DefaultDB.incStats("round_started")
	DefaultDB.incChannelStats(g.ChanID, "round_started")

	g.currentRound = r
	r.state = RoundStarted
	timeUp := time.After(RoundDuration)
	timeLeftTick := time.NewTicker(tickDuration)
	displayAnswerTick := time.NewTicker(tickDuration)

	// print question
	g.Out <- StateMessage{ChanID: g.ChanID, State: RoundStarted, Round: currentRound, RoundText: r.questionText(g.ChanID, false)}
	log.Info("Round Started", zap.String("chanID", g.ChanID), zap.Int("questionLimit", questionLimit))

	for {
		select {
		case rawMsg := <-g.In: // new answer coming from player
			started := time.Now()
			msg, ok := rawMsg.(TextMessage)
			if !ok {
				log.Error("Unexpected message type input from client")
				continue
			}

			handled := g.handleMessage(msg, r)
			if handled {
				gameMsgProcessTimer.UpdateSince(started)
				gameServiceTimer.UpdateSince(msg.ReceivedAt)
				continue
			}

			if r.finised() {
				timeLeftTick.Stop()
				displayAnswerTick.Stop()
				g.showAnswer(r)
				r.state = RoundFinished
				g.updateRanking(r.ranking())
				g.Out <- StateMessage{ChanID: g.ChanID, State: RoundFinished, Round: currentRound}
				log.Info("Round finished", zap.String("chanID", g.ChanID), zap.Bool("timeout", false))
				DefaultDB.incStats("round_finished")
				DefaultDB.incChannelStats(g.ChanID, "round_finished")
				gameMsgProcessTimer.UpdateSince(started)

				return nil
			}
			gameMsgProcessTimer.UpdateSince(started)
			gameServiceTimer.UpdateSince(msg.ReceivedAt)

		case <-timeLeftTick.C: // inform time left
			select {
			case g.Out <- TickMessage{ChanID: g.ChanID, TimeLeft: r.timeLeft()}:
			default:
			}

		case <-displayAnswerTick.C: // show correct answer (at most once every 10s)
			g.showAnswer(r)

		case <-timeUp: // time is up
			timeLeftTick.Stop()
			displayAnswerTick.Stop()
			g.State = RoundFinished
			g.updateRanking(r.ranking())
			g.Out <- StateMessage{ChanID: g.ChanID, State: RoundTimeout, Round: currentRound}
			log.Info("Round finished", zap.String("chanID", g.ChanID), zap.Bool("timeout", true))
			showUnAnswered := true
			g.Out <- r.questionText(g.ChanID, showUnAnswered)
			DefaultDB.incStats("round_timeout")
			DefaultDB.incChannelStats(g.ChanID, "round_timeout")

			return nil
		}
	}
}

func (g *Game) handleMessage(msg TextMessage, r *round) (handled bool) {
	playerActiveMap.Set(string(msg.Player.ID), struct{}{}, cache.DefaultExpiration)
	log.Debug("startRound got message", zap.String("chanID", g.ChanID), zap.Object("msg", msg))
	answer := msg.Text
	correct, alreadyAnswered, idx := r.answer(msg.Player, answer)
	if !correct {
		if TickAfterWrongAnswer {
			g.Out <- WrongAnswerMessage{ChanID: g.ChanID, TimeLeft: r.timeLeft()}
		}
		return true
	}
	if alreadyAnswered {
		log.Debug("already answered", zap.String("chanID", g.ChanID), zap.String("by", string(r.correct[idx])))
		return true
	}

	DefaultDB.incStats("answer_correct")
	DefaultDB.incChannelStats(g.ChanID, "answer_correct")
	DefaultDB.incPlayerStats(msg.Player.ID, "answer_correct")
	log.Info("answer correct",
		zap.String("playerID", string(msg.Player.ID)),
		zap.String("playerName", msg.Player.Name),
		zap.String("answer", answer),
		zap.Int("questionID", r.q.ID),
		zap.String("chanID", g.ChanID))

	return false
}

func (g *Game) updateRanking(r Rank) {
	g.rank = g.rank.Add(r)
	DefaultDB.saveScore(g.ChanID, g.ChanName, r)
}

func (g *Game) CurrentQuestion() Question {
	return g.currentRound.q
}

func (g *Game) showAnswer(r *round) {
	var show bool
	// if there is no highlighted answer don't display
	for _, v := range r.highlight {
		if v {
			show = true
			break
		}
	}
	if !show {
		return
	}

	qnaText := r.questionText(g.ChanID, false)
	select {
	case g.Out <- qnaText:
	default:
	}

	for i := range r.highlight {
		r.highlight[i] = false
	}
}

// round represents with one question
type round struct {
	q         Question
	state     State
	correct   []PlayerID // correct answer answered by a player, "" means not answered
	players   map[PlayerID]Player
	highlight map[int]bool

	endAt time.Time
}

func newRound(seed int64, totalRoundPlayed int, players map[PlayerID]Player, questionLimit int) (*round, error) {
	q, err := NextQuestion(seed, totalRoundPlayed, questionLimit)
	if err != nil {
		return nil, err
	}

	return &round{
		q:         q,
		correct:   make([]PlayerID, len(q.Answers)),
		state:     Created,
		players:   players,
		highlight: make(map[int]bool),
		endAt:     time.Now().Add(RoundDuration).Round(time.Second),
	}, nil
}

func (r *round) timeLeft() time.Duration {
	return r.endAt.Sub(time.Now().Round(time.Second))
}

// questionText construct QNAMessage which contains questions, answers and score
func (r *round) questionText(gameID string, showUnAnswered bool) QNAMessage {
	ras := make([]roundAnswers, len(r.q.Answers))

	for i, ans := range r.q.Answers {
		ra := roundAnswers{
			Text:  ans.String(),
			Score: ans.Score,
		}
		if pID := r.correct[i]; pID != "" {
			ra.Answered = true
			ra.PlayerName = r.players[pID].Name
		}
		if r.highlight[i] {
			ra.Highlight = true
		}
		ras[i] = ra
	}

	msg := QNAMessage{
		ChanID:         gameID,
		QuestionText:   r.q.Text,
		QuestionID:     r.q.ID,
		ShowUnanswered: showUnAnswered,
		TimeLeft:       r.timeLeft(),
		Answers:        ras,
	}

	return msg
}

func (r *round) finised() bool {
	answered := 0
	for _, pID := range r.correct {
		if pID != "" {
			answered++
		}
	}

	return answered == len(r.q.Answers)
}

// ranking generates a rank for current round which contains player, answers and score
func (r *round) ranking() Rank {
	var roundScores Rank
	lookup := make(map[PlayerID]PlayerScore)
	for i, pID := range r.correct {
		if pID != "" {
			score := r.q.Answers[i].Score
			if ps, ok := lookup[pID]; !ok {
				lookup[pID] = PlayerScore{
					PlayerID: pID,
					Name:     r.players[pID].Name,
					Score:    score,
				}
			} else {
				ps = lookup[pID]
				ps.Score += score
				lookup[pID] = ps
			}
		}
	}

	for _, ps := range lookup {
		roundScores = append(roundScores, ps)
	}
	sort.Sort(roundScores)
	for i := range roundScores {
		roundScores[i].Position = i + 1
	}

	return roundScores
}

func (r *round) answer(p Player, text string) (correct, answered bool, index int) {
	if r.state != RoundStarted {
		return false, false, -1
	}

	if _, ok := r.players[p.ID]; !ok {
		r.players[p.ID] = p
	}
	if correct, _, i := r.q.checkAnswer(text); correct {
		if r.correct[i] != "" {
			// already answered
			return correct, true, i
		}
		r.correct[i] = p.ID
		r.highlight[i] = true

		return correct, false, i
	}
	return false, false, -1
}
<commit_msg>Add latency metrics<commit_after>package fam100

import (
	"sort"
	"strconv"
	"time"

	"github.com/patrickmn/go-cache"
	"github.com/rcrowley/go-metrics"
	"github.com/uber-go/zap"
)

var (
	RoundDuration        = 90 * time.Second
	tickDuration         = 10 * time.Second
	DelayBetweenRound    = 5 * time.Second
	TickAfterWrongAnswer = false
	RoundPerGame         = 3
	DefaultQuestionLimit = 600
	log                  zap.Logger

	gameMsgProcessTimer = metrics.NewRegisteredTimer("game.processedMessage", metrics.DefaultRegistry)
	gameServiceTimer    = metrics.NewRegisteredTimer("game.serviceTimeNS", metrics.DefaultRegistry)
	gameLatencyTimer    = metrics.NewRegisteredTimer("game.latencyNS", metrics.DefaultRegistry)
	playerActive        = metrics.NewRegisteredGauge("player.active", metrics.DefaultRegistry)
	playerActiveMap     = cache.New(5*time.Minute, 30*time.Second)
)

func init() {
	log = zap.NewJSON()
	go func() {
		for range time.Tick(30 * time.Second) {
			playerActive.Update(int64(playerActiveMap.ItemCount()))
		}
	}()
}

func SetLogger(l zap.Logger) {
	log = l.With(zap.String("module", "fam100"))
}

// Message to communicate between player and the game
type Message interface{}

// TextMessage represents a chat message
type TextMessage struct {
	ChanID     string
	Player     Player
	Text       string
	ReceivedAt time.Time
}

// StateMessage represents state change in the game
type StateMessage struct {
	ChanID    string
	Round     int
	State     State
	RoundText QNAMessage //question and answer
}

// TickMessage represents time left notification
type TickMessage struct {
	ChanID   string
	TimeLeft time.Duration
}

type WrongAnswerMessage TickMessage

// QNAMessage represents question and answer for a round
type QNAMessage struct {
	ChanID         string
	Round          int
	QuestionText   string
	QuestionID     int
	Answers        []roundAnswers
	ShowUnanswered bool // reveal un-answered question (end of round)
	TimeLeft       time.Duration
}

type roundAnswers struct {
	Text       string
	Score      int
	Answered   bool
	PlayerName string
	Highlight  bool
}
type RankMessage struct {
	ChanID string
	Round  int
	Rank   Rank
	Final  bool
}

// PlayerID is the player ID type
type PlayerID string

// Player of the game
type Player struct {
	ID   PlayerID
	Name string
}

// State represents state of the round
type State string

// Available state
const (
	Created       State = "created"
	Started       State = "started"
	Finished      State = "finished"
	RoundStarted  State = "roundStarted"
	RoundTimeout  State = "RoundTimeout"
	RoundFinished State = "roundFinished"
)

// Game can consists of multiple round
// each round user will be asked question and gain points
type Game struct {
	ChanID           string
	ChanName         string
	State            State
	TotalRoundPlayed int
	players          map[PlayerID]Player
	seed             int64
	rank             Rank
	currentRound     *round

	In  chan Message
	Out chan Message
}

// NewGame create a new round
func NewGame(chanID, chanName string, in, out chan Message) (r *Game, err error) {
	seed, totalRoundPlayed, err := DefaultDB.nextGame(chanID)
	if err != nil {
		return nil, err
	}

	return &Game{
		ChanID:           chanID,
		ChanName:         chanName,
		State:            Created,
		players:          make(map[PlayerID]Player),
		seed:             seed,
		TotalRoundPlayed: totalRoundPlayed,
		In:               in,
		Out:              out,
	}, err
}

// Start the game
func (g *Game) Start() {
	g.State = Started
	log.Info("Game started",
		zap.String("chanID", g.ChanID),
		zap.Int64("seed", g.seed),
		zap.Int("totalRoundPlayed", g.TotalRoundPlayed))

	go func() {
		g.Out <- StateMessage{ChanID: g.ChanID, State: Started}
		DefaultDB.incStats("game_started")
		DefaultDB.incChannelStats(g.ChanID, "game_started")
		for i := 1; i <= RoundPerGame; i++ {
			err := g.startRound(i)
			if err != nil {
				log.Error("starting round failed", zap.String("chanID", g.ChanID), zap.Error(err))
			}
			final := i == RoundPerGame
			g.Out <- RankMessage{ChanID: g.ChanID, Round: i, Rank: g.rank, Final: final}
			if !final {
				time.Sleep(DelayBetweenRound)
			}
		}
		DefaultDB.incStats("game_finished")
		DefaultDB.incChannelStats(g.ChanID, "game_finished")
		g.State = Finished
		g.Out <- StateMessage{ChanID: g.ChanID, State: Finished}
		log.Info("Game finished", zap.String("chanID", g.ChanID))
	}()
}

func (g *Game) startRound(currentRound int) error {
	g.TotalRoundPlayed++
	DefaultDB.incRoundPlayed(g.ChanID)

	questionLimit := DefaultQuestionLimit
	if limitConf, err := DefaultDB.ChannelConfig(g.ChanID, "questionLimit", ""); err == nil && limitConf != "" {
		if limit, err := strconv.ParseInt(limitConf, 10, 64); err == nil {
			questionLimit = int(limit)
		}
	}

	r, err := newRound(g.seed, g.TotalRoundPlayed, g.players, questionLimit)
	if err != nil {
		return err
	}
	DefaultDB.incStats("round_started")
	DefaultDB.incChannelStats(g.ChanID, "round_started")

	g.currentRound = r
	r.state = RoundStarted
	timeUp := time.After(RoundDuration)
	timeLeftTick := time.NewTicker(tickDuration)
	displayAnswerTick := time.NewTicker(tickDuration)

	// print question
	g.Out <- StateMessage{ChanID: g.ChanID, State: RoundStarted, Round: currentRound, RoundText: r.questionText(g.ChanID, false)}
	log.Info("Round Started", zap.String("chanID", g.ChanID), zap.Int("questionLimit", questionLimit))

	for {
		select {
		case rawMsg := <-g.In: // new answer coming from player
			started := time.Now()
			msg, ok := rawMsg.(TextMessage)
			if !ok {
				log.Error("Unexpected message type input from client")
				continue
			}
			gameLatencyTimer.UpdateSince(msg.ReceivedAt)

			handled := g.handleMessage(msg, r)
			if handled {
				gameMsgProcessTimer.UpdateSince(started)
				gameServiceTimer.UpdateSince(msg.ReceivedAt)
				continue
			}

			if r.finised() {
				timeLeftTick.Stop()
				displayAnswerTick.Stop()
				g.showAnswer(r)
				r.state = RoundFinished
				g.updateRanking(r.ranking())
				g.Out <- StateMessage{ChanID: g.ChanID, State: RoundFinished, Round: currentRound}
				log.Info("Round finished", zap.String("chanID", g.ChanID), zap.Bool("timeout", false))
				DefaultDB.incStats("round_finished")
				DefaultDB.incChannelStats(g.ChanID, "round_finished")
				gameMsgProcessTimer.UpdateSince(started)
				gameServiceTimer.UpdateSince(msg.ReceivedAt)

				return nil
			}
			gameMsgProcessTimer.UpdateSince(started)
			gameServiceTimer.UpdateSince(msg.ReceivedAt)

		case <-timeLeftTick.C: // inform time left
			select {
			case g.Out <- TickMessage{ChanID: g.ChanID, TimeLeft: r.timeLeft()}:
			default:
			}

		case <-displayAnswerTick.C: // show correct answer (at most once every 10s)
			g.showAnswer(r)

		case <-timeUp: // time is up
			timeLeftTick.Stop()
			displayAnswerTick.Stop()
			g.State = RoundFinished
			g.updateRanking(r.ranking())
			g.Out <- StateMessage{ChanID: g.ChanID, State: RoundTimeout, Round: currentRound}
			log.Info("Round finished", zap.String("chanID", g.ChanID), zap.Bool("timeout", true))
			showUnAnswered := true
			g.Out <- r.questionText(g.ChanID, showUnAnswered)
			DefaultDB.incStats("round_timeout")
			DefaultDB.incChannelStats(g.ChanID, "round_timeout")

			return nil
		}
	}
}

func (g *Game) handleMessage(msg TextMessage, r *round) (handled bool) {
	playerActiveMap.Set(string(msg.Player.ID), struct{}{}, cache.DefaultExpiration)
	log.Debug("startRound got message", zap.String("chanID", g.ChanID), zap.Object("msg", msg))
	answer := msg.Text
	correct, alreadyAnswered, idx := r.answer(msg.Player, answer)
	if !correct {
		if TickAfterWrongAnswer {
			g.Out <- WrongAnswerMessage{ChanID: g.ChanID, TimeLeft: r.timeLeft()}
		}
		return true
	}
	if alreadyAnswered {
		log.Debug("already answered", zap.String("chanID", g.ChanID), zap.String("by", string(r.correct[idx])))
		return true
	}

	DefaultDB.incStats("answer_correct")
	DefaultDB.incChannelStats(g.ChanID, "answer_correct")
	DefaultDB.incPlayerStats(msg.Player.ID, "answer_correct")
	log.Info("answer correct",
		zap.String("playerID", string(msg.Player.ID)),
		zap.String("playerName", msg.Player.Name),
		zap.String("answer", answer),
		zap.Int("questionID", r.q.ID),
		zap.String("chanID", g.ChanID))

	return false
}

func (g *Game) updateRanking(r Rank) {
	g.rank = g.rank.Add(r)
	DefaultDB.saveScore(g.ChanID, g.ChanName, r)
}

func (g *Game) CurrentQuestion() Question {
	return g.currentRound.q
}

func (g *Game) showAnswer(r *round) {
	var show bool
	// if there is no highlighted answer don't display
	for _, v := range r.highlight {
		if v {
			show = true
			break
		}
	}
	if !show {
		return
	}

	qnaText := r.questionText(g.ChanID, false)
	select {
	case g.Out <- qnaText:
	default:
	}

	for i := range r.highlight {
		r.highlight[i] = false
	}
}

// round represents with one question
type round struct {
	q         Question
	state     State
	correct   []PlayerID // correct answer answered by a player, "" means not answered
	players   map[PlayerID]Player
	highlight map[int]bool

	endAt time.Time
}

func newRound(seed int64, totalRoundPlayed int, players map[PlayerID]Player, questionLimit int) (*round, error) {
	q, err := NextQuestion(seed, totalRoundPlayed, questionLimit)
	if err != nil {
		return nil, err
	}

	return &round{
		q:         q,
		correct:   make([]PlayerID, len(q.Answers)),
		state:     Created,
		players:   players,
		highlight: make(map[int]bool),
		endAt:     time.Now().Add(RoundDuration).Round(time.Second),
	}, nil
}

func (r *round) timeLeft() time.Duration {
	return r.endAt.Sub(time.Now().Round(time.Second))
}

// questionText construct QNAMessage which contains questions, answers and score
func (r *round) questionText(gameID string, showUnAnswered bool) QNAMessage {
	ras := make([]roundAnswers, len(r.q.Answers))

	for i, ans := range r.q.Answers {
		ra := roundAnswers{
			Text:  ans.String(),
			Score: ans.Score,
		}
		if pID := r.correct[i]; pID != "" {
			ra.Answered = true
			ra.PlayerName = r.players[pID].Name
		}
		if r.highlight[i] {
			ra.Highlight = true
		}
		ras[i] = ra
	}

	msg := QNAMessage{
		ChanID:         gameID,
		QuestionText:   r.q.Text,
		QuestionID:     r.q.ID,
		ShowUnanswered: showUnAnswered,
		TimeLeft:       r.timeLeft(),
		Answers:        ras,
	}

	return msg
}

func (r *round) finised() bool {
	answered := 0
	for _, pID := range r.correct {
		if pID != "" {
			answered++
		}
	}

	return answered == len(r.q.Answers)
}

// ranking generates a rank for current round which contains player, answers and score
func (r *round) ranking() Rank {
	var roundScores Rank
	lookup := make(map[PlayerID]PlayerScore)
	for i, pID := range r.correct {
		if pID != "" {
			score := r.q.Answers[i].Score
			if ps, ok := lookup[pID]; !ok {
				lookup[pID] = PlayerScore{
					PlayerID: pID,
					Name:     r.players[pID].Name,
					Score:    score,
				}
			} else {
				ps = lookup[pID]
				ps.Score += score
				lookup[pID] = ps
			}
		}
	}

	for _, ps := range lookup {
		roundScores = append(roundScores, ps)
	}
	sort.Sort(roundScores)
	for i := range roundScores {
		roundScores[i].Position = i + 1
	}

	return roundScores
}

func (r *round) answer(p Player, text string) (correct, answered bool, index int) {
	if r.state != RoundStarted {
		return false, false, -1
	}

	if _, ok := r.players[p.ID]; !ok {
		r.players[p.ID] = p
	}
	if correct, _, i := r.q.checkAnswer(text); correct {
		if r.correct[i] != "" {
			// already answered
			return correct, true, i
		}
		r.correct[i] = p.ID
		r.highlight[i] = true

		return correct, false, i
	}
	return false, false, -1
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package cache

import (
	"sync"
	"sync/atomic"
)

// LRUCache represent a LRU cache state.
type LRUCache struct {
	sync.Mutex

	recent   lruNode
	table    map[uint64]*lruNs
	capacity int
	size     int
}

// NewLRUCache create new initialized LRU cache.
func NewLRUCache(capacity int) *LRUCache {
	c := &LRUCache{
		table:    make(map[uint64]*lruNs),
		capacity: capacity,
	}
	c.recent.rNext = &c.recent
	c.recent.rPrev = &c.recent
	return c
}

// SetCapacity set cache capacity.
func (c *LRUCache) SetCapacity(capacity int) {
	c.Lock()
	c.capacity = capacity
	c.evict()
	c.Unlock()
}

// GetNamespace return namespace object for given id.
func (c *LRUCache) GetNamespace(id uint64) Namespace {
	c.Lock()
	defer c.Unlock()

	if p, ok := c.table[id]; ok {
		return p
	}

	p := &lruNs{
		lru:   c,
		id:    id,
		table: make(map[uint64]*lruNode),
	}
	c.table[id] = p
	return p
}

// Purge purge entire cache.
func (c *LRUCache) Purge(fin func()) {
	c.Lock()
	top := &c.recent
	for n := c.recent.rPrev; n != top; {
		n.deleted = true
		n.rRemove()
		n.delfin = fin
		n.evict_NB()
		n = c.recent.rPrev
	}
	c.size = 0
	c.Unlock()
}

func (c *LRUCache) Zap() {
	c.Lock()
	for _, ns := range c.table {
		for _, n := range ns.table {
			n.rNext = nil
			n.rPrev = nil
			n.execFin()
		}
		ns.zapped = true
		ns.table = nil
	}
	c.recent.rNext = &c.recent
	c.recent.rPrev = &c.recent
	c.size = 0
	c.table = make(map[uint64]*lruNs)
	c.Unlock()
}

func (c *LRUCache) evict() {
	top := &c.recent
	for n := c.recent.rPrev; c.size > c.capacity && n != top; {
		n.rRemove()
		n.evict_NB()
		c.size -= n.charge
		n = c.recent.rPrev
	}
}

type lruNs struct {
	lru    *LRUCache
	id     uint64
	table  map[uint64]*lruNode
	zapped bool
}

func (p *lruNs) Get(key uint64, setf SetFunc) (obj Object, ok bool) {
	lru := p.lru
	lru.Lock()

	if p.zapped {
		lru.Unlock()
		if setf == nil {
			return
		}
		if ok, value, _, fin := setf(); ok {
			return &emptyCacheObj{value, fin}, true
		}
		return
	}

	n, ok := p.table[key]
	if ok {
		if !n.deleted {
			// bump to front
			n.rRemove()
			n.rInsert(&lru.recent)
		}
		atomic.AddInt32(&n.ref, 1)
	} else {
		if setf == nil {
			lru.Unlock()
			return
		}

		ok, value, charge, fin := setf()
		if !ok {
			lru.Unlock()
			return nil, false
		}

		n = &lruNode{
			ns:     p,
			key:    key,
			value:  value,
			charge: charge,
			setfin: fin,
			ref:    2,
		}
		p.table[key] = n
		n.rInsert(&lru.recent)

		lru.size += charge
		lru.evict()
	}

	lru.Unlock()

	return &lruObject{node: n}, true
}

func (p *lruNs) Delete(key uint64, fin func()) bool {
	lru := p.lru
	lru.Lock()

	if p.zapped {
		lru.Unlock()
		if fin != nil {
			fin()
		}
		return false
	}

	n, ok := p.table[key]
	if !ok {
		lru.Unlock()
		if fin != nil {
			fin()
		}
		return false
	}

	if n.deleted {
		lru.Unlock()
		return false
	}

	n.deleted = true
	n.rRemove()
	n.delfin = fin
	n.evict_NB()
	lru.size -= n.charge

	lru.Unlock()
	return true
}

func (p *lruNs) Purge(fin func()) {
	lru := p.lru

	lru.Lock()
	if p.zapped {
		return
	}

	for _, n := range p.table {
		if n.deleted {
			continue
		}
		n.rRemove()
		n.delfin = fin
		n.evict_NB()
		lru.size -= n.charge
	}
	lru.Unlock()
}

func (p *lruNs) Zap() {
	lru := p.lru

	lru.Lock()
	if p.zapped {
		return
	}

	for _, n := range p.table {
		if n.rRemove() {
			lru.size -= n.charge
		}
		n.execFin()
	}
	p.zapped = true
	p.table = nil
	delete(lru.table, p.id)
	lru.Unlock()
}

type lruNode struct {
	ns *lruNs

	rNext, rPrev *lruNode

	key     uint64
	value   interface{}
	charge  int
	ref     int32
	deleted bool
	setfin  func()
	delfin  func()
}

func (n *lruNode) rInsert(at *lruNode) {
	x := at.rNext
	at.rNext = n
	n.rPrev = at
	n.rNext = x
	x.rPrev = n
}

func (n *lruNode) rRemove() bool {
	// only remove if not already removed
	if n.rPrev == nil {
		return false
	}

	n.rPrev.rNext = n.rNext
	n.rNext.rPrev = n.rPrev
	n.rPrev = nil
	n.rNext = nil

	return true
}

func (n *lruNode) execFin() {
	if n.setfin != nil {
		n.setfin()
		n.setfin = nil
	}
	if n.delfin != nil {
		n.delfin()
		n.delfin = nil
	}
}

func (n *lruNode) doEvict() {
	if n.ns.zapped {
		return
	}

	// remove elem
	delete(n.ns.table, n.key)

	// execute finalizer
	n.execFin()

	n.value = nil
}

func (n *lruNode) evict() {
	if atomic.AddInt32(&n.ref, -1) != 0 {
		return
	}

	lru := n.ns.lru
	lru.Lock()
	n.doEvict()
	lru.Unlock()
}

func (n *lruNode) evict_NB() {
	if atomic.AddInt32(&n.ref, -1) != 0 {
		return
	}

	n.doEvict()
}

type lruObject struct {
	node *lruNode
	once uint32
}

func (p *lruObject) Value() interface{} {
	if atomic.LoadUint32(&p.once) == 0 {
		return p.node.value
	}
	return nil
}

func (p *lruObject) Release() {
	if !atomic.CompareAndSwapUint32(&p.once, 0, 1) {
		return
	}

	p.node.evict()
	p.node = nil
}
<commit_msg>Remove naked returns from leveldb/cache/lru_cache.go.<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package cache

import (
	"sync"
	"sync/atomic"
)

// LRUCache represent a LRU cache state.
type LRUCache struct {
	sync.Mutex

	recent   lruNode
	table    map[uint64]*lruNs
	capacity int
	size     int
}

// NewLRUCache create new initialized LRU cache.
func NewLRUCache(capacity int) *LRUCache {
	c := &LRUCache{
		table:    make(map[uint64]*lruNs),
		capacity: capacity,
	}
	c.recent.rNext = &c.recent
	c.recent.rPrev = &c.recent
	return c
}

// SetCapacity set cache capacity.
func (c *LRUCache) SetCapacity(capacity int) {
	c.Lock()
	c.capacity = capacity
	c.evict()
	c.Unlock()
}

// GetNamespace return namespace object for given id.
func (c *LRUCache) GetNamespace(id uint64) Namespace {
	c.Lock()
	defer c.Unlock()

	if p, ok := c.table[id]; ok {
		return p
	}

	p := &lruNs{
		lru:   c,
		id:    id,
		table: make(map[uint64]*lruNode),
	}
	c.table[id] = p
	return p
}

// Purge purge entire cache.
func (c *LRUCache) Purge(fin func()) {
	c.Lock()
	top := &c.recent
	for n := c.recent.rPrev; n != top; {
		n.deleted = true
		n.rRemove()
		n.delfin = fin
		n.evict_NB()
		n = c.recent.rPrev
	}
	c.size = 0
	c.Unlock()
}

func (c *LRUCache) Zap() {
	c.Lock()
	for _, ns := range c.table {
		for _, n := range ns.table {
			n.rNext = nil
			n.rPrev = nil
			n.execFin()
		}
		ns.zapped = true
		ns.table = nil
	}
	c.recent.rNext = &c.recent
	c.recent.rPrev = &c.recent
	c.size = 0
	c.table = make(map[uint64]*lruNs)
	c.Unlock()
}

func (c *LRUCache) evict() {
	top := &c.recent
	for n := c.recent.rPrev; c.size > c.capacity && n != top; {
		n.rRemove()
		n.evict_NB()
		c.size -= n.charge
		n = c.recent.rPrev
	}
}

type lruNs struct {
	lru    *LRUCache
	id     uint64
	table  map[uint64]*lruNode
	zapped bool
}

func (p *lruNs) Get(key uint64, setf SetFunc) (Object, bool) {
	lru := p.lru
	lru.Lock()

	if p.zapped {
		lru.Unlock()
		if setf == nil {
			return nil, false
		}
		if ok, value, _, fin := setf(); ok {
			return &emptyCacheObj{value, fin}, true
		}
		return nil, false
	}

	n, ok := p.table[key]
	if ok {
		if !n.deleted {
			// bump to front
			n.rRemove()
			n.rInsert(&lru.recent)
		}
		atomic.AddInt32(&n.ref, 1)
	} else {
		if setf == nil {
			lru.Unlock()
			return nil, false
		}

		ok, value, charge, fin := setf()
		if !ok {
			lru.Unlock()
			return nil, false
		}

		n = &lruNode{
			ns:     p,
			key:    key,
			value:  value,
			charge: charge,
			setfin: fin,
			ref:    2,
		}
		p.table[key] = n
		n.rInsert(&lru.recent)

		lru.size += charge
		lru.evict()
	}

	lru.Unlock()

	return &lruObject{node: n}, true
}

func (p *lruNs) Delete(key uint64, fin func()) bool {
	lru := p.lru
	lru.Lock()

	if p.zapped {
		lru.Unlock()
		if fin != nil {
			fin()
		}
		return false
	}

	n, ok := p.table[key]
	if !ok {
		lru.Unlock()
		if fin != nil {
			fin()
		}
		return false
	}

	if n.deleted {
		lru.Unlock()
		return false
	}

	n.deleted = true
	n.rRemove()
	n.delfin = fin
	n.evict_NB()
	lru.size -= n.charge

	lru.Unlock()
	return true
}

func (p *lruNs) Purge(fin func()) {
	lru := p.lru

	lru.Lock()
	if p.zapped {
		return
	}

	for _, n := range p.table {
		if n.deleted {
			continue
		}
		n.rRemove()
		n.delfin = fin
		n.evict_NB()
		lru.size -= n.charge
	}
	lru.Unlock()
}

func (p *lruNs) Zap() {
	lru := p.lru

	lru.Lock()
	if p.zapped {
		return
	}

	for _, n := range p.table {
		if n.rRemove() {
			lru.size -= n.charge
		}
		n.execFin()
	}
	p.zapped = true
	p.table = nil
	delete(lru.table, p.id)
	lru.Unlock()
}

type lruNode struct {
	ns *lruNs

	rNext, rPrev *lruNode

	key     uint64
	value   interface{}
	charge  int
	ref     int32
	deleted bool
	setfin  func()
	delfin  func()
}

func (n *lruNode) rInsert(at *lruNode) {
	x := at.rNext
	at.rNext = n
	n.rPrev = at
	n.rNext = x
	x.rPrev = n
}

func (n *lruNode) rRemove() bool {
	// only remove if not already removed
	if n.rPrev == nil {
		return false
	}

	n.rPrev.rNext = n.rNext
	n.rNext.rPrev = n.rPrev
	n.rPrev = nil
	n.rNext = nil

	return true
}

func (n *lruNode) execFin() {
	if n.setfin != nil {
		n.setfin()
		n.setfin = nil
	}
	if n.delfin != nil {
		n.delfin()
		n.delfin = nil
	}
}

func (n *lruNode) doEvict() {
	if n.ns.zapped {
		return
	}

	// remove elem
	delete(n.ns.table, n.key)

	// execute finalizer
	n.execFin()

	n.value = nil
}

func (n *lruNode) evict() {
	if atomic.AddInt32(&n.ref, -1) != 0 {
		return
	}

	lru := n.ns.lru
	lru.Lock()
	n.doEvict()
	lru.Unlock()
}

func (n *lruNode) evict_NB() {
	if atomic.AddInt32(&n.ref, -1) != 0 {
		return
	}

	n.doEvict()
}

type lruObject struct {
	node *lruNode
	once uint32
}

func (p *lruObject) Value() interface{} {
	if atomic.LoadUint32(&p.once) == 0 {
		return p.node.value
	}
	return nil
}

func (p *lruObject) Release() {
	if !atomic.CompareAndSwapUint32(&p.once, 0, 1) {
		return
	}

	p.node.evict()
	p.node = nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/go-pg/pg"
	"os"
	"io/ioutil"
	"encoding/json"
	"errors"
	"github.com/go-pg/pg/orm"
	"github.com/romana/rlog"
	"encoding/csv"
	"io"
	"path/filepath"
	"strings"
	"bufio"
	"regexp"
	"fmt"
)

const(
	_ = iota
	ActionParse = iota
	ActionGenerate = iota
	ActionDrop = iota
	ActionFormat = iota
)

var mapAction = map[string]int{
	"parse": ActionParse,
	"generate": ActionGenerate,
	"drop": ActionDrop,
	"format": ActionFormat,
}

type TTuple []string

type TApp struct {
	pathOptions *string
	fOptions *os.File
	dbOptions *pg.Options
	db        *pg.DB
	action    int

	fCSV *os.File
	pathCSV string

	pathOutput *string
	fOutput *os.File
}

func (app *TApp) mProcessArguments() (err error) {

	app.pathOptions = flag.String("d", "./config/database.json",
		"Path to a json file with database connection information")
	//pfSource := flag.String("s", "./data.csv", "Path to a CSV file with data to process")
	pstrAction := flag.String("a", "parse",
		"Action to perform; \n Available are: \n\t" +
			" - 'parse' parses given csv file; \n\t" +
			" - 'generate' generates table in existing database; \n\t" +
			" - 'drop' drops table in existing database; \n\t" +
			" - 'format' reformats bogus CSV to real CSV")

	app.pathOutput = flag.String("o", "", "Output file path; Used on reformatting.")

	flag.Usage = fUsage
	flag.Parse()

	app.fOptions, err = app.mOpenFile(app.pathOptions)
	if err != nil { return }

	err = app.mReadOptions()
	if err != nil {
		rlog.Debug(err)
		return }

	err = app.mReadAction(pstrAction)
	if err != nil { return }

	app.pathCSV = flag.Arg(0)

	return
}

func (app *TApp) mReadAction(pstrAction *string) error {
	var ok bool
	if app.action, ok = mapAction[*pstrAction]; !ok {
		return errors.New("Action '" + *pstrAction + "' not implemented")
	}

	return nil
}

func (app *TApp) mOpenFile(pstrFileName *string) (pf *os.File, err error) {
	pf, err = os.Open(*pstrFileName)
	if err != nil {
		err = &TErrorFile{*pstrFileName, err}
	}

	return
}

func (app *TApp) mReadOptions() error {

	defer app.fOptions.Close()

	slbDBOptions, err := ioutil.ReadAll(app.fOptions)
	if err != nil {
		return &TErrorFile{*app.pathOptions, err}
	}

	app.dbOptions = &pg.Options{}
	err = json.Unmarshal(slbDBOptions, app.dbOptions)
	if err != nil {
		return &TErrorFile{*app.pathOptions, err}
	}

	return nil
}

func (app *TApp) mInit(){
	app.db = pg.Connect(app.dbOptions)
}

func (app *TApp) mRun() error {
	switch app.action {
	case ActionParse:
		return app.mParse()
	case ActionGenerate:
		return app.mGenerate()
	case ActionDrop:
		return app.mDrop()
	case ActionFormat:
		return app.mFormat()
	default:
		return errors.New("Something went completely wrong")
	}
}

func (app *TApp) mParse() (err error) {

	if err = app.mOpenSource(); err != nil { return }
	defer app.fCSV.Close()

	csvReader := csv.NewReader(app.fCSV)
	csvReader.Comma = ';'
	csvReader.Comment = '#'
	csvReader.FieldsPerRecord = 6

	var slTuple TTuple
	//slTuple, err = csvReader.Read()
	//if err != nil { return }
	//rlog.Infof("CSV header: '%v'", slTuple)

	for{
		slTuple, err = csvReader.Read()
		if err == io.EOF { break }
		if err != nil { return }

		var psdbmPool *TSDBMPool
		psdbmPool, err = NewPool(slTuple)
		if err != nil { return }

		err = psdbmPool.mSave(app.db)
		if err != nil { return }
	}

	return nil
}

func (app *TApp) mGenerate() error {
	return app.db.CreateTable(&TSDBMPool{}, &orm.CreateTableOptions{})
}

func (app *TApp) mDrop() (err error) {
	strQuery := "DROP TABLE IF EXISTS pool"
	_, err = app.db.Exec(strQuery)

	return
}

func (app *TApp) mFormat() (err error) {
	if err = app.mOpenSource(); err != nil { return }
	defer app.fCSV.Close()

	//got no path for output file; let's make it out of input path
	app.mPrepareOutputPath()

	app.fOutput, err = os.Create(*app.pathOutput)
	if err != nil { return }
	defer app.fOutput.Close()

	scSource := bufio.NewScanner(app.fCSV)

	scSource.Scan()
	//let's comment first string which is, kinda, header
	var strFirst string
	if strFirst, err = fWinToUtf(scSource.Text()); err != nil { return err }
	app.fOutput.WriteString("#" + strFirst + "\n")

	replacer := strings.NewReplacer("\t", "", "\"", "\"\"")
	preStrings, err := regexp.Compile(`^(\d+;\d+;\d+;\d+;)(\D+);(\D+)$`)
	if err != nil { return }

	var strSource string
	for scSource.Scan() {

		//take care of lame encoding
		strSource, err = fWinToUtf(scSource.Text())
		if err != nil { return err }

		strNew := replacer.Replace(strSource)
		strNew = preStrings.ReplaceAllString(strNew, "$1\"$2\";\"$3\"\n")

		if _, err = app.fOutput.WriteString(strNew); err != nil { return }
	}

	err = scSource.Err()

	return
}

func (app *TApp) mShutDown() {
	app.db.Close()
}

func (app *TApp) mPrepareOutputPath() {
	if *app.pathOutput == "" {
		var strOutputFile, strOutputPath string
		strDir, strSourceFile := filepath.Split(app.pathCSV)
		strExt := filepath.Ext(strSourceFile)
		if strExt != "" {
			strOutputFile = strings.Trim(strSourceFile, "." + strExt)
		}else{
			strOutputFile = strSourceFile
		}

		strOutputPath = strDir + strOutputFile + "_formatted.csv"
		app.pathOutput = &strOutputPath
	}
}

func (app *TApp) mOpenSource() error {
	if app.pathCSV == "" {
		return errors.New("No data file provided")
	}

	var err error

	if app.fCSV, err = app.mOpenFile(&app.pathCSV); err != nil {
		return &TErrorFile{app.pathCSV, err}
	}

	return err
}

func fUsage() {
	fmt.Println("Usage:")
	fmt.Println("1st form: pool -a (generate|drop) [-d <path to db settings>]")
	fmt.Println("2nd form: pool -a format [-o <path to output file>] <path to input file>")
	fmt.Println("3rd form: pool -a parse [-d <path to db settings>] <path to input file>")
	flag.PrintDefaults()
}
<commit_msg>fixed missing strings with numbers in provider or region<commit_after>package main

import (
	"flag"
	"github.com/go-pg/pg"
	"os"
	"io/ioutil"
	"encoding/json"
	"errors"
	"github.com/go-pg/pg/orm"
	"github.com/romana/rlog"
	"encoding/csv"
	"io"
	"path/filepath"
	"strings"
	"bufio"
	"regexp"
	"fmt"
)

const(
	_ = iota
	ActionParse = iota
	ActionGenerate = iota
	ActionDrop = iota
	ActionFormat = iota
)

var mapAction = map[string]int{
	"parse": ActionParse,
	"generate": ActionGenerate,
	"drop": ActionDrop,
	"format": ActionFormat,
}

type TTuple []string

type TApp struct {
	pathOptions *string
	fOptions *os.File
	dbOptions *pg.Options
	db        *pg.DB
	action    int

	fCSV *os.File
	pathCSV string

	pathOutput *string
	fOutput *os.File

	errorCounter int
}

func (app *TApp) mProcessArguments() (err error) {

	app.pathOptions = flag.String("d", "./config/database.json",
		"Path to a json file with database connection information")
	//pfSource := flag.String("s", "./data.csv", "Path to a CSV file with data to process")
	pstrAction := flag.String("a", "parse",
		"Action to perform; \n Available are: \n\t" +
			" - 'parse' parses given csv file; \n\t" +
			" - 'generate' generates table in existing database; \n\t" +
			" - 'drop' drops table in existing database; \n\t" +
			" - 'format' reformats bogus CSV to real CSV")

	app.pathOutput = flag.String("o", "", "Output file path; Used on reformatting.")

	flag.Usage = fUsage
	flag.Parse()

	app.fOptions, err = app.mOpenFile(app.pathOptions)
	if err != nil { return }

	err = app.mReadOptions()
	if err != nil {
		rlog.Debug(err)
		return }

	err = app.mReadAction(pstrAction)
	if err != nil { return }

	app.pathCSV = flag.Arg(0)

	return
}

func (app *TApp) mReadAction(pstrAction *string) error {
	var ok bool
	if app.action, ok = mapAction[*pstrAction]; !ok {
		return errors.New("Action '" + *pstrAction + "' not implemented")
	}

	return nil
}

func (app *TApp) mOpenFile(pstrFileName *string) (pf *os.File, err error) {
	pf, err = os.Open(*pstrFileName)
	if err != nil {
		err = &TErrorFile{*pstrFileName, err}
	}

	return
}

func (app *TApp) mReadOptions() error {

	defer app.fOptions.Close()

	slbDBOptions, err := ioutil.ReadAll(app.fOptions)
	if err != nil {
		return &TErrorFile{*app.pathOptions, err}
	}

	app.dbOptions = &pg.Options{}
	err = json.Unmarshal(slbDBOptions, app.dbOptions)
	if err != nil {
		return &TErrorFile{*app.pathOptions, err}
	}

	return nil
}

func (app *TApp) mInit(){
	app.db = pg.Connect(app.dbOptions)
}

func (app *TApp) mRun() error {
	switch app.action {
	case ActionParse:
		return app.mParse()
	case ActionGenerate:
		return app.mGenerate()
	case ActionDrop:
		return app.mDrop()
	case ActionFormat:
		return app.mFormat()
	default:
		return errors.New("Something went completely wrong")
	}
}

func (app *TApp) mParse() (err error) {

	if err = app.mOpenSource(); err != nil { return }
	defer app.fCSV.Close()

	csvReader := csv.NewReader(app.fCSV)
	csvReader.Comma = ';'
	csvReader.Comment = '#'
	csvReader.FieldsPerRecord = 6

	var slTuple TTuple
	//slTuple, err = csvReader.Read()
	//if err != nil { return }
	//rlog.Infof("CSV header: '%v'", slTuple)

	for{
		slTuple, err = csvReader.Read()
		if err == io.EOF { break }
		if err != nil { return }

		var psdbmPool *TSDBMPool
		psdbmPool, err = NewPool(slTuple)
		if err != nil { return }

		err = psdbmPool.mSave(app.db)
		if err != nil { return }
	}

	return nil
}

func (app *TApp) mGenerate() error {
	return app.db.CreateTable(&TSDBMPool{}, &orm.CreateTableOptions{})
}

func (app *TApp) mDrop() (err error) {
	strQuery := "DROP TABLE IF EXISTS pool"
	_, err = app.db.Exec(strQuery)

	return
}

func (app *TApp) mFormat() (err error) {
	if err = app.mOpenSource(); err != nil { return }
	defer app.fCSV.Close()

	//got no path for output file; let's make it out of input path
	app.mPrepareOutputPath()

	app.fOutput, err = os.Create(*app.pathOutput)
	if err != nil { return }
	defer app.fOutput.Close()

	scSource := bufio.NewScanner(app.fCSV)

	scSource.Scan()
	//let's comment first string which is, kinda, header
	var strFirst string
	if strFirst, err = fWinToUtf(scSource.Text()); err != nil { return err }
	app.fOutput.WriteString("#" + strFirst + "\n")

	replacer := strings.NewReplacer("\t", "", "\"", "\"\"")
	preStrings, err := regexp.Compile(`^(\d+;\d+;\d+;\d+;)([^;]+);([^;]+)$`)
	if err != nil { return }

	var strSource string
	for scSource.Scan() {

		if scSource.Text() == "" { continue }

		//take care of lame encoding
		strSource, err = fWinToUtf(scSource.Text())
		if err != nil { return err }

		//get rid of tabs
		strTemp := replacer.Replace(strSource)
		//add extra quotes
		strNew := preStrings.ReplaceAllString(strTemp, "$1\"$2\";\"$3\"\n")

		if strNew == strTemp {
			rlog.Errorf("no quotes added to: '%s'", strTemp)
			app.errorCounter ++
		}

		if _, err = app.fOutput.WriteString(strNew); err != nil { return }
	}

	err = scSource.Err()
	return
}

func (app *TApp) mShutDown() {
	app.db.Close()
	rlog.Debugf("Got %d errors\n", app.errorCounter)
}

func (app *TApp) mPrepareOutputPath() {
	if *app.pathOutput == "" {
		var strOutputFile, strOutputPath string
		strDir, strSourceFile := filepath.Split(app.pathCSV)
		strExt := filepath.Ext(strSourceFile)
		if strExt != "" {
			strOutputFile = strings.Trim(strSourceFile, "." + strExt)
		}else{
			strOutputFile = strSourceFile
		}

		strOutputPath = strDir + strOutputFile + "_formatted.csv"
		app.pathOutput = &strOutputPath
	}
}

func (app *TApp) mOpenSource() error {
	if app.pathCSV == "" {
		return errors.New("No data file provided")
	}

	var err error

	if app.fCSV, err = app.mOpenFile(&app.pathCSV); err != nil {
		return &TErrorFile{app.pathCSV, err}
	}

	return err
}

func fUsage() {
	fmt.Println("Usage:")
	fmt.Println("1st form: pool -a (generate|drop) [-d <path to db settings>]")
	fmt.Println("2nd form: pool -a format [-o <path to output file>] <path to input file>")
	fmt.Println("3rd form: pool -a parse [-d <path to db settings>] <path to input file>")
	flag.PrintDefaults()
}
<|endoftext|>


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

package sparsebitset

// popcount answers the number of bits set to `1` in this word.  It
// uses the bit population count (Hamming Weight) logic taken from
// https://code.google.com/p/go/issues/detail?id=4988#c11.  Original
// by 'https://code.google.com/u/arnehormann/'.
func popcount(x uint64) (n uint64) {
	x -= (x >> 1) & 0x5555555555555555
	x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
	x += x >> 4
	x &= 0x0f0f0f0f0f0f0f0f
	x *= 0x0101010101010101
	return x >> 56
}

// popcountSet answers the number of bits set to `1` in this set.
func popcountSet(a blockAry) uint64 {
	c := uint64(0)
	for _, el := range a {
		c += popcount(el.Mask)
	}
	return c
}

// popcountSetMasked answers the remaining number of bits set to `1`,
// when masked with another bitset.
func popcountSetMasked(a, other blockAry) uint64 {
	c := uint64(0)
	for i, el := range a {
		c += popcount(el.Mask &^ other[i].Mask)
	}
	return c
}

// popcountSetAnd answers the remaining number of bits set to `1`,
// when `and`ed with another bitset.
func popcountSetAnd(a, other blockAry) uint64 {
	c := uint64(0)
	for i, el := range a {
		c += popcount(el.Mask & other[i].Mask)
	}
	return c
}

// popcountSetOr answers the remaining number of bits set to `1`,
// when inclusively `or`ed with another bitset.
func popcountSetOr(a, other blockAry) uint64 {
	c := uint64(0)
	for i, el := range a {
		c += popcount(el.Mask | other[i].Mask)
	}
	return c
}

// popcountSetXor answers the remaining number of bits set to `1`,
// when exclusively `or`ed with another bitset.
func popcountSetXor(a, other blockAry) uint64 {
	c := uint64(0)
	for i, el := range a {
		c += popcount(el.Mask ^ other[i].Mask)
	}
	return c
}
<commit_msg>Implement proper iteration logic in `popcount*`<commit_after>// (c) Copyright 2015 JONNALAGADDA Srinivas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sparsebitset

// popcount answers the number of bits set to `1` in this word.  It
// uses the bit population count (Hamming Weight) logic taken from
// https://code.google.com/p/go/issues/detail?id=4988#c11.  Original
// by 'https://code.google.com/u/arnehormann/'.
func popcount(x uint64) (n uint64) {
	x -= (x >> 1) & 0x5555555555555555
	x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
	x += x >> 4
	x &= 0x0f0f0f0f0f0f0f0f
	x *= 0x0101010101010101
	return x >> 56
}

// popcountSet answers the number of bits set to `1` in this set.
func popcountSet(a blockAry) uint64 {
	c := uint64(0)
	for _, el := range a {
		c += popcount(el.Mask)
	}
	return c
}

// popcountSetAndNot answers the remaining number of bits set to `1`,
// when subtracting another bitset as specified.
func popcountSetAndNot(a, b blockAry) uint64 {
	c := uint64(0)

	la := len(a)
	lb := len(b)
	i, j := 0, 0
	for i < la && j < lb {
		abl, bbl := a[i], b[j]

		if abl.Offset < bbl.Offset {
			c += popcount(abl.Mask)
			i++
		} else if abl.Offset == bbl.Offset {
			c += popcount(abl.Mask &^ bbl.Mask)
			i, j = i+1, j+1
		} else {
			j++
		}
	}
	for ; i < la; i++ {
		c += popcount(a[i].Mask)
	}

	return c
}

// popcountSetAnd answers the remaining number of bits set to `1`,
// when `and`ed with another bitset.
func popcountSetAnd(a, b blockAry) uint64 {
	c := uint64(0)

	la := len(a)
	lb := len(b)
	i, j := 0, 0
	for i < la && j < lb {
		abl, bbl := a[i], b[j]

		if abl.Offset < bbl.Offset {
			i++
		} else if abl.Offset == bbl.Offset {
			c += popcount(abl.Mask & bbl.Mask)
			i, j = i+1, j+1
		} else {
			j++
		}
	}

	return c
}

// popcountSetOr answers the remaining number of bits set to `1`,
// when inclusively `or`ed with another bitset.
func popcountSetOr(a, b blockAry) uint64 {
	c := uint64(0)

	la := len(a)
	lb := len(b)
	i, j := 0, 0
	for i < la && j < lb {
		abl, bbl := a[i], b[j]

		if abl.Offset < bbl.Offset {
			c += popcount(abl.Mask)
			i++
		} else if abl.Offset == bbl.Offset {
			c += popcount(abl.Mask | bbl.Mask)
			i, j = i+1, j+1
		} else {
			c += popcount(bbl.Mask)
			j++
		}
	}
	for ; i < la; i++ {
		c += popcount(a[i].Mask)
	}
	for ; j < lb; i++ {
		c += popcount(b[j].Mask)
	}

	return c
}

// popcountSetXor answers the remaining number of bits set to `1`,
// when exclusively `or`ed with another bitset.
func popcountSetXor(a, b blockAry) uint64 {
	c := uint64(0)

	la := len(a)
	lb := len(b)
	i, j := 0, 0
	for i < la && j < lb {
		abl, bbl := a[i], b[j]

		if abl.Offset < bbl.Offset {
			c += popcount(abl.Mask)
			i++
		} else if abl.Offset == bbl.Offset {
			c += popcount(abl.Mask ^ bbl.Mask)
			i, j = i+1, j+1
		} else {
			c += popcount(bbl.Mask)
			j++
		}
	}
	for ; i < la; i++ {
		c += popcount(a[i].Mask)
	}
	for ; j < lb; i++ {
		c += popcount(b[j].Mask)
	}

	return c
}
<|endoftext|>


<commit_before>// Package dnsutil copied from coredns project
// https://github.com/coredns/coredns/blob/master/plugin/pkg/dnsutil/reverse.go
package dnsutil

import (
	"net"
	"strings"
)

// ExtractAddressFromReverse turns a standard PTR reverse record name
// into an IP address. This works for ipv4 or ipv6.
//
// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
// fails the empty string is returned.
func ExtractAddressFromReverse(reverseName string) string {
	search := ""

	f := reverse

	switch {
	case strings.HasSuffix(reverseName, IP4arpa):
		search = strings.TrimSuffix(reverseName, IP4arpa)
	case strings.HasSuffix(reverseName, IP6arpa):
		search = strings.TrimSuffix(reverseName, IP6arpa)
		f = reverse6
	default:
		return ""
	}

	// Reverse the segments and then combine them.
	return f(strings.Split(search, "."))
}

// IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates
// name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4)
// and 2 for ip6.arpa. (IPv6).
func IsReverse(name string) int {
	if strings.HasSuffix(name, IP4arpa) {
		return 1
	}
	if strings.HasSuffix(name, IP6arpa) {
		return 2
	}
	return 0
}

func reverse(slice []string) string {
	for i := 0; i < len(slice)/2; i++ {
		j := len(slice) - i - 1
		slice[i], slice[j] = slice[j], slice[i]
	}
	ip := net.ParseIP(strings.Join(slice, ".")).To4()
	if ip == nil {
		return ""
	}
	return ip.String()
}

// reverse6 reverse the segments and combine them according to RFC3596:
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
// is reversed to 2001:db8::567:89ab
func reverse6(slice []string) string {
	for i := 0; i < len(slice)/2; i++ {
		j := len(slice) - i - 1
		slice[i], slice[j] = slice[j], slice[i]
	}
	slice6 := []string{}
	for i := 0; i < len(slice)/4; i++ {
		slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
	}
	ip := net.ParseIP(strings.Join(slice6, ":")).To16()
	if ip == nil {
		return ""
	}
	return ip.String()
}

const (
	// IP4arpa is the reverse tree suffix for v4 IP addresses.
	IP4arpa = ".in-addr.arpa."
	// IP6arpa is the reverse tree suffix for v6 IP addresses.
	IP6arpa = ".ip6.arpa."
)
<commit_msg>shared/dnsutil: Ends all comments with a full-stop.<commit_after>// Package dnsutil copied from coredns project
// https://github.com/coredns/coredns/blob/master/plugin/pkg/dnsutil/reverse.go
package dnsutil

import (
	"net"
	"strings"
)

// ExtractAddressFromReverse turns a standard PTR reverse record name
// into an IP address. This works for ipv4 or ipv6.
//
// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
// fails the empty string is returned.
func ExtractAddressFromReverse(reverseName string) string {
	search := ""

	f := reverse

	switch {
	case strings.HasSuffix(reverseName, IP4arpa):
		search = strings.TrimSuffix(reverseName, IP4arpa)
	case strings.HasSuffix(reverseName, IP6arpa):
		search = strings.TrimSuffix(reverseName, IP6arpa)
		f = reverse6
	default:
		return ""
	}

	// Reverse the segments and then combine them.
	return f(strings.Split(search, "."))
}

// IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates
// name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4)
// and 2 for ip6.arpa. (IPv6).
func IsReverse(name string) int {
	if strings.HasSuffix(name, IP4arpa) {
		return 1
	}
	if strings.HasSuffix(name, IP6arpa) {
		return 2
	}
	return 0
}

func reverse(slice []string) string {
	for i := 0; i < len(slice)/2; i++ {
		j := len(slice) - i - 1
		slice[i], slice[j] = slice[j], slice[i]
	}
	ip := net.ParseIP(strings.Join(slice, ".")).To4()
	if ip == nil {
		return ""
	}
	return ip.String()
}

// reverse6 reverse the segments and combine them according to RFC3596:
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
// is reversed to 2001:db8::567:89ab.
func reverse6(slice []string) string {
	for i := 0; i < len(slice)/2; i++ {
		j := len(slice) - i - 1
		slice[i], slice[j] = slice[j], slice[i]
	}
	slice6 := []string{}
	for i := 0; i < len(slice)/4; i++ {
		slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
	}
	ip := net.ParseIP(strings.Join(slice6, ":")).To16()
	if ip == nil {
		return ""
	}
	return ip.String()
}

const (
	// IP4arpa is the reverse tree suffix for v4 IP addresses.
	IP4arpa = ".in-addr.arpa."
	// IP6arpa is the reverse tree suffix for v6 IP addresses.
	IP6arpa = ".ip6.arpa."
)
<|endoftext|>


<commit_before>package shell

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEnvironmentSet(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{})

	env.Set("    THIS_IS_THE_BEST   \n\n", "\"IT SURE IS\"\n\n")
	assert.Equal(t, env.Get("THIS_IS_THE_BEST"), "IT SURE IS")

	env.Set("NEW_LINES_STAY_IN_SINGLE_QUOTES", "  'indeed \n it\n does\n'  ")
	assert.Equal(t, env.Get("NEW_LINES_STAY_IN_SINGLE_QUOTES"), "indeed \n it\n does\n")

	env.Set("NEW_LINES_STAY_IN_DOUBLE_QUOTES", "  \"indeed \n it\n does\n\"      ")
	assert.Equal(t, env.Get("NEW_LINES_STAY_IN_DOUBLE_QUOTES"), "indeed \n it\n does\n")

	env.Set("REMOVES_WHITESPACE_FROM_NO_QUOTES", "\n       \n  new line party\n  \n  ")
	assert.Equal(t, env.Get("REMOVES_WHITESPACE_FROM_NO_QUOTES"), "new line party")

	env.Set("DOESNT_AFFECT_QUOTES_INSIDE", `oh "hello" there`)
	assert.Equal(t, env.Get("DOESNT_AFFECT_QUOTES_INSIDE"), `oh "hello" there`)
}

func TestEnvironmentRemove(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{"FOO=bar"})

	assert.Equal(t, env.Get("FOO"), "bar")
	assert.Equal(t, env.Remove("FOO"), "bar")
	assert.Equal(t, env.Get(""), "")
}

func TestEnvironmentMerge(t *testing.T) {
	env1, _ := EnvironmentFromSlice([]string{"FOO=bar"})
	env2, _ := EnvironmentFromSlice([]string{"BAR=foo"})

	env3 := env1.Merge(env2)

	assert.Equal(t, env3.ToSlice(), []string{"FOO=bar", "BAR=foo"})
}

func TestEnvironmentCopy(t *testing.T) {
	env1, _ := EnvironmentFromSlice([]string{"FOO=bar"})
	env2 := env1.Copy()

	assert.Equal(t, env2.ToSlice(), []string{"FOO=bar"})

	env1.Set("FOO", "not-bar-anymore")

	assert.Equal(t, env2.ToSlice(), []string{"FOO=bar"})
}

func TestEnvironmentToSlice(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{"\n\nTHIS_IS_GREAT=\"this is the \n best thing\"      "})

	assert.Equal(t, env.ToSlice(), []string{"THIS_IS_GREAT=\"this is the \\n best thing\""})
}
<commit_msg>Fixed environment tests<commit_after>package shell

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEnvironmentSet(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{})

	env.Set("    THIS_IS_THE_BEST   \n\n", "\"IT SURE IS\"\n\n")
	assert.Equal(t, env.Get("THIS_IS_THE_BEST"), "IT SURE IS")

	env.Set("NEW_LINES_STAY_IN_SINGLE_QUOTES", "  'indeed \n it\n does\n'  ")
	assert.Equal(t, env.Get("NEW_LINES_STAY_IN_SINGLE_QUOTES"), "indeed \n it\n does\n")

	env.Set("NEW_LINES_STAY_IN_DOUBLE_QUOTES", "  \"indeed \n it\n does\n\"      ")
	assert.Equal(t, env.Get("NEW_LINES_STAY_IN_DOUBLE_QUOTES"), "indeed \n it\n does\n")

	env.Set("REMOVES_WHITESPACE_FROM_NO_QUOTES", "\n       \n  new line party\n  \n  ")
	assert.Equal(t, env.Get("REMOVES_WHITESPACE_FROM_NO_QUOTES"), "new line party")

	env.Set("DOESNT_AFFECT_QUOTES_INSIDE", `oh "hello" there`)
	assert.Equal(t, env.Get("DOESNT_AFFECT_QUOTES_INSIDE"), `oh "hello" there`)
}

func TestEnvironmentRemove(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{"FOO=bar"})

	assert.Equal(t, env.Get("FOO"), "bar")
	assert.Equal(t, env.Remove("FOO"), "bar")
	assert.Equal(t, env.Get(""), "")
}

func TestEnvironmentMerge(t *testing.T) {
	env1, _ := EnvironmentFromSlice([]string{"FOO=bar"})
	env2, _ := EnvironmentFromSlice([]string{"BAR=foo"})

	env3 := env1.Merge(env2)

	assert.Equal(t, env3.ToSlice(), []string{"BAR=foo", "FOO=bar"})
}

func TestEnvironmentCopy(t *testing.T) {
	env1, _ := EnvironmentFromSlice([]string{"FOO=bar"})
	env2 := env1.Copy()

	assert.Equal(t, env2.ToSlice(), []string{"FOO=bar"})

	env1.Set("FOO", "not-bar-anymore")

	assert.Equal(t, env2.ToSlice(), []string{"FOO=bar"})
}

func TestEnvironmentToSlice(t *testing.T) {
	env, _ := EnvironmentFromSlice([]string{"\n\nTHIS_IS_GREAT=\"this is the \n best thing\"      "})

	assert.Equal(t, env.ToSlice(), []string{"THIS_IS_GREAT=this is the \n best thing"})
}
<|endoftext|>


<commit_before>/*

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	"github.com/open-policy-agent/opa/ast"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.

// ConstraintTemplateSpec defines the desired state of ConstraintTemplate
type ConstraintTemplateSpec struct {
	CRD     CRD      `json:"crd,omitempty"`
	Targets []Target `json:"targets,omitempty"`
}

type CRD struct {
	Spec CRDSpec `json:"spec,omitempty"`
}

type CRDSpec struct {
	Names      apiextensionsv1beta1.CustomResourceDefinitionNames `json:"names,omitempty"`
	Validation *Validation                                        `json:"validation,omitempty"`
}

type Validation struct {
	OpenAPIV3Schema *apiextensionsv1beta1.JSONSchemaProps `json:"openAPIV3Schema,omitempty"`
}

type Target struct {
	Target string `json:"target,omitempty"`
	Rego   string `json:"rego,omitempty"`
}

// ConstraintTemplateStatus defines the observed state of ConstraintTemplate
type ConstraintTemplateStatus struct {
	Created bool   `json:"created,omitempty"`
	Error   string `json:"error,omitempty"`
	Errors	[]*ast.Error `json:"errors,omitempty"`
	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
	// Important: Run "make" to regenerate code after modifying this file
}

// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ConstraintTemplate is the Schema for the constrainttemplates API
// +k8s:openapi-gen=true
type ConstraintTemplate struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Error  string                   `json:"status,regoErrors"`
	Spec   ConstraintTemplateSpec   `json:"spec,omitempty"`
	Status ConstraintTemplateStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ConstraintTemplateList contains a list of ConstraintTemplate
type ConstraintTemplateList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []ConstraintTemplate `json:"items"`
}

func init() {
	SchemeBuilder.Register(&ConstraintTemplate{}, &ConstraintTemplateList{})
}
<commit_msg>rm extra error string<commit_after>/*

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	"github.com/open-policy-agent/opa/ast"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.

// ConstraintTemplateSpec defines the desired state of ConstraintTemplate
type ConstraintTemplateSpec struct {
	CRD     CRD      `json:"crd,omitempty"`
	Targets []Target `json:"targets,omitempty"`
}

type CRD struct {
	Spec CRDSpec `json:"spec,omitempty"`
}

type CRDSpec struct {
	Names      apiextensionsv1beta1.CustomResourceDefinitionNames `json:"names,omitempty"`
	Validation *Validation                                        `json:"validation,omitempty"`
}

type Validation struct {
	OpenAPIV3Schema *apiextensionsv1beta1.JSONSchemaProps `json:"openAPIV3Schema,omitempty"`
}

type Target struct {
	Target string `json:"target,omitempty"`
	Rego   string `json:"rego,omitempty"`
}

// ConstraintTemplateStatus defines the observed state of ConstraintTemplate
type ConstraintTemplateStatus struct {
	Created bool   `json:"created,omitempty"`
	Error   string `json:"error,omitempty"`
	Errors	[]*ast.Error `json:"errors,omitempty"`
	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
	// Important: Run "make" to regenerate code after modifying this file
}

// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ConstraintTemplate is the Schema for the constrainttemplates API
// +k8s:openapi-gen=true
type ConstraintTemplate struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   ConstraintTemplateSpec   `json:"spec,omitempty"`
	Status ConstraintTemplateStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ConstraintTemplateList contains a list of ConstraintTemplate
type ConstraintTemplateList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []ConstraintTemplate `json:"items"`
}

func init() {
	SchemeBuilder.Register(&ConstraintTemplate{}, &ConstraintTemplateList{})
}
<|endoftext|>


<commit_before>package openstack

import (
	"fmt"
	"log"
	"strings"

	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceComputeFloatingIPAssociateV2() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeFloatingIPAssociateV2Create,
		Read:   resourceComputeFloatingIPAssociateV2Read,
		Delete: resourceComputeFloatingIPAssociateV2Delete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
			},
			"floating_ip": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"fixed_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceComputeFloatingIPAssociateV2Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(GetRegion(d))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	floatingIP := d.Get("floating_ip").(string)
	fixedIP := d.Get("fixed_ip").(string)
	instanceId := d.Get("instance_id").(string)

	associateOpts := floatingips.AssociateOpts{
		FloatingIP: floatingIP,
		FixedIP:    fixedIP,
	}
	log.Printf("[DEBUG] Associate Options: %#v", associateOpts)

	err = floatingips.AssociateInstance(computeClient, instanceId, associateOpts).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error associating Floating IP: %s", err)
	}

	// There's an API call to get this information, but it has been
	// deprecated. The Neutron API could be used, but I'm trying not
	// to mix service APIs. Therefore, a faux ID will be used.
	id := fmt.Sprintf("%s/%s/%s", floatingIP, instanceId, fixedIP)
	d.SetId(id)

	// This API call is synchronous, so Create won't return until the IP
	// is attached. No need to wait for a state.

	return resourceComputeFloatingIPAssociateV2Read(d, meta)
}

func resourceComputeFloatingIPAssociateV2Read(d *schema.ResourceData, meta interface{}) error {
	// Obtain relevant info from parsing the ID
	floatingIP, instanceId, fixedIP, err := parseComputeFloatingIPAssociateId(d.Id())
	if err != nil {
		return err
	}

	d.Set("floating_ip", floatingIP)
	d.Set("instance_id", instanceId)
	d.Set("fixed_ip", fixedIP)
	d.Set("region", GetRegion(d))

	return nil
}

func resourceComputeFloatingIPAssociateV2Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(GetRegion(d))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	floatingIP := d.Get("floating_ip").(string)
	instanceId := d.Get("instance_id").(string)

	disassociateOpts := floatingips.DisassociateOpts{
		FloatingIP: floatingIP,
	}
	log.Printf("[DEBUG] Disssociate Options: %#v", disassociateOpts)

	err = floatingips.DisassociateInstance(computeClient, instanceId, disassociateOpts).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error disassociating floating IP: %s", err)
	}

	return nil
}

func parseComputeFloatingIPAssociateId(id string) (string, string, string, error) {
	idParts := strings.Split(id, "/")
	if len(idParts) < 3 {
		return "", "", "", fmt.Errorf("Unable to determine floating ip association ID")
	}

	floatingIP := idParts[0]
	instanceId := idParts[1]
	fixedIP := idParts[2]

	return floatingIP, instanceId, fixedIP, nil
}
<commit_msg>provider/openstack: Handle disassociating FloatingIP's from a server that's been deleted using the `CheckDeleted` function.<commit_after>package openstack

import (
	"fmt"
	"log"
	"strings"

	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceComputeFloatingIPAssociateV2() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeFloatingIPAssociateV2Create,
		Read:   resourceComputeFloatingIPAssociateV2Read,
		Delete: resourceComputeFloatingIPAssociateV2Delete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
			},
			"floating_ip": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"fixed_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceComputeFloatingIPAssociateV2Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(GetRegion(d))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	floatingIP := d.Get("floating_ip").(string)
	fixedIP := d.Get("fixed_ip").(string)
	instanceId := d.Get("instance_id").(string)

	associateOpts := floatingips.AssociateOpts{
		FloatingIP: floatingIP,
		FixedIP:    fixedIP,
	}
	log.Printf("[DEBUG] Associate Options: %#v", associateOpts)

	err = floatingips.AssociateInstance(computeClient, instanceId, associateOpts).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error associating Floating IP: %s", err)
	}

	// There's an API call to get this information, but it has been
	// deprecated. The Neutron API could be used, but I'm trying not
	// to mix service APIs. Therefore, a faux ID will be used.
	id := fmt.Sprintf("%s/%s/%s", floatingIP, instanceId, fixedIP)
	d.SetId(id)

	// This API call is synchronous, so Create won't return until the IP
	// is attached. No need to wait for a state.

	return resourceComputeFloatingIPAssociateV2Read(d, meta)
}

func resourceComputeFloatingIPAssociateV2Read(d *schema.ResourceData, meta interface{}) error {
	// Obtain relevant info from parsing the ID
	floatingIP, instanceId, fixedIP, err := parseComputeFloatingIPAssociateId(d.Id())
	if err != nil {
		return err
	}

	d.Set("floating_ip", floatingIP)
	d.Set("instance_id", instanceId)
	d.Set("fixed_ip", fixedIP)
	d.Set("region", GetRegion(d))

	return nil
}

func resourceComputeFloatingIPAssociateV2Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(GetRegion(d))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	floatingIP := d.Get("floating_ip").(string)
	instanceId := d.Get("instance_id").(string)

	disassociateOpts := floatingips.DisassociateOpts{
		FloatingIP: floatingIP,
	}
	log.Printf("[DEBUG] Disssociate Options: %#v", disassociateOpts)

	err = floatingips.DisassociateInstance(computeClient, instanceId, disassociateOpts).ExtractErr()
	if err != nil {
		return CheckDeleted(d, err, "floating ip association")
	}

	return nil
}

func parseComputeFloatingIPAssociateId(id string) (string, string, string, error) {
	idParts := strings.Split(id, "/")
	if len(idParts) < 3 {
		return "", "", "", fmt.Errorf("Unable to determine floating ip association ID")
	}

	floatingIP := idParts[0]
	instanceId := idParts[1]
	fixedIP := idParts[2]

	return floatingIP, instanceId, fixedIP, nil
}
<|endoftext|>