<commit_before>package api

import (
	"fmt"
)

// AgentCheck represents a check known to the agent
type AgentCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// AgentService represents a service known to the agent
type AgentService struct {
	ID                string
	Service           string
	Tags              []string
	Port              int
	Address           string
	EnableTagOverride bool
}

// AgentMember represents a cluster member known to the agent
type AgentMember struct {
	Name        string
	Addr        string
	Port        uint16
	Tags        map[string]string
	Status      int
	ProtocolMin uint8
	ProtocolMax uint8
	ProtocolCur uint8
	DelegateMin uint8
	DelegateMax uint8
	DelegateCur uint8
}

// AgentServiceRegistration is used to register a new service
type AgentServiceRegistration struct {
	ID                string   `json:",omitempty"`
	Name              string   `json:",omitempty"`
	Tags              []string `json:",omitempty"`
	Port              int      `json:",omitempty"`
	Address           string   `json:",omitempty"`
	EnableTagOverride bool     `json:",omitempty"`
	Check             *AgentServiceCheck
	Checks            AgentServiceChecks
}

// AgentCheckRegistration is used to register a new check
type AgentCheckRegistration struct {
	ID        string `json:",omitempty"`
	Name      string `json:",omitempty"`
	Notes     string `json:",omitempty"`
	ServiceID string `json:",omitempty"`
	AgentServiceCheck
}

// AgentServiceCheck is used to define a node or service level check
type AgentServiceCheck struct {
	Script            string `json:",omitempty"`
	DockerContainerID string `json:",omitempty"`
	Shell             string `json:",omitempty"` // Only supported for Docker.
	Interval          string `json:",omitempty"`
	Timeout           string `json:",omitempty"`
	TTL               string `json:",omitempty"`
	HTTP              string `json:",omitempty"`
	TCP               string `json:",omitempty"`
	Status            string `json:",omitempty"`
	TLSSkipVerify     string `json:",omitempty"`

	// In Consul 0.7 and later, checks that are associated with a service
	// may also contain this optional DeregisterCriticalServiceAfter field,
	// which is a timeout in the same Go time format as Interval and TTL. If
	// a check is in the critical state for more than this configured value,
	// then its associated service (and all of its associated checks) will
	// automatically be deregistered.
	DeregisterCriticalServiceAfter string `json:",omitempty"`
}
type AgentServiceChecks []*AgentServiceCheck

// Agent can be used to query the Agent endpoints
type Agent struct {
	c *Client

	// cache the node name
	nodeName string
}

// Agent returns a handle to the agent endpoints
func (c *Client) Agent() *Agent {
	return &Agent{c: c}
}

// Self is used to query the agent we are speaking to for
// information about itself
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
	r := a.c.newRequest("GET", "/v1/agent/self")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]map[string]interface{}
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// NodeName is used to get the node name of the agent
func (a *Agent) NodeName() (string, error) {
	if a.nodeName != "" {
		return a.nodeName, nil
	}
	info, err := a.Self()
	if err != nil {
		return "", err
	}
	name := info["Config"]["NodeName"].(string)
	a.nodeName = name
	return name, nil
}

// Checks returns the locally registered checks
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
	r := a.c.newRequest("GET", "/v1/agent/checks")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Services returns the locally registered services
func (a *Agent) Services() (map[string]*AgentService, error) {
	r := a.c.newRequest("GET", "/v1/agent/services")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentService
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Members returns the known gossip members. The WAN
// flag can be used to query a server for WAN members.
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
	r := a.c.newRequest("GET", "/v1/agent/members")
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out []*AgentMember
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// ServiceRegister is used to register a new service with
// the local agent
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/register")
	r.obj = service
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ServiceDeregister is used to deregister a service with
// the local agent
func (a *Agent) ServiceDeregister(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// PassTTL is used to set a TTL check to the passing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) PassTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "pass")
}

// WarnTTL is used to set a TTL check to the warning state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) WarnTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "warn")
}

// FailTTL is used to set a TTL check to the failing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) FailTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "fail")
}

// updateTTL is used to update the TTL of a check. This is the internal
// method that uses the old API that's present in Consul versions prior to
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 and the server endpoints will
// be removed in 0.9.
func (a *Agent) updateTTL(checkID, note, status string) error {
	switch status {
	case "pass":
	case "warn":
	case "fail":
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}
	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.params.Set("note", note)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// checkUpdate is the payload for a PUT for a check update.
type checkUpdate struct {
	// Status is one of the api.Health* states: HealthPassing
	// ("passing"), HealthWarning ("warning"), or HealthCritical
	// ("critical").
	Status string

	// Output is the information to post to the UI for operators as the
	// output of the process that decided to hit the TTL check. This is
	// different from the note field that's associated with the check
	// itself.
	Output string
}

// UpdateTTL is used to update the TTL of a check. This uses the newer API
// that was introduced in Consul 0.6.4 and later. We translate the old status
// strings for compatibility (though a newer version of Consul will still be
// required to use this API).
func (a *Agent) UpdateTTL(checkID, output, status string) error {
	switch status {
	case "pass", HealthPassing:
		status = HealthPassing
	case "warn", HealthWarning:
		status = HealthWarning
	case "fail", HealthCritical:
		status = HealthCritical
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}

	endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.obj = &checkUpdate{
		Status: status,
		Output: output,
	}

	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckRegister is used to register a new check with
// the local agent
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/register")
	r.obj = check
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckDeregister is used to deregister a check with
// the local agent
func (a *Agent) CheckDeregister(checkID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// Join is used to instruct the agent to attempt a join to
// another cluster member
func (a *Agent) Join(addr string, wan bool) error {
	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ForceLeave is used to have the agent eject a failed node
func (a *Agent) ForceLeave(node string) error {
	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableServiceMaintenance toggles service maintenance mode on
// for the given service ID.
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableServiceMaintenance toggles service maintenance mode off
// for the given service ID.
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableNodeMaintenance toggles node maintenance mode on for the
// agent we are connected to.
func (a *Agent) EnableNodeMaintenance(reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableNodeMaintenance toggles node maintenance mode off for the
// agent we are connected to.
func (a *Agent) DisableNodeMaintenance() error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<commit_msg>Adds notes field to API.<commit_after>package api

import (
	"fmt"
)

// AgentCheck represents a check known to the agent
type AgentCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// AgentService represents a service known to the agent
type AgentService struct {
	ID                string
	Service           string
	Tags              []string
	Port              int
	Address           string
	EnableTagOverride bool
}

// AgentMember represents a cluster member known to the agent
type AgentMember struct {
	Name        string
	Addr        string
	Port        uint16
	Tags        map[string]string
	Status      int
	ProtocolMin uint8
	ProtocolMax uint8
	ProtocolCur uint8
	DelegateMin uint8
	DelegateMax uint8
	DelegateCur uint8
}

// AgentServiceRegistration is used to register a new service
type AgentServiceRegistration struct {
	ID                string   `json:",omitempty"`
	Name              string   `json:",omitempty"`
	Tags              []string `json:",omitempty"`
	Port              int      `json:",omitempty"`
	Address           string   `json:",omitempty"`
	EnableTagOverride bool     `json:",omitempty"`
	Check             *AgentServiceCheck
	Checks            AgentServiceChecks
}

// AgentCheckRegistration is used to register a new check
type AgentCheckRegistration struct {
	ID        string `json:",omitempty"`
	Name      string `json:",omitempty"`
	Notes     string `json:",omitempty"`
	ServiceID string `json:",omitempty"`
	AgentServiceCheck
}

// AgentServiceCheck is used to define a node or service level check
type AgentServiceCheck struct {
	Script            string `json:",omitempty"`
	DockerContainerID string `json:",omitempty"`
	Shell             string `json:",omitempty"` // Only supported for Docker.
	Interval          string `json:",omitempty"`
	Timeout           string `json:",omitempty"`
	TTL               string `json:",omitempty"`
	HTTP              string `json:",omitempty"`
	TCP               string `json:",omitempty"`
	Status            string `json:",omitempty"`
	Notes             string `json:",omitempty"`
	TLSSkipVerify     string `json:",omitempty"`

	// In Consul 0.7 and later, checks that are associated with a service
	// may also contain this optional DeregisterCriticalServiceAfter field,
	// which is a timeout in the same Go time format as Interval and TTL. If
	// a check is in the critical state for more than this configured value,
	// then its associated service (and all of its associated checks) will
	// automatically be deregistered.
	DeregisterCriticalServiceAfter string `json:",omitempty"`
}
type AgentServiceChecks []*AgentServiceCheck

// Agent can be used to query the Agent endpoints
type Agent struct {
	c *Client

	// cache the node name
	nodeName string
}

// Agent returns a handle to the agent endpoints
func (c *Client) Agent() *Agent {
	return &Agent{c: c}
}

// Self is used to query the agent we are speaking to for
// information about itself
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
	r := a.c.newRequest("GET", "/v1/agent/self")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]map[string]interface{}
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// NodeName is used to get the node name of the agent
func (a *Agent) NodeName() (string, error) {
	if a.nodeName != "" {
		return a.nodeName, nil
	}
	info, err := a.Self()
	if err != nil {
		return "", err
	}
	name := info["Config"]["NodeName"].(string)
	a.nodeName = name
	return name, nil
}

// Checks returns the locally registered checks
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
	r := a.c.newRequest("GET", "/v1/agent/checks")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Services returns the locally registered services
func (a *Agent) Services() (map[string]*AgentService, error) {
	r := a.c.newRequest("GET", "/v1/agent/services")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentService
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Members returns the known gossip members. The WAN
// flag can be used to query a server for WAN members.
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
	r := a.c.newRequest("GET", "/v1/agent/members")
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out []*AgentMember
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// ServiceRegister is used to register a new service with
// the local agent
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/register")
	r.obj = service
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ServiceDeregister is used to deregister a service with
// the local agent
func (a *Agent) ServiceDeregister(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// PassTTL is used to set a TTL check to the passing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) PassTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "pass")
}

// WarnTTL is used to set a TTL check to the warning state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) WarnTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "warn")
}

// FailTTL is used to set a TTL check to the failing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) FailTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "fail")
}

// updateTTL is used to update the TTL of a check. This is the internal
// method that uses the old API that's present in Consul versions prior to
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 and the server endpoints will
// be removed in 0.9.
func (a *Agent) updateTTL(checkID, note, status string) error {
	switch status {
	case "pass":
	case "warn":
	case "fail":
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}
	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.params.Set("note", note)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// checkUpdate is the payload for a PUT for a check update.
type checkUpdate struct {
	// Status is one of the api.Health* states: HealthPassing
	// ("passing"), HealthWarning ("warning"), or HealthCritical
	// ("critical").
	Status string

	// Output is the information to post to the UI for operators as the
	// output of the process that decided to hit the TTL check. This is
	// different from the note field that's associated with the check
	// itself.
	Output string
}

// UpdateTTL is used to update the TTL of a check. This uses the newer API
// that was introduced in Consul 0.6.4 and later. We translate the old status
// strings for compatibility (though a newer version of Consul will still be
// required to use this API).
func (a *Agent) UpdateTTL(checkID, output, status string) error {
	switch status {
	case "pass", HealthPassing:
		status = HealthPassing
	case "warn", HealthWarning:
		status = HealthWarning
	case "fail", HealthCritical:
		status = HealthCritical
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}

	endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.obj = &checkUpdate{
		Status: status,
		Output: output,
	}

	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckRegister is used to register a new check with
// the local agent
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/register")
	r.obj = check
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckDeregister is used to deregister a check with
// the local agent
func (a *Agent) CheckDeregister(checkID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// Join is used to instruct the agent to attempt a join to
// another cluster member
func (a *Agent) Join(addr string, wan bool) error {
	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ForceLeave is used to have the agent eject a failed node
func (a *Agent) ForceLeave(node string) error {
	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableServiceMaintenance toggles service maintenance mode on
// for the given service ID.
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableServiceMaintenance toggles service maintenance mode off
// for the given service ID.
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableNodeMaintenance toggles node maintenance mode on for the
// agent we are connected to.
func (a *Agent) EnableNodeMaintenance(reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableNodeMaintenance toggles node maintenance mode off for the
// agent we are connected to.
func (a *Agent) DisableNodeMaintenance() error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<|endoftext|>


<commit_before>package ov

import (
	"encoding/json"
	"fmt"
	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

// The Marshal() will omit the bool attibutes below if they are false.
// Please remove the omitempty option and use it as and when required.

type StorageVolumeTemplate struct {
	Category                    string              `json:"category,omitempty"`
	Created                     string              `json:"created,omitempty"`
	CompatibleStorageSystemsUri utils.Nstring       `json:"compatibleStorageSystemsUri,omitempty"`
	Description                 utils.Nstring       `json:"description,omitempty"`
	ETAG                        string              `json:"eTag,omitempty"`
	IsRoot                      bool                `json:"isRoot,omitempty"`
	ScopesURI                   utils.Nstring       `json:"scopesUri,omitempty"`
	Name                        string              `json:"name,omitempty"`
	State                       string              `json:"state,omitempty"`
	Status                      string              `json:"status,omitempty"`
	Type                        string              `json:"type,omitempty"`
	URI                         utils.Nstring       `json:"uri,omitempty"`
	InitialScopeUris            []utils.Nstring     `json:"initialScopeUris,omitempty"`
	RootTemplateUri             utils.Nstring       `json:"rootTemplateUri,omitempty"`
	StoragePoolUri              utils.Nstring       `json:"storagePoolUri,omitempty"`
	Version                     string              `json:"version,omitempty"`
	Uuid                        string              `json:"uuid,omitempty"`
	Family                      string              `json:"family,omitempty"`
	TemplateProperties          *TemplateProperties `json:"properties,omitempty"`
}

type TemplateProperties struct {
	Name                          *TemplatePropertyDatatypeStructString `json:"name,omitempty"`
	StoragePool                   *TemplatePropertyDatatypeStructString `json:"storagePool,omitempty"`
	Size                          *TemplatePropertyDatatypeStructInt    `json:"size,omitempty"`
	ProvisioningType              *TemplatePropertyDatatypeStructString `json:"provisioningType,omitempty"`
	SnapshotPool                  *TemplatePropertyDatatypeStructString `json:"snapshotPool,omitempty"`
	DataTransferLimit             *TemplatePropertyDatatypeStructInt    `json:"dataTransferLimit,omitempty"`
	IsDeduplicated                *TemplatePropertyDatatypeStructBool   `json:"isDeduplicated,omitempty"`
	IsEncrypted                   *TemplatePropertyDatatypeStructBool   `json:"isEncrypted,omitempty"`
	IsPinned                      *TemplatePropertyDatatypeStructBool   `json:"isPinned,omitempty"`
	IopsLimit                     *TemplatePropertyDatatypeStructInt    `json:"iopsLimit,omitempty"`
	Folder                        *TemplatePropertyDatatypeStructString `json:"folder,omitempty"`
	TemplateVersion               *TemplatePropertyDatatypeStructString `json:"templateVersion,omitempty"`
	PerformancePolicy             *TemplatePropertyDatatypeStructString `json:"performancePolicy,omitempty"`
	VolumetSet                    *TemplatePropertyDatatypeStructString `json:"volumeSet,omitempty"`
	Description                   *TemplatePropertyDatatypeStructString `json:"description,omitempty"`
	IsAdaptiveOptimizationEnabled *TemplatePropertyDatatypeStructBool   `json:"isAdaptiveOptimizationEnabled,omitempty"`
	IsCompressed                  *TemplatePropertyDatatypeStructBool   `json:"isCompressed,omitempty"`
	DataProtectionLevel           *TemplatePropertyDatatypeStructString `json:"dataProtectionLevel,omitempty"`
	IsShareable                   *TemplatePropertyDatatypeStructBool   `json:"isShareable,omitempty"`
}

// Struct for properties whose default value is a string
type TemplatePropertyDatatypeStructString struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Maxlength   int           `json:"maxLength,omitempty"`
	Minlength   int           `json:"minLength,omitempty"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     string        `json:"default,omitempty"`
	Minimum     int           `json:"minimum,omitempty"`
	Format      string        `json:"format,omitempty"`
}

// Struct for properties whose default value is an int
type TemplatePropertyDatatypeStructInt struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     int           `json:"default"`
	Maximum     int           `json:"maximum,omitempty"`
	Minimum     int           `json:"minimum,omitempty"`
	Format      string        `json:"format,omitempty"`
}

// Struct for properties whose default value is a bool
type TemplatePropertyDatatypeStructBool struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     bool          `json:"default"`
	Format      string        `json:"format,omitempty"`
}

type Meta struct {
	Locked       bool   `json:"locked"`
	SemanticType string `json:"semanticType,omitempty"`
	CreateOnly   bool   `json:"createOnly,omitempty"`
}

type StorageVolumeTemplateList struct {
	Total       int                     `json:"total,omitempty"`       // "total": 1,
	Count       int                     `json:"count,omitempty"`       // "count": 1,
	Start       int                     `json:"start,omitempty"`       // "start": 0,
	PrevPageURI utils.Nstring           `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	NextPageURI utils.Nstring           `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	URI         utils.Nstring           `json:"uri,omitempty"`         // "uri": "/rest/server-profiles?filter=connectionTemplateUri%20matches%7769cae0-b680-435b-9b87-9b864c81657fsort=name:asc"
	Members     []StorageVolumeTemplate `json:"members,omitempty"`     // "members":[]
}

func (c *OVClient) GetStorageVolumeTemplateByName(name string) (StorageVolumeTemplate, error) {
	var (
		sVolTemplate StorageVolumeTemplate
	)
	sVolTemplates, err := c.GetStorageVolumeTemplates(fmt.Sprintf("name matches '%s'", name), "name:asc", "", "")
	if sVolTemplates.Total > 0 {
		return sVolTemplates.Members[0], err
	} else {
		return sVolTemplate, err
	}
}

func (c *OVClient) GetStorageVolumeTemplates(filter string, sort string, start string, count string) (StorageVolumeTemplateList, error) {
	var (
		uri           = "/rest/storage-volume-templates"
		q             map[string]interface{}
		sVolTemplates StorageVolumeTemplateList
	)
	q = make(map[string]interface{})
	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}

	data, err := c.RestAPICall(rest.GET, uri, nil)

	if err != nil {
		return sVolTemplates, err
	}

	log.Debugf("GetStorageVolumeTemplates %s", data)
	if err := json.Unmarshal([]byte(data), &sVolTemplates); err != nil {
		return sVolTemplates, err
	}
	return sVolTemplates, nil
}

func (c *OVClient) CreateStorageVolumeTemplate(sVolTemplate StorageVolumeTemplate) error {
	log.Infof("Initializing creation of storage volume for %s.", sVolTemplate.Name)
	var (
		uri = "/rest/storage-volume-templates"
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	// Fetching Root Template URI for Storage Template Creation.
	St_pool_URI := sVolTemplate.TemplateProperties.StoragePool.Default
	if St_pool_URI == "" {
		log.Errorf("Default URI not available or not provided correctly in Storage Pool Property")
	}
	s_pool, _ := c.GetStoragePoolByUri(St_pool_URI)
	s_sys, er_sys := c.GetStorageSystemByUri(string(s_pool.StorageSystemUri))
	if er_sys != nil {
		log.Errorf("Error finding Storage System ")
	}
	vol_temp_list, _ := c.GetStorageVolumeTemplates("", "name:desc", "", "")
	for i := 0; i < len(vol_temp_list.Members); i++ {
		if vol_temp_list.Members[i].IsRoot == true && vol_temp_list.Members[i].Family == s_sys.Family {
			sVolTemplate.RootTemplateUri = vol_temp_list.Members[i].URI
			break
		}
	}
	if sVolTemplate.RootTemplateUri == "" {
		log.Errorf("Not able to fetch correct Root Template URI")
	}

	log.Debugf("REST : %s \n %+v\n", uri, sVolTemplate)
	data, err := c.RestAPICall(rest.POST, uri, sVolTemplate)

	if err != nil {
		log.Errorf("Error submitting new storage volume template request: %s", err)
		return err
	}

	log.Debugf("Response New StorageVolumeTemplate %s", data)

	return nil
}

func (c *OVClient) DeleteStorageVolumeTemplate(name string) error {
	var (
		sVolTemplate StorageVolumeTemplate
		err          error
		uri          string
	)

	sVolTemplate, err = c.GetStorageVolumeTemplateByName(name)
	if err != nil {
		return err
	}
	if sVolTemplate.Name != "" {
		log.Debugf("REST : %s \n %+v\n", sVolTemplate.URI, sVolTemplate)
		uri = sVolTemplate.URI.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			return err
		}
		_, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete storage volume template request: %s", err)
			return err
		}

		return nil
	} else {
		log.Infof("StorageVolumeTemplate could not be found to delete, %s, skipping delete ...", name)
	}
	return nil
}

func (c *OVClient) UpdateStorageVolumeTemplate(sVolTemplate StorageVolumeTemplate) error {
	log.Infof("Initializing update of storage volume to %s.", sVolTemplate.Name)
	var (
		uri = sVolTemplate.URI.String()
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	log.Debugf("REST : %s \n %+v\n", uri, sVolTemplate)
	_, err := c.RestAPICall(rest.PUT, uri, sVolTemplate)
	if err != nil {
		log.Errorf("Error submitting update StorageVolumeTemplate request: %s", err)
		return err
	}
	return nil
}
<commit_msg>Added get root template<commit_after>package ov

import (
	"encoding/json"
	"fmt"

	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

// The Marshal() will omit the bool attibutes below if they are false.
// Please remove the omitempty option and use it as and when required.

type StorageVolumeTemplate struct {
	Category                    string              `json:"category,omitempty"`
	Created                     string              `json:"created,omitempty"`
	CompatibleStorageSystemsUri utils.Nstring       `json:"compatibleStorageSystemsUri,omitempty"`
	Description                 utils.Nstring       `json:"description,omitempty"`
	ETAG                        string              `json:"eTag,omitempty"`
	IsRoot                      bool                `json:"isRoot,omitempty"`
	ScopesURI                   utils.Nstring       `json:"scopesUri,omitempty"`
	Name                        string              `json:"name,omitempty"`
	State                       string              `json:"state,omitempty"`
	Status                      string              `json:"status,omitempty"`
	Type                        string              `json:"type,omitempty"`
	URI                         utils.Nstring       `json:"uri,omitempty"`
	InitialScopeUris            []utils.Nstring     `json:"initialScopeUris,omitempty"`
	RootTemplateUri             utils.Nstring       `json:"rootTemplateUri,omitempty"`
	StoragePoolUri              utils.Nstring       `json:"storagePoolUri,omitempty"`
	Version                     string              `json:"version,omitempty"`
	Uuid                        string              `json:"uuid,omitempty"`
	Family                      string              `json:"family,omitempty"`
	TemplateProperties          *TemplateProperties `json:"properties,omitempty"`
}

type TemplateProperties struct {
	Name                          *TemplatePropertyDatatypeStructString `json:"name,omitempty"`
	StoragePool                   *TemplatePropertyDatatypeStructString `json:"storagePool,omitempty"`
	Size                          *TemplatePropertyDatatypeStructInt    `json:"size,omitempty"`
	ProvisioningType              *TemplatePropertyDatatypeStructString `json:"provisioningType,omitempty"`
	SnapshotPool                  *TemplatePropertyDatatypeStructString `json:"snapshotPool,omitempty"`
	DataTransferLimit             *TemplatePropertyDatatypeStructInt    `json:"dataTransferLimit,omitempty"`
	IsDeduplicated                *TemplatePropertyDatatypeStructBool   `json:"isDeduplicated,omitempty"`
	IsEncrypted                   *TemplatePropertyDatatypeStructBool   `json:"isEncrypted,omitempty"`
	IsPinned                      *TemplatePropertyDatatypeStructBool   `json:"isPinned,omitempty"`
	IopsLimit                     *TemplatePropertyDatatypeStructInt    `json:"iopsLimit,omitempty"`
	Folder                        *TemplatePropertyDatatypeStructString `json:"folder,omitempty"`
	TemplateVersion               *TemplatePropertyDatatypeStructString `json:"templateVersion,omitempty"`
	PerformancePolicy             *TemplatePropertyDatatypeStructString `json:"performancePolicy,omitempty"`
	VolumetSet                    *TemplatePropertyDatatypeStructString `json:"volumeSet,omitempty"`
	Description                   *TemplatePropertyDatatypeStructString `json:"description,omitempty"`
	IsAdaptiveOptimizationEnabled *TemplatePropertyDatatypeStructBool   `json:"isAdaptiveOptimizationEnabled,omitempty"`
	IsCompressed                  *TemplatePropertyDatatypeStructBool   `json:"isCompressed,omitempty"`
	DataProtectionLevel           *TemplatePropertyDatatypeStructString `json:"dataProtectionLevel,omitempty"`
	IsShareable                   *TemplatePropertyDatatypeStructBool   `json:"isShareable,omitempty"`
}

// Struct for properties whose default value is a string
type TemplatePropertyDatatypeStructString struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Maxlength   int           `json:"maxLength,omitempty"`
	Minlength   int           `json:"minLength,omitempty"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     string        `json:"default,omitempty"`
	Minimum     int           `json:"minimum,omitempty"`
	Format      string        `json:"format,omitempty"`
}

// Struct for properties whose default value is an int
type TemplatePropertyDatatypeStructInt struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     int           `json:"default"`
	Maximum     int           `json:"maximum,omitempty"`
	Minimum     int           `json:"minimum,omitempty"`
	Format      string        `json:"format,omitempty"`
}

// Struct for properties whose default value is a bool
type TemplatePropertyDatatypeStructBool struct {
	Meta        *Meta         `json:"meta,omitempty"`
	Type        string        `json:"type,omitempty"`
	Title       string        `json:"title,omitempty"`
	Required    bool          `json:"required"`
	Description utils.Nstring `json:"description"`
	Enum        []string      `json:"enum,omitempty"`
	Default     bool          `json:"default"`
	Format      string        `json:"format,omitempty"`
}

type Meta struct {
	Locked       bool   `json:"locked"`
	SemanticType string `json:"semanticType,omitempty"`
	CreateOnly   bool   `json:"createOnly,omitempty"`
}

type StorageVolumeTemplateList struct {
	Total       int                     `json:"total,omitempty"`       // "total": 1,
	Count       int                     `json:"count,omitempty"`       // "count": 1,
	Start       int                     `json:"start,omitempty"`       // "start": 0,
	PrevPageURI utils.Nstring           `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	NextPageURI utils.Nstring           `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	URI         utils.Nstring           `json:"uri,omitempty"`         // "uri": "/rest/server-profiles?filter=connectionTemplateUri%20matches%7769cae0-b680-435b-9b87-9b864c81657fsort=name:asc"
	Members     []StorageVolumeTemplate `json:"members,omitempty"`     // "members":[]
}

func (c *OVClient) GetStorageVolumeTemplateByName(name string) (StorageVolumeTemplate, error) {
	var (
		sVolTemplate StorageVolumeTemplate
	)
	sVolTemplates, err := c.GetStorageVolumeTemplates(fmt.Sprintf("name matches '%s'", name), "name:asc", "", "")
	if sVolTemplates.Total > 0 {
		return sVolTemplates.Members[0], err
	} else {
		return sVolTemplate, err
	}
}

func (c *OVClient) GetRootStorageVolumeTemplate() (StorageVolumeTemplate, error) {
	var (
		sVolTemplate StorageVolumeTemplate
	)
	sVolTemplates, err := c.GetStorageVolumeTemplates("isRoot = 'true'", "name:asc", "", "")
	if sVolTemplates.Total > 0 {
		return sVolTemplates.Members[0], err
	} else {
		return sVolTemplate, err
	}
}

func (c *OVClient) GetStorageVolumeTemplates(filter string, sort string, start string, count string) (StorageVolumeTemplateList, error) {
	var (
		uri           = "/rest/storage-volume-templates"
		q             map[string]interface{}
		sVolTemplates StorageVolumeTemplateList
	)
	q = make(map[string]interface{})
	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}

	data, err := c.RestAPICall(rest.GET, uri, nil)

	if err != nil {
		return sVolTemplates, err
	}

	log.Debugf("GetStorageVolumeTemplates %s", data)
	if err := json.Unmarshal([]byte(data), &sVolTemplates); err != nil {
		return sVolTemplates, err
	}
	return sVolTemplates, nil
}

func (c *OVClient) CreateStorageVolumeTemplate(sVolTemplate StorageVolumeTemplate) error {
	log.Infof("Initializing creation of storage volume for %s.", sVolTemplate.Name)
	var (
		uri = "/rest/storage-volume-templates"
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	// Fetching Root Template URI for Storage Template Creation.
	St_pool_URI := sVolTemplate.TemplateProperties.StoragePool.Default
	if St_pool_URI == "" {
		log.Errorf("Default URI not available or not provided correctly in Storage Pool Property")
	}
	s_pool, _ := c.GetStoragePoolByUri(St_pool_URI)
	s_sys, er_sys := c.GetStorageSystemByUri(string(s_pool.StorageSystemUri))
	if er_sys != nil {
		log.Errorf("Error finding Storage System ")
	}
	vol_temp_list, _ := c.GetStorageVolumeTemplates("", "name:desc", "", "")
	for i := 0; i < len(vol_temp_list.Members); i++ {
		if vol_temp_list.Members[i].IsRoot == true && vol_temp_list.Members[i].Family == s_sys.Family {
			sVolTemplate.RootTemplateUri = vol_temp_list.Members[i].URI
			break
		}
	}
	if sVolTemplate.RootTemplateUri == "" {
		log.Errorf("Not able to fetch correct Root Template URI")
	}

	log.Debugf("REST : %s \n %+v\n", uri, sVolTemplate)
	data, err := c.RestAPICall(rest.POST, uri, sVolTemplate)

	if err != nil {
		log.Errorf("Error submitting new storage volume template request: %s", err)
		return err
	}

	log.Debugf("Response New StorageVolumeTemplate %s", data)

	return nil
}

func (c *OVClient) DeleteStorageVolumeTemplate(name string) error {
	var (
		sVolTemplate StorageVolumeTemplate
		err          error
		uri          string
	)

	sVolTemplate, err = c.GetStorageVolumeTemplateByName(name)
	if err != nil {
		return err
	}
	if sVolTemplate.Name != "" {
		log.Debugf("REST : %s \n %+v\n", sVolTemplate.URI, sVolTemplate)
		uri = sVolTemplate.URI.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			return err
		}
		_, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete storage volume template request: %s", err)
			return err
		}

		return nil
	} else {
		log.Infof("StorageVolumeTemplate could not be found to delete, %s, skipping delete ...", name)
	}
	return nil
}

func (c *OVClient) UpdateStorageVolumeTemplate(sVolTemplate StorageVolumeTemplate) error {
	log.Infof("Initializing update of storage volume to %s.", sVolTemplate.Name)
	var (
		uri = sVolTemplate.URI.String()
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	log.Debugf("REST : %s \n %+v\n", uri, sVolTemplate)
	_, err := c.RestAPICall(rest.PUT, uri, sVolTemplate)
	if err != nil {
		log.Errorf("Error submitting update StorageVolumeTemplate request: %s", err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcutil"
)

// maybeAcceptBlock potentially accepts a block into the memory block chain.
// It performs several validation checks which depend on its position within
// the block chain before adding it.  The block is expected to have already gone
// through ProcessBlock before calling this function with it.
//
// The flags modify the behavior of this function as follows:
//  - BFFastAdd: The somewhat expensive BIP0034 validation is not performed.
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
	fastAdd := flags&BFFastAdd == BFFastAdd

	// Get a block node for the block previous to this one.  Will be nil
	// if this is the genesis block.
	prevNode, err := b.getPrevNodeFromBlock(block)
	if err != nil {
		log.Errorf("getPrevNodeFromBlock: %v", err)
		return err
	}

	// The height of this block is one more than the referenced previous
	// block.
	blockHeight := int64(0)
	if prevNode != nil {
		blockHeight = prevNode.height + 1
	}
	block.SetHeight(blockHeight)

	blockHeader := &block.MsgBlock().Header
	if !fastAdd {
		// Ensure the difficulty specified in the block header matches
		// the calculated difficulty based on the previous block and
		// difficulty retarget rules.
		expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
			block.MsgBlock().Header.Timestamp)
		if err != nil {
			return err
		}
		blockDifficulty := blockHeader.Bits
		if blockDifficulty != expectedDifficulty {
			str := "block difficulty of %d is not the expected value of %d"
			str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty)
			return ruleError(ErrUnexpectedDifficulty, str)
		}

		// Ensure the timestamp for the block header is after the
		// median time of the last several blocks (medianTimeBlocks).
		medianTime, err := b.calcPastMedianTime(prevNode)
		if err != nil {
			log.Errorf("calcPastMedianTime: %v", err)
			return err
		}
		if !blockHeader.Timestamp.After(medianTime) {
			str := "block timestamp of %v is not after expected %v"
			str = fmt.Sprintf(str, blockHeader.Timestamp,
				medianTime)
			return ruleError(ErrTimeTooOld, str)
		}

		// Ensure all transactions in the block are finalized.
		for _, tx := range block.Transactions() {
			if !IsFinalizedTransaction(tx, blockHeight,
				blockHeader.Timestamp) {
				str := fmt.Sprintf("block contains "+
					"unfinalized transaction %v", tx.Sha())
				return ruleError(ErrUnfinalizedTx, str)
			}
		}

	}

	// Ensure chain matches up to predetermined checkpoints.
	// It's safe to ignore the error on Sha since it's already cached.
	blockHash, _ := block.Sha()
	if !b.verifyCheckpoint(blockHeight, blockHash) {
		// TODO(davec): This should probably be a distinct error type
		// (maybe CheckpointError).  Since this error shouldn't happen
		// unless the peer is connected to a rogue network serving up an
		// alternate chain, the caller would likely need to react by
		// disconnecting peers and rolling back the chain to the last
		// known good point.
		str := fmt.Sprintf("block at height %d does not match "+
			"checkpoint hash", blockHeight)
		return ruleError(ErrBadCheckpoint, str)
	}

	// Find the previous checkpoint and prevent blocks which fork the main
	// chain before it.  This prevents storage of new, otherwise valid,
	// blocks which build off of old blocks that are likely at a much easier
	// difficulty and therefore could be used to waste cache and disk space.
	checkpointBlock, err := b.findPreviousCheckpoint()
	if err != nil {
		return err
	}
	if checkpointBlock != nil && blockHeight < checkpointBlock.Height() {
		str := fmt.Sprintf("block at height %d forks the main chain "+
			"before the previous checkpoint at height %d",
			blockHeight, checkpointBlock.Height())
		return ruleError(ErrForkTooOld, str)
	}

	if !fastAdd {
		// Reject version 1 blocks once a majority of the network has
		// upgraded.  This is part of BIP0034.
		if blockHeader.Version == 1 {
			if b.isMajorityVersion(2, prevNode,
				b.netParams.BlockV1RejectNumRequired,
				b.netParams.BlockV1RejectNumToCheck) {

				str := "new blocks with version %d are no " +
					"longer valid"
				str = fmt.Sprintf(str, blockHeader.Version)
				return ruleError(ErrBlockVersionTooOld, str)
			}
		}

		// Ensure coinbase starts with serialized block heights for
		// blocks whose version is the serializedHeightVersion or
		// newer once a majority of the network has upgraded.  This is
		// part of BIP0034.
		if blockHeader.Version >= serializedHeightVersion {
			if b.isMajorityVersion(serializedHeightVersion,
				prevNode,
				b.netParams.CoinbaseBlockHeightNumRequired,
				b.netParams.CoinbaseBlockHeightNumToCheck) {

				expectedHeight := int64(0)
				if prevNode != nil {
					expectedHeight = prevNode.height + 1
				}
				coinbaseTx := block.Transactions()[0]
				err := checkSerializedHeight(coinbaseTx,
					expectedHeight)
				if err != nil {
					return err
				}
			}
		}
	}

	// Prune block nodes which are no longer needed before creating
	// a new node.
	err = b.pruneBlockNodes()
	if err != nil {
		return err
	}

	// Create a new block node for the block and add it to the in-memory
	// block chain (could be either a side chain or the main chain).
	newNode := newBlockNode(blockHeader, blockHash, blockHeight)
	if prevNode != nil {
		newNode.parent = prevNode
		newNode.height = blockHeight
		newNode.workSum.Add(prevNode.workSum, newNode.workSum)
	}

	// Connect the passed block to the chain while respecting proper chain
	// selection according to the chain with the most proof of work.  This
	// also handles validation of the transaction scripts.
	err = b.connectBestChain(newNode, block, flags)
	if err != nil {
		return err
	}

	// Notify the caller that the new block was accepted into the block
	// chain.  The caller would typically want to react by relaying the
	// inventory to other peers.
	b.sendNotification(NTBlockAccepted, block)

	return nil
}
<commit_msg>Remove TODO that is complete.<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcutil"
)

// maybeAcceptBlock potentially accepts a block into the memory block chain.
// It performs several validation checks which depend on its position within
// the block chain before adding it.  The block is expected to have already gone
// through ProcessBlock before calling this function with it.
//
// The flags modify the behavior of this function as follows:
//  - BFFastAdd: The somewhat expensive BIP0034 validation is not performed.
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
	fastAdd := flags&BFFastAdd == BFFastAdd

	// Get a block node for the block previous to this one.  Will be nil
	// if this is the genesis block.
	prevNode, err := b.getPrevNodeFromBlock(block)
	if err != nil {
		log.Errorf("getPrevNodeFromBlock: %v", err)
		return err
	}

	// The height of this block is one more than the referenced previous
	// block.
	blockHeight := int64(0)
	if prevNode != nil {
		blockHeight = prevNode.height + 1
	}
	block.SetHeight(blockHeight)

	blockHeader := &block.MsgBlock().Header
	if !fastAdd {
		// Ensure the difficulty specified in the block header matches
		// the calculated difficulty based on the previous block and
		// difficulty retarget rules.
		expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
			block.MsgBlock().Header.Timestamp)
		if err != nil {
			return err
		}
		blockDifficulty := blockHeader.Bits
		if blockDifficulty != expectedDifficulty {
			str := "block difficulty of %d is not the expected value of %d"
			str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty)
			return ruleError(ErrUnexpectedDifficulty, str)
		}

		// Ensure the timestamp for the block header is after the
		// median time of the last several blocks (medianTimeBlocks).
		medianTime, err := b.calcPastMedianTime(prevNode)
		if err != nil {
			log.Errorf("calcPastMedianTime: %v", err)
			return err
		}
		if !blockHeader.Timestamp.After(medianTime) {
			str := "block timestamp of %v is not after expected %v"
			str = fmt.Sprintf(str, blockHeader.Timestamp,
				medianTime)
			return ruleError(ErrTimeTooOld, str)
		}

		// Ensure all transactions in the block are finalized.
		for _, tx := range block.Transactions() {
			if !IsFinalizedTransaction(tx, blockHeight,
				blockHeader.Timestamp) {
				str := fmt.Sprintf("block contains "+
					"unfinalized transaction %v", tx.Sha())
				return ruleError(ErrUnfinalizedTx, str)
			}
		}

	}

	// Ensure chain matches up to predetermined checkpoints.
	// It's safe to ignore the error on Sha since it's already cached.
	blockHash, _ := block.Sha()
	if !b.verifyCheckpoint(blockHeight, blockHash) {
		str := fmt.Sprintf("block at height %d does not match "+
			"checkpoint hash", blockHeight)
		return ruleError(ErrBadCheckpoint, str)
	}

	// Find the previous checkpoint and prevent blocks which fork the main
	// chain before it.  This prevents storage of new, otherwise valid,
	// blocks which build off of old blocks that are likely at a much easier
	// difficulty and therefore could be used to waste cache and disk space.
	checkpointBlock, err := b.findPreviousCheckpoint()
	if err != nil {
		return err
	}
	if checkpointBlock != nil && blockHeight < checkpointBlock.Height() {
		str := fmt.Sprintf("block at height %d forks the main chain "+
			"before the previous checkpoint at height %d",
			blockHeight, checkpointBlock.Height())
		return ruleError(ErrForkTooOld, str)
	}

	if !fastAdd {
		// Reject version 1 blocks once a majority of the network has
		// upgraded.  This is part of BIP0034.
		if blockHeader.Version == 1 {
			if b.isMajorityVersion(2, prevNode,
				b.netParams.BlockV1RejectNumRequired,
				b.netParams.BlockV1RejectNumToCheck) {

				str := "new blocks with version %d are no " +
					"longer valid"
				str = fmt.Sprintf(str, blockHeader.Version)
				return ruleError(ErrBlockVersionTooOld, str)
			}
		}

		// Ensure coinbase starts with serialized block heights for
		// blocks whose version is the serializedHeightVersion or
		// newer once a majority of the network has upgraded.  This is
		// part of BIP0034.
		if blockHeader.Version >= serializedHeightVersion {
			if b.isMajorityVersion(serializedHeightVersion,
				prevNode,
				b.netParams.CoinbaseBlockHeightNumRequired,
				b.netParams.CoinbaseBlockHeightNumToCheck) {

				expectedHeight := int64(0)
				if prevNode != nil {
					expectedHeight = prevNode.height + 1
				}
				coinbaseTx := block.Transactions()[0]
				err := checkSerializedHeight(coinbaseTx,
					expectedHeight)
				if err != nil {
					return err
				}
			}
		}
	}

	// Prune block nodes which are no longer needed before creating
	// a new node.
	err = b.pruneBlockNodes()
	if err != nil {
		return err
	}

	// Create a new block node for the block and add it to the in-memory
	// block chain (could be either a side chain or the main chain).
	newNode := newBlockNode(blockHeader, blockHash, blockHeight)
	if prevNode != nil {
		newNode.parent = prevNode
		newNode.height = blockHeight
		newNode.workSum.Add(prevNode.workSum, newNode.workSum)
	}

	// Connect the passed block to the chain while respecting proper chain
	// selection according to the chain with the most proof of work.  This
	// also handles validation of the transaction scripts.
	err = b.connectBestChain(newNode, block, flags)
	if err != nil {
		return err
	}

	// Notify the caller that the new block was accepted into the block
	// chain.  The caller would typically want to react by relaying the
	// inventory to other peers.
	b.sendNotification(NTBlockAccepted, block)

	return nil
}
<|endoftext|>


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

package script

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/control-center/serviced/commons"
	"github.com/zenoss/glog"
)

func evalEmpty(r *runner, n node) error {
	glog.V(1).Infof("nothing to eval: %s", n.line)
	return nil
}
func evalSnapshot(r *runner, n node) error {
	glog.V(0).Info("performing snapshot")

	if r.snapshot == nil {
		return fmt.Errorf("no snapshot function provided for %s", SNAPSHOT)
	}
	if r.restore == nil {
		return fmt.Errorf("no restore function provided for %s", SNAPSHOT)
	}

	tID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SNAPSHOT)
	}

	mySnapshotID, err := r.snapshot(tID)
	if err != nil {
		return err
	}
	r.snapshotID = mySnapshotID //keep track of the latest snapshot to rollback to
	glog.V(0).Infof("snapshot id: %s", mySnapshotID)

	exitFunc := func(failed bool) {
		if failed && r.snapshotID == mySnapshotID {
			glog.Infof("restoring snapshot %s", mySnapshotID)
			if err := r.restore(mySnapshotID, true); err != nil {
				glog.Errorf("failed restoring snapshot %s: %v", mySnapshotID, err)

			}
		}
	}
	r.addExitFunction(exitFunc)
	return nil
}

func evalUSE(r *runner, n node) error {
	imageName := n.args[0]
	glog.V(0).Infof("preparing to use image: %s", imageName)
	svcID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", USE)
	}

	imageID, err := commons.ParseImageID(imageName)
	if err != nil {
		return err
	}
	if imageID.Tag == "" {
		imageID.Tag = "latest"
	}
	glog.Infof("pulling image %s, this may take a while...", imageID)
	if err := r.pullImage(imageID.String()); err != nil {
		return fmt.Errorf("unable to pull image %s", imageID)
	}

	//verify image has been pulled
	img, err := r.findImage(imageID.String(), false)
	if err != nil {
		err = fmt.Errorf("could not look up image %s: %s. Check your docker login and retry service deployment.", imageID, err)
		return err
	}

	//Tag images to latest all images
	var newTag *commons.ImageID

	newTag, err = renameImageID(r.config.DockerRegistry, svcID, imageID.String(), "latest")
	if err != nil {
		return err
	}
	glog.Infof("tagging image %s to %s ", imageName, newTag)
	if _, err = r.tagImage(img, newTag.String()); err != nil {
		glog.Errorf("could not tag image: %s (%v)", imageName, err)
		return err
	}
	return nil
}

func evalSvcWait(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_WAIT)
	}

	if r.svcWait == nil {
		return fmt.Errorf("no service wait function for %s", SVC_WAIT)
	}

	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_WAIT)
	}

	var svcIDs []string
	var stateIdx = -1
	for i, arg := range(n.args) {
		if arg == "started"  || arg == "stopped" || arg == "paused" {
			stateIdx = i
			break
		}
		svcPath := arg
		svcID, err := r.svcFromPath(tenantID, arg)
		if err != nil {
			return err
		}
		if svcID == "" {
			return fmt.Errorf("no service id found for %s", svcPath)
		}
		svcIDs = append(svcIDs, svcID)
	}

	state := ServiceState(n.args[stateIdx])

	timeout := uint32(0)
	hasTimeout := len(n.args) == stateIdx + 2
	if hasTimeout {
		var timeout64 uint64
		var err error
		lastArg := n.args[len(n.args) - 1]
		if timeout64, err = strconv.ParseUint(lastArg, 10, 32); err != nil {
			return fmt.Errorf("Unable to parse timeout value %s: %s", lastArg, err)
		}
		timeout = uint32(timeout64)
	}

	plural := ""
	if stateIdx > 1 {
		plural = "s"
	}
	glog.Infof("waiting %d for service%s %s to be %s", timeout, plural, strings.Join(n.args[:stateIdx], ", "), state)
	if err := r.svcWait(svcIDs, state, timeout); err != nil {
		return err
	}

	return nil
}

func evalSvcStart(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_START)
	}

	if r.svcStart == nil {
		return fmt.Errorf("no service start function for %s", SVC_START)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_START)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("starting service %s %s", svcPath, svcID)
	if err := r.svcStart(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcStop(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_STOP)
	}

	if r.svcStop == nil {
		return fmt.Errorf("no service stop function for %s", SVC_STOP)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_STOP)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("stopping service %s %s", svcPath, svcID)
	if err := r.svcStop(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcRestart(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RESTART)
	}

	if r.svcRestart == nil {
		return fmt.Errorf("no service restart function for %s", SVC_RESTART)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RESTART)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("restarting service %s %s", svcPath, svcID)
	if err := r.svcRestart(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcRun(r *runner, n node) error {
	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RUN)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RUN)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	n.args[0] = svcID

	glog.V(0).Infof("running: serviced service run %s", strings.Join(n.args, " "))
	args := []string{"service", "run"}
	args = append(args, n.args...)
	if err := r.execCommand("serviced", args...); err != nil {
		return err
	}

	return nil
}

func evalSvcExec(r *runner, n node) error {
	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RUN)
	}

	svcPath := n.args[1]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RUN)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	n.args[1] = svcID
	// "HMS-YMD_svcID" will be the name of the container
	containerName := time.Now().Format("150405-20060102") + "_" + svcID

	glog.V(0).Infof("running: serviced service shell -s %s %s", containerName, strings.Join(n.args[1:], " "))
	args := []string{"service", "shell", "-s", containerName}
	args = append(args, n.args[1:]...)
	if err := r.execCommand("serviced", args...); err != nil {
		return err
	}

	// Now commit the container (if 'COMMIT' was specified)
	switch n.args[0] {
	case "COMMIT":
		glog.V(0).Infof("committing container %s", containerName)
		if _, err := r.commitContainer(containerName); err != nil {
			return err
		}
	}
	return nil
}

func evalDependency(r *runner, n node) error {
	glog.V(0).Infof("checking serviced dependency: %s", n.args[0])
	glog.V(0).Info("dependency check for serviced not implemented, skipping...")
	return nil
}

func evalRequireSvc(r *runner, n node) error {
	if r.tenantIDLookup == nil {
		return fmt.Errorf("no tenant lookup function provided for %s", REQUIRE_SVC)
	}
	glog.V(0).Infof("checking service requirement")
	if r.config.ServiceID == "" {
		return errors.New("no service id specified")
	}
	glog.V(0).Infof("verifying service %s", r.config.ServiceID)
	//lookup tenant id for service
	tID, err := r.tenantIDLookup(r.config.ServiceID)
	if err != nil {
		return err
	}
	glog.V(0).Infof("found %s tenant id for service %s", tID, r.config.ServiceID)
	r.env["TENANT_ID"] = tID
	return nil
}
<commit_msg>Cleanup intermediate SVC_EXEC snapshots<commit_after>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package script

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/control-center/serviced/commons"
	"github.com/zenoss/glog"
)

func evalEmpty(r *runner, n node) error {
	glog.V(1).Infof("nothing to eval: %s", n.line)
	return nil
}
func evalSnapshot(r *runner, n node) error {
	glog.V(0).Info("performing snapshot")

	if r.snapshot == nil {
		return fmt.Errorf("no snapshot function provided for %s", SNAPSHOT)
	}
	if r.restore == nil {
		return fmt.Errorf("no restore function provided for %s", SNAPSHOT)
	}

	tID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SNAPSHOT)
	}

	mySnapshotID, err := r.snapshot(tID)
	if err != nil {
		return err
	}
	r.snapshotID = mySnapshotID //keep track of the latest snapshot to rollback to
	glog.V(0).Infof("snapshot id: %s", mySnapshotID)

	exitFunc := func(failed bool) {
		if failed && r.snapshotID == mySnapshotID {
			glog.Infof("restoring snapshot %s", mySnapshotID)
			if err := r.restore(mySnapshotID, true); err != nil {
				glog.Errorf("failed restoring snapshot %s: %v", mySnapshotID, err)

			}
		}
	}
	r.addExitFunction(exitFunc)
	return nil
}

func evalUSE(r *runner, n node) error {
	imageName := n.args[0]
	glog.V(0).Infof("preparing to use image: %s", imageName)
	svcID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", USE)
	}

	imageID, err := commons.ParseImageID(imageName)
	if err != nil {
		return err
	}
	if imageID.Tag == "" {
		imageID.Tag = "latest"
	}
	glog.Infof("pulling image %s, this may take a while...", imageID)
	if err := r.pullImage(imageID.String()); err != nil {
		return fmt.Errorf("unable to pull image %s", imageID)
	}

	//verify image has been pulled
	img, err := r.findImage(imageID.String(), false)
	if err != nil {
		err = fmt.Errorf("could not look up image %s: %s. Check your docker login and retry service deployment.", imageID, err)
		return err
	}

	//Tag images to latest all images
	var newTag *commons.ImageID

	newTag, err = renameImageID(r.config.DockerRegistry, svcID, imageID.String(), "latest")
	if err != nil {
		return err
	}
	glog.Infof("tagging image %s to %s ", imageName, newTag)
	if _, err = r.tagImage(img, newTag.String()); err != nil {
		glog.Errorf("could not tag image: %s (%v)", imageName, err)
		return err
	}
	return nil
}

func evalSvcWait(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_WAIT)
	}

	if r.svcWait == nil {
		return fmt.Errorf("no service wait function for %s", SVC_WAIT)
	}

	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_WAIT)
	}

	var svcIDs []string
	var stateIdx = -1
	for i, arg := range(n.args) {
		if arg == "started"  || arg == "stopped" || arg == "paused" {
			stateIdx = i
			break
		}
		svcPath := arg
		svcID, err := r.svcFromPath(tenantID, arg)
		if err != nil {
			return err
		}
		if svcID == "" {
			return fmt.Errorf("no service id found for %s", svcPath)
		}
		svcIDs = append(svcIDs, svcID)
	}

	state := ServiceState(n.args[stateIdx])

	timeout := uint32(0)
	hasTimeout := len(n.args) == stateIdx + 2
	if hasTimeout {
		var timeout64 uint64
		var err error
		lastArg := n.args[len(n.args) - 1]
		if timeout64, err = strconv.ParseUint(lastArg, 10, 32); err != nil {
			return fmt.Errorf("Unable to parse timeout value %s: %s", lastArg, err)
		}
		timeout = uint32(timeout64)
	}

	plural := ""
	if stateIdx > 1 {
		plural = "s"
	}
	glog.Infof("waiting %d for service%s %s to be %s", timeout, plural, strings.Join(n.args[:stateIdx], ", "), state)
	if err := r.svcWait(svcIDs, state, timeout); err != nil {
		return err
	}

	return nil
}

func evalSvcStart(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_START)
	}

	if r.svcStart == nil {
		return fmt.Errorf("no service start function for %s", SVC_START)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_START)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("starting service %s %s", svcPath, svcID)
	if err := r.svcStart(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcStop(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_STOP)
	}

	if r.svcStop == nil {
		return fmt.Errorf("no service stop function for %s", SVC_STOP)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_STOP)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("stopping service %s %s", svcPath, svcID)
	if err := r.svcStop(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcRestart(r *runner, n node) error {

	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RESTART)
	}

	if r.svcRestart == nil {
		return fmt.Errorf("no service restart function for %s", SVC_RESTART)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RESTART)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	recursive := false
	if len(n.args) > 1 {
		recursive = true
	}

	glog.Infof("restarting service %s %s", svcPath, svcID)
	if err := r.svcRestart(svcID, recursive); err != nil {
		return err
	}

	return nil
}

func evalSvcRun(r *runner, n node) error {
	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RUN)
	}

	svcPath := n.args[0]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RUN)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	n.args[0] = svcID

	glog.V(0).Infof("running: serviced service run %s", strings.Join(n.args, " "))
	args := []string{"service", "run"}
	args = append(args, n.args...)
	if err := r.execCommand("serviced", args...); err != nil {
		return err
	}

	return nil
}

func evalSvcExec(r *runner, n node) error {
	if r.svcFromPath == nil {
		return fmt.Errorf("no service id lookup function for %s", SVC_RUN)
	}

	svcPath := n.args[1]
	tenantID, found := r.env["TENANT_ID"]
	if !found {
		return fmt.Errorf("no service tenant id specified for %s", SVC_RUN)
	}
	svcID, err := r.svcFromPath(tenantID, svcPath)
	if err != nil {
		return err
	}
	if svcID == "" {
		return fmt.Errorf("no service id found for %s", svcPath)
	}

	n.args[1] = svcID
	// "HMS-YMD_svcID" will be the name of the container
	containerName := time.Now().Format("150405-20060102") + "_" + svcID

	glog.V(0).Infof("running: serviced service shell -s %s %s", containerName, strings.Join(n.args[1:], " "))
	args := []string{"service", "shell", "-s", containerName}
	args = append(args, n.args[1:]...)
	if err := r.execCommand("serviced", args...); err != nil {
		return err
	}

	// Now commit the container (if 'COMMIT' was specified)
	switch n.args[0] {
	case "COMMIT":
		glog.V(0).Infof("committing container %s", containerName)
		var snapshotID string
		if snapshotID, err = r.commitContainer(containerName); err != nil {
			return err
		}
		exitFunc := func(failed bool) {
			if !failed {
				glog.V(1).Infof("cleaning up snapshot %s", snapshotID)
				if err := r.execCommand("serviced", "snapshot", "remove", snapshotID); err != nil {
					glog.Errorf("failed deleting snapshot %s: %v", snapshotID, err)
				}
			}
		}
		r.addExitFunction(exitFunc)
	}
	return nil
}

func evalDependency(r *runner, n node) error {
	glog.V(0).Infof("checking serviced dependency: %s", n.args[0])
	glog.V(0).Info("dependency check for serviced not implemented, skipping...")
	return nil
}

func evalRequireSvc(r *runner, n node) error {
	if r.tenantIDLookup == nil {
		return fmt.Errorf("no tenant lookup function provided for %s", REQUIRE_SVC)
	}
	glog.V(0).Infof("checking service requirement")
	if r.config.ServiceID == "" {
		return errors.New("no service id specified")
	}
	glog.V(0).Infof("verifying service %s", r.config.ServiceID)
	//lookup tenant id for service
	tID, err := r.tenantIDLookup(r.config.ServiceID)
	if err != nil {
		return err
	}
	glog.V(0).Infof("found %s tenant id for service %s", tID, r.config.ServiceID)
	r.env["TENANT_ID"] = tID
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package eval

import (
	"fmt"
	"math/big"

	"numgrad.io/parser"
)

type Scope struct {
	Parent *Scope
	Ident  map[string]interface{}
}

func Eval(s *Scope, expr parser.Expr) (interface{}, error) {
	switch expr := expr.(type) {
	case *parser.Ident:
		for s != nil {
			if v, ok := s.Ident[expr.Name]; ok {
				return v, nil
			}
			s = s.Parent
		}
		return nil, fmt.Errorf("eval: undefined identifier: %q", expr.Name)
	case *parser.BinaryExpr:
		lhs, err := Eval(s, expr.Left)
		if err != nil {
			return nil, err
		}
		rhs, err := Eval(s, expr.Right)
		if err != nil {
			return nil, err
		}
		v, err := binOp(expr.Op, lhs, rhs)
		if err != nil {
			return nil, fmt.Errorf("eval: %v evaluating %s", err, expr)
		}
		return v, nil
	case *parser.UnaryExpr:
		sub, err := Eval(s, expr.Expr)
		if err != nil {
			return nil, err
		}
		switch expr.Op {
		case parser.Not:
		case parser.Mul: // deref
		case parser.Ref:
		case parser.LeftParen:
			return sub, nil
		}
	case *parser.CallExpr:
	case *parser.BasicLiteral:
		return expr.Value, nil
	}
	return nil, fmt.Errorf("eval TODO %#+v, %T", expr, expr)
}

func binOp(op parser.Token, x, y interface{}) (interface{}, error) {
	switch op {
	case parser.Add:
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Add(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Add(x, y), nil
			}
		}
	case parser.Sub:
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Sub(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Sub(x, y), nil
			}
		}
	case parser.Mul:
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Mul(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Mul(x, y), nil
			}
		}
	case parser.Div:
	case parser.Rem:
	case parser.Pow:
	case parser.LogicalAnd:
		// TODO shortcut eval
		if x, ok := x.(bool); ok {
			if y, ok := y.(bool); ok {
				return x && y, nil
			}
		}
	case parser.LogicalOr:
		// TODO shortcut eval
		if x, ok := x.(bool); ok {
			if y, ok := y.(bool); ok {
				return x || y, nil
			}
		}
	case parser.Equal:
		if x == y {
			return true, nil
		}
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				return x.Cmp(y) == 0, nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				return x.Cmp(y) == 0, nil
			}
		}
	case parser.NotEqual:
		if x == y {
			return false, nil
		}
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				return x.Cmp(y) != 0, nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				return x.Cmp(y) != 0, nil
			}
		}
	case parser.Less:
	case parser.Greater:
		return nil, fmt.Errorf("unknown binop %s", op)
	}
	return nil, fmt.Errorf("type mismatch Left: %T, Right: %T", x, y)
}
<commit_msg>a few more eval ints<commit_after>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package eval

import (
	"fmt"
	"math/big"

	"numgrad.io/parser"
)

type Scope struct {
	Parent *Scope
	Ident  map[string]interface{}
}

func Eval(s *Scope, expr parser.Expr) (interface{}, error) {
	switch expr := expr.(type) {
	case *parser.Ident:
		for s != nil {
			if v, ok := s.Ident[expr.Name]; ok {
				return v, nil
			}
			s = s.Parent
		}
		return nil, fmt.Errorf("eval: undefined identifier: %q", expr.Name)
	case *parser.BinaryExpr:
		lhs, err := Eval(s, expr.Left)
		if err != nil {
			return nil, err
		}
		rhs, err := Eval(s, expr.Right)
		if err != nil {
			return nil, err
		}
		v, err := binOp(expr.Op, lhs, rhs)
		if err != nil {
			return nil, fmt.Errorf("eval: %v evaluating %s", err, expr)
		}
		return v, nil
	case *parser.UnaryExpr:
		sub, err := Eval(s, expr.Expr)
		if err != nil {
			return nil, err
		}
		switch expr.Op {
		case parser.Not:
		case parser.Mul: // deref
		case parser.Ref:
		case parser.LeftParen:
			return sub, nil
		}
	case *parser.CallExpr:
	case *parser.BasicLiteral:
		return expr.Value, nil
	}
	return nil, fmt.Errorf("eval TODO %#+v, %T", expr, expr)
}

func binOp(op parser.Token, x, y interface{}) (interface{}, error) {
	switch op {
	case parser.Add:
		switch x := x.(type) {
		case int64:
			switch y := y.(type) {
			case int64:
				return x + y, nil
			}
		case float32:
			switch y := y.(type) {
			case float32:
				return x + y, nil
			}
		case float64:
			switch y := y.(type) {
			case float64:
				return x + y, nil
			}
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Add(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Add(x, y), nil
			}
		}
	case parser.Sub:
		switch x := x.(type) {
		case int64:
			switch y := y.(type) {
			case int64:
				return x - y, nil
			}
		case float32:
			switch y := y.(type) {
			case float32:
				return x - y, nil
			}
		case float64:
			switch y := y.(type) {
			case float64:
				return x - y, nil
			}
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Sub(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Sub(x, y), nil
			}
		}
	case parser.Mul:
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				z := big.NewInt(0)
				return z.Mul(x, y), nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				z := big.NewFloat(0)
				return z.Mul(x, y), nil
			}
		}
	case parser.Div:
	case parser.Rem:
	case parser.Pow:
	case parser.LogicalAnd:
		// TODO shortcut eval
		if x, ok := x.(bool); ok {
			if y, ok := y.(bool); ok {
				return x && y, nil
			}
		}
	case parser.LogicalOr:
		// TODO shortcut eval
		if x, ok := x.(bool); ok {
			if y, ok := y.(bool); ok {
				return x || y, nil
			}
		}
	case parser.Equal:
		if x == y {
			return true, nil
		}
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				return x.Cmp(y) == 0, nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				return x.Cmp(y) == 0, nil
			}
		}
	case parser.NotEqual:
		if x == y {
			return false, nil
		}
		switch x := x.(type) {
		case *big.Int:
			switch y := y.(type) {
			case *big.Int:
				return x.Cmp(y) != 0, nil
			}
		case *big.Float:
			switch y := y.(type) {
			case *big.Float:
				return x.Cmp(y) != 0, nil
			}
		}
	case parser.Less:
	case parser.Greater:
		return nil, fmt.Errorf("unknown binop %s", op)
	}
	return nil, fmt.Errorf("type mismatch Left: %T, Right: %T", x, y)
}
<|endoftext|>


<commit_before>package lexer

// Note that most of the panic() calls should be removed once the lexer is bug-free.

import (
	"strings"
	"log"
	"fmt"
	"unicode"
)

type lexer struct {
	input []rune
	startPos, endPos int
	output []*Token
	filename string
	charNumber, lineNumber int
}

func (v *lexer) errWithCustomPosition(err string, ln, cn int) {
	log.Fatal(fmt.Sprintf("Lexer error [%d:%d:%s]: %s\n", ln, cn, v.filename, err))
}

func (v *lexer) err(err string) {
	v.errWithCustomPosition(err, v.lineNumber, v.charNumber)
}

func (v *lexer) peek(ahead int) rune {
	if ahead < 0 {
		panic(fmt.Sprintf("Tried to peek a negative number: %d", ahead))
	}

	if v.endPos + ahead >= len(v.input) {
		return 0
	}
	return v.input[v.endPos + ahead]
}

func (v *lexer) consume() {
	v.charNumber++
	if v.peek(0) == '\n' {
		v.charNumber = 1
		v.lineNumber++
	}

	v.endPos++
}

func (v *lexer) expect(r rune) {
	if v.peek(0) == r {
		v.consume()
	} else {
		v.err(fmt.Sprintf("Expected `%c`, found `%c`", r, v.peek(0)))
	}
}

func (v *lexer) discardBuffer() {
	v.startPos = v.endPos
}

// debugging func
func (v *lexer) printBuffer() {
	fmt.Printf("[%d:%d] `%s`\n", v.startPos, v.endPos, string(v.input[v.startPos:v.endPos]))
}

func (v *lexer) pushToken(t TokenType) {
	tok := &Token { Type: t, Filename: v.filename }
	tok.Contents = string(v.input[v.startPos:v.endPos])
	v.startPos = v.endPos
	v.output = append(v.output, tok)

	if true {
		fmt.Printf("[%4d:%4d:%-17s] `%s`\n", v.startPos, v.endPos, tok.Type, tok.Contents)
	}
}

func Lex(input []rune, filename string) []*Token {
	v := &lexer {
		input: input,
		startPos: 0,
		endPos: 0,
		filename: filename,
		charNumber: 1,
		lineNumber: 1,
	}

	v.lex()
	return v.output
}

func (v *lexer) lex() {
	for {
		v.skipLayoutAndComments()
		if isEOF(v.peek(0)) {
			return
		}

		if isDecimalDigit(v.peek(0)) {
			v.recognizeNumberToken()
		} else if isLetter(v.peek(0)) || v.peek(0) == '_' {
			v.recognizeIdentifierToken()
		} else if v.peek(0) == '"' {
			v.recognizeStringToken()
		} else if v.peek(0) == '\'' {
			v.recognizeCharacterToken()
		} else if isOperator(v.peek(0)) {
			v.recognizeOperatorToken()
		} else if isSeparator(v.peek(0)) {
			v.recognizeSeparatorToken()
		} else {
			v.err("Unrecognised token")
		}
	}
}

func (v *lexer) skipLayoutAndComments() {
	start:
	for isLayout(v.peek(0)) {
		v.consume()
	}

	lineNumber := v.lineNumber
	charNumber := v.charNumber

	// Block comments
	if v.peek(0) == '/' && v.peek(1) == '*' {
		v.consume()
		v.consume()

		for {
			if isEOF(v.peek(0)) {
				v.errWithCustomPosition("Unterminated block comment", lineNumber, charNumber)
			}
			if v.peek(0) == '*' && v.peek(1) == '/' {
				v.consume()
				v.consume()
				goto start
			}
			v.consume()
		}
	}

	// Single-line comments
	if v.peek(0) == '#' || (v.peek(0) == '/' && v.peek(1) == '/') {
		v.consume()
		if v.peek(0) == '/' {
			v.consume()
		}

		for {
			if isEOL(v.peek(0)) || isEOF(v.peek(0)) {
				v.consume()
				goto start
			}
			v.consume()
		}
	}

	//v.printBuffer()
	v.discardBuffer()
}

func (v *lexer) recognizeNumberToken() {
	v.consume()

	if v.peek(0) == 'x' || v.peek(0) == 'X' {
		// Hexadecimal
		v.consume()
		for isHexDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else if v.peek(0) == 'b' {
		// Binary
		v.consume()
		for isBinaryDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else if v.peek(0) == 'o' {
		// Octal
		v.consume()
		for isOctalDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else {
		// Decimal
		for {
			if isDecimalDigit(v.peek(0)) || v.peek(0) == '_' {
				v.consume()
				continue
			} else if v.peek(0) == 'f' || v.peek(0) == 'd' {
				v.consume()
			}
			v.pushToken(TOKEN_NUMBER)
			return
		}
	}
}

func (v *lexer) recognizeIdentifierToken() {
	v.consume()

	for isLetter(v.peek(0)) || isDecimalDigit(v.peek(0)) || v.peek(0) == '_' {
		v.consume()
	}

	v.pushToken(TOKEN_IDENTIFIER)
}

func (v *lexer) recognizeStringToken() {
	lineNumber := v.lineNumber
	charNumber := v.charNumber

	v.expect('"')

	for {
		if v.peek(0) == '\\' && v.peek(1) == '"' {
			v.consume()
			v.consume()
		} else if v.peek(0) == '"' {
			v.consume()
			v.pushToken(TOKEN_STRING)
			return
		} else if isEOF(v.peek(0))	{
			v.errWithCustomPosition("Unterminated block literal", lineNumber, charNumber)
		} else {
			v.consume()
		}
	}
}

func (v *lexer) recognizeCharacterToken() {
	lineNumber := v.lineNumber
	charNumber := v.charNumber

	v.expect('\'')

	if v.peek(0) == '\'' {
		v.err("Empty character constant")
	}

	for {
		if v.peek(0) == '\\' && v.peek(1) == '\'' {
			v.consume()
			v.consume()
		} else if v.peek(0) == '\'' {
			v.consume()
			v.pushToken(TOKEN_CHARACTER)
			return
		} else if isEOF(v.peek(0)) {
			v.errWithCustomPosition("Unterminated character literal", lineNumber, charNumber)
		} else {
			v.consume()
		}
	}
}

func (v *lexer) recognizeOperatorToken() {
	// stop := from being treated as an operator
	// treat them as individual operators instead.
	if v.peek(0) == ':' && v.peek(1) == '=' {
		v.consume()
	} else {
		v.consume()
		if isOperator(v.peek(0)) {
			v.consume()
		}
	}

	v.pushToken(TOKEN_OPERATOR)
}

func (v *lexer) recognizeSeparatorToken() {
	v.consume()
	v.pushToken(TOKEN_SEPARATOR)
}

func isDecimalDigit(r rune) bool {
	return r >= '0' && r <= '9'
}

func isHexDigit(r rune) bool {
	return isDecimalDigit(r) || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
}

func isBinaryDigit(r rune) bool {
	return r == '0' || r == '1'
}

func isOctalDigit(r rune) bool {
	return r >= '0' && r <= '7'
}

func isLetter(r rune) bool {
	return unicode.IsLetter(r)
}

func isOperator(r rune) bool {
	return strings.ContainsRune("+-*/=><!~?:|&%^\"'", r)
}

func isExpressionOperator(r rune) bool {
	return strings.ContainsRune("+-*/=><!~?:|&%^\"'()", r) // this is unused?
}

func isSeparator(r rune) bool {
	return strings.ContainsRune(" ;,.`@(){}[]", r)
}

func isEOL(r rune) bool {
	return r == '\n'
}

func isEOF(r rune) bool {
	return r == 0
}

func isLayout(r rune) bool {
	return (r <= ' ' || unicode.IsSpace(r)) && !isEOF(r)
}

<commit_msg>cleanup lexer some more<commit_after>package lexer

// Note that most of the panic() calls should be removed once the lexer is bug-free.

import (
	"strings"
	"log"
	"fmt"
	"unicode"
)

type lexer struct {
	input []rune
	startPos, endPos int
	output []*Token
	filename string
	charNumber, lineNumber int
}

func (v *lexer) errWithCustomPosition(err string, ln, cn int) {
	log.Fatal(fmt.Sprintf("Lexer error [%d:%d:%s]: %s\n", ln, cn, v.filename, err))
}

func (v *lexer) err(err string) {
	v.errWithCustomPosition(err, v.lineNumber, v.charNumber)
}

func (v *lexer) peek(ahead int) rune {
	if ahead < 0 {
		panic(fmt.Sprintf("Tried to peek a negative number: %d", ahead))
	}

	if v.endPos + ahead >= len(v.input) {
		return 0
	}
	return v.input[v.endPos + ahead]
}

func (v *lexer) consume() {
	v.charNumber++
	if v.peek(0) == '\n' {
		v.charNumber = 1
		v.lineNumber++
	}

	v.endPos++
}

func (v *lexer) expect(r rune) {
	if v.peek(0) == r {
		v.consume()
	} else {
		v.err(fmt.Sprintf("Expected `%c`, found `%c`", r, v.peek(0)))
	}
}

func (v *lexer) discardBuffer() {
	v.startPos = v.endPos
}

// debugging func
func (v *lexer) printBuffer() {
	fmt.Printf("[%d:%d] `%s`\n", v.startPos, v.endPos, string(v.input[v.startPos:v.endPos]))
}

func (v *lexer) pushToken(t TokenType) {
	tok := &Token {
		Type: t,
		Filename: v.filename,
		CharNumber: v.charNumber,
		LineNumber: v.lineNumber,
		Contents: string(v.input[v.startPos:v.endPos]),
	}

	v.startPos = v.endPos
	v.output = append(v.output, tok)

	if true {
		fmt.Printf("[%4d:%4d:%-17s] `%s`\n", v.startPos, v.endPos, tok.Type, tok.Contents)
	}
}

func Lex(input []rune, filename string) []*Token {
	v := &lexer {
		input: input,
		startPos: 0,
		endPos: 0,
		filename: filename,
		charNumber: 1,
		lineNumber: 1,
	}

	v.lex()
	return v.output
}

func (v *lexer) lex() {
	for {
		v.skipLayoutAndComments()
		if isEOF(v.peek(0)) {
			return
		}

		if isDecimalDigit(v.peek(0)) {
			v.recognizeNumberToken()
		} else if isLetter(v.peek(0)) || v.peek(0) == '_' {
			v.recognizeIdentifierToken()
		} else if v.peek(0) == '"' {
			v.recognizeStringToken()
		} else if v.peek(0) == '\'' {
			v.recognizeCharacterToken()
		} else if isOperator(v.peek(0)) {
			v.recognizeOperatorToken()
		} else if isSeparator(v.peek(0)) {
			v.recognizeSeparatorToken()
		} else {
			v.err("Unrecognised token")
		}
	}
}

func (v *lexer) skipLayoutAndComments() {
	start:
	for isLayout(v.peek(0)) {
		v.consume()
	}

	lineNumber := v.lineNumber
	charNumber := v.charNumber

	// Block comments
	if v.peek(0) == '/' && v.peek(1) == '*' {
		v.consume()
		v.consume()

		for {
			if isEOF(v.peek(0)) {
				v.errWithCustomPosition("Unterminated block comment", lineNumber, charNumber)
			}
			if v.peek(0) == '*' && v.peek(1) == '/' {
				v.consume()
				v.consume()
				goto start
			}
			v.consume()
		}
	}

	// Single-line comments
	if v.peek(0) == '#' || (v.peek(0) == '/' && v.peek(1) == '/') {
		v.consume()
		if v.peek(0) == '/' {
			v.consume()
		}

		for {
			if isEOL(v.peek(0)) || isEOF(v.peek(0)) {
				v.consume()
				goto start
			}
			v.consume()
		}
	}

	//v.printBuffer()
	v.discardBuffer()
}

func (v *lexer) recognizeNumberToken() {
	v.consume()

	if v.peek(0) == 'x' || v.peek(0) == 'X' {
		// Hexadecimal
		v.consume()
		for isHexDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else if v.peek(0) == 'b' {
		// Binary
		v.consume()
		for isBinaryDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else if v.peek(0) == 'o' {
		// Octal
		v.consume()
		for isOctalDigit(v.peek(0)) || v.peek(0) == '_' {
			v.consume()
		}
		v.pushToken(TOKEN_NUMBER)
	} else {
		// Decimal
		for {
			if isDecimalDigit(v.peek(0)) || v.peek(0) == '_' {
				v.consume()
				continue
			} else if v.peek(0) == 'f' || v.peek(0) == 'd' {
				v.consume()
			}
			v.pushToken(TOKEN_NUMBER)
			return
		}
	}
}

func (v *lexer) recognizeIdentifierToken() {
	v.consume()

	for isLetter(v.peek(0)) || isDecimalDigit(v.peek(0)) || v.peek(0) == '_' {
		v.consume()
	}

	v.pushToken(TOKEN_IDENTIFIER)
}

func (v *lexer) recognizeStringToken() {
	lineNumber := v.lineNumber
	charNumber := v.charNumber

	v.expect('"')

	for {
		if v.peek(0) == '\\' && v.peek(1) == '"' {
			v.consume()
			v.consume()
		} else if v.peek(0) == '"' {
			v.consume()
			v.pushToken(TOKEN_STRING)
			return
		} else if isEOF(v.peek(0))	{
			v.errWithCustomPosition("Unterminated block literal", lineNumber, charNumber)
		} else {
			v.consume()
		}
	}
}

func (v *lexer) recognizeCharacterToken() {
	lineNumber := v.lineNumber
	charNumber := v.charNumber

	v.expect('\'')

	if v.peek(0) == '\'' {
		v.err("Empty character constant")
	}

	for {
		if v.peek(0) == '\\' && v.peek(1) == '\'' {
			v.consume()
			v.consume()
		} else if v.peek(0) == '\'' {
			v.consume()
			v.pushToken(TOKEN_CHARACTER)
			return
		} else if isEOF(v.peek(0)) {
			v.errWithCustomPosition("Unterminated character literal", lineNumber, charNumber)
		} else {
			v.consume()
		}
	}
}

func (v *lexer) recognizeOperatorToken() {
	// stop := from being treated as an operator
	// treat them as individual operators instead.
	if v.peek(0) == ':' && v.peek(1) == '=' {
		v.consume()
	} else {
		v.consume()
		if isOperator(v.peek(0)) {
			v.consume()
		}
	}

	v.pushToken(TOKEN_OPERATOR)
}

func (v *lexer) recognizeSeparatorToken() {
	v.consume()
	v.pushToken(TOKEN_SEPARATOR)
}

func isDecimalDigit(r rune) bool {
	return r >= '0' && r <= '9'
}

func isHexDigit(r rune) bool {
	return isDecimalDigit(r) || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
}

func isBinaryDigit(r rune) bool {
	return r == '0' || r == '1'
}

func isOctalDigit(r rune) bool {
	return r >= '0' && r <= '7'
}

func isLetter(r rune) bool {
	return unicode.IsLetter(r)
}

func isOperator(r rune) bool {
	return strings.ContainsRune("+-*/=><!~?:|&%^\"'", r)
}

func isExpressionOperator(r rune) bool {
	return strings.ContainsRune("+-*/=><!~?:|&%^\"'()", r) // this is unused?
}

func isSeparator(r rune) bool {
	return strings.ContainsRune(" ;,.`@(){}[]", r)
}

func isEOL(r rune) bool {
	return r == '\n'
}

func isEOF(r rune) bool {
	return r == 0
}

func isLayout(r rune) bool {
	return (r <= ' ' || unicode.IsSpace(r)) && !isEOF(r)
}

<|endoftext|>