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

package flow

import (
	"database/sql"
	"errors"
	"math"
	"strings"
)

// WorkflowID is the type of unique workflow identifiers.
type WorkflowID int64

// Workflow represents the entire life cycle of a single document.
//
// A workflow begins with the creation of a document, and drives its
// life cycle through a sequence of responses to user actions or other
// system events.
//
// The engine in `flow` is visible primarily through workflows,
// documents and their behaviour.
//
// Currently, the topology of workflows is a graph, and is determined
// by the node definitions herein.
//
// N.B. It is highly recommended, but not necessary, that workflow
// names be defined in a system of hierarchical namespaces.
type Workflow struct {
	ID         WorkflowID `json:"ID,omitempty"`     // Globally-unique identifier of this workflow
	Name       string     `json:"Name,omitempty"`   // Globally-unique name of this workflow
	DocType    DocType    `json:"DocType"`          // Document type of which this workflow defines the life cycle
	BeginState DocState   `json:"BeginState"`       // Where this flow begins
	Active     bool       `json:"Active,omitempty"` // Is this workflow enabled?
}

// ApplyEvent takes an input user action or a system event, and
// applies its document action to the given document.  This results in
// a possibly new document state.  This method also prepares a message
// that is posted to applicable mailboxes.
func (w *Workflow) ApplyEvent(otx *sql.Tx, event *DocEvent, recipients []GroupID) (DocStateID, error) {
	if !w.Active {
		return 0, ErrWorkflowInactive
	}
	if event.Status == EventStatusApplied {
		return 0, ErrDocEventAlreadyApplied
	}
	if w.DocType.ID != event.DocType {
		return 0, ErrDocEventDocTypeMismatch
	}

	n, err := Nodes.GetByState(w.DocType.ID, event.State)
	if err != nil {
		return 0, err
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	nstate, err := n.applyEvent(tx, event, recipients)
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return nstate, nil
}

// Unexported type, only for convenience methods.
type _Workflows struct{}

// Workflows provides a resource-like interface to the workflows
// defined in the system.
var Workflows _Workflows

// New creates and initialises a workflow definition using the given
// name, the document type whose life cycle this workflow should
// manage, and the initial document state in which this workflow
// begins.
//
// N.B.  Workflow names must be globally-unique.
func (_Workflows) New(otx *sql.Tx, name string, dtype DocTypeID, state DocStateID) (WorkflowID, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return 0, errors.New("name should not be empty")
	}
	if dtype <= 0 {
		return 0, errors.New("document type should be a positive integer")
	}
	if state <= 1 {
		return 0, errors.New("initial document state should be an integer > 1")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	INSERT INTO wf_workflows(name, doctype_id, docstate_id, active)
	VALUES(?, ?, ?, 1)
	`
	res, err := tx.Exec(q, name, dtype, state)
	if err != nil {
		return 0, err
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return WorkflowID(id), nil
}

// List answers a subset of the workflows defined in the system,
// according to the given specification.
//
// Result set begins with ID >= `offset`, and has not more than
// `limit` elements.  A value of `0` for `offset` fetches from the
// beginning, while a value of `0` for `limit` fetches until the end.
func (_Workflows) List(offset, limit int64) ([]*Workflow, error) {
	if offset < 0 || limit < 0 {
		return nil, errors.New("offset and limit must be non-negative integers")
	}
	if limit == 0 {
		limit = math.MaxInt64
	}

	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON wf.doctype_id = dtm.id
	JOIN wf_docstates_master dsm ON wf.docstate_id = dsm.id
	ORDER BY wf.id
	LIMIT ? OFFSET ?
	`
	rows, err := db.Query(q, limit, offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ary := make([]*Workflow, 0, 10)
	for rows.Next() {
		var elem Workflow
		err = rows.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
			&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
		if err != nil {
			return nil, err
		}
		ary = append(ary, &elem)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return ary, nil
}

// Get retrieves the details of the requested workflow from the
// database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) Get(id WorkflowID) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON dtm.id = wf.doctype_id
	JOIN wf_docstates_master dsm ON dsm.id = wf.docstate_id
	WHERE wf.id = ?
	`
	row := db.QueryRow(q, id)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// GetByDocType retrieves the details of the requested workflow from
// the database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) GetByDocType(dtid DocTypeID) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON dtm.id = wf.doctype_id
	JOIN wf_docstates_master dsm ON dsm.id = wf.docstate_id
	WHERE wf.doctype_id = ?
	`
	row := db.QueryRow(q, dtid)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// GetByName retrieves the details of the requested workflow from the
// database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) GetByName(name string) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON wf.doctype_id = dtm.id
	JOIN wf_docstates_master dsm ON wf.docstate_id = dsm.id
	WHERE wf.name = ?
	`
	row := db.QueryRow(q, name)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// Rename assigns a new name to the given workflow.
func (_Workflows) Rename(otx *sql.Tx, id WorkflowID, name string) error {
	name = strings.TrimSpace(name)
	if name == "" {
		return errors.New("name should be non-empty")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	UPDATE wf_workflows SET name = ?
	WHERE id = ?
	`
	_, err := tx.Exec(q, name, id)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

	return nil
}

// SetActive sets the status of the workflow as either active or
// inactive, helping in workflow management and deprecation.
func (_Workflows) SetActive(otx *sql.Tx, id WorkflowID, active bool) error {
	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	var flag int
	if active {
		flag = 1
	}
	q := `
	UPDATE wf_workflows SET active = ?
	WHERE id = ?
	`
	_, err := tx.Exec(q, flag, id)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

	return nil
}

// AddNode maps the given document state to the specified node.  This
// map is consulted by the workflow when performing a state transition
// of the system.
func (_Workflows) AddNode(otx *sql.Tx, dtype DocTypeID, state DocStateID,
	ac AccessContextID, wid WorkflowID, name string, ntype NodeType) (NodeID, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return 0, errors.New("name should not be empty")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	INSERT INTO wf_workflow_nodes(doctype_id, docstate_id, ac_id, workflow_id, name, type)
	VALUES(?, ?, ?, ?, ?, ?)
	`
	res, err := tx.Exec(q, dtype, state, ac, wid, name, string(ntype))
	if err != nil {
		return 0, err
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return NodeID(id), nil
}

// RemoveNode unmaps the given document state to the specified node.
// This map is consulted by the workflow when performing a state
// transition of the system.
func (_Workflows) RemoveNode(otx *sql.Tx, wid WorkflowID, nid NodeID) error {
	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	DELETE FROM wf_workflow_nodes
	WHERE workflow_id = ?
	AND id = ?
	`
	_, err := tx.Exec(q, wid, nid)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

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

package flow

import (
	"database/sql"
	"errors"
	"math"
	"strings"
)

// WorkflowID is the type of unique workflow identifiers.
type WorkflowID int64

// Workflow represents the entire life cycle of a single document.
//
// A workflow begins with the creation of a document, and drives its
// life cycle through a sequence of responses to user actions or other
// system events.
//
// The engine in `flow` is visible primarily through workflows,
// documents and their behaviour.
//
// Currently, the topology of workflows is a graph, and is determined
// by the node definitions herein.
//
// N.B. It is highly recommended, but not necessary, that workflow
// names be defined in a system of hierarchical namespaces.
type Workflow struct {
	ID         WorkflowID `json:"ID,omitempty"`     // Globally-unique identifier of this workflow
	Name       string     `json:"Name,omitempty"`   // Globally-unique name of this workflow
	DocType    DocType    `json:"DocType"`          // Document type of which this workflow defines the life cycle
	BeginState DocState   `json:"BeginState"`       // Where this flow begins
	Active     bool       `json:"Active,omitempty"` // Is this workflow enabled?
}

// ApplyEvent takes an input user action or a system event, and
// applies its document action to the given document.  This results in
// a possibly new document state.  This method also prepares a message
// that is posted to applicable mailboxes.
func (w *Workflow) ApplyEvent(otx *sql.Tx, event *DocEvent, recipients []GroupID) (DocStateID, error) {
	if !w.Active {
		return 0, ErrWorkflowInactive
	}
	if event.Status == EventStatusApplied {
		return 0, ErrDocEventAlreadyApplied
	}
	if w.DocType.ID != event.DocType {
		return 0, ErrDocEventDocTypeMismatch
	}

	n, err := Nodes.GetByState(w.DocType.ID, event.State)
	if err != nil {
		return 0, err
	}

	var gt string
	tq := `SELECT group_type FROM wf_groups_master WHERE id = ?`
	row := db.QueryRow(tq, event.Group)
	err = row.Scan(&gt)
	if err != nil {
		return 0, err
	}
	if gt != "S" {
		return 0, errors.New("group must be singleton")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	nstate, err := n.applyEvent(tx, event, recipients)
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return nstate, nil
}

// Unexported type, only for convenience methods.
type _Workflows struct{}

// Workflows provides a resource-like interface to the workflows
// defined in the system.
var Workflows _Workflows

// New creates and initialises a workflow definition using the given
// name, the document type whose life cycle this workflow should
// manage, and the initial document state in which this workflow
// begins.
//
// N.B.  Workflow names must be globally-unique.
func (_Workflows) New(otx *sql.Tx, name string, dtype DocTypeID, state DocStateID) (WorkflowID, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return 0, errors.New("name should not be empty")
	}
	if dtype <= 0 {
		return 0, errors.New("document type should be a positive integer")
	}
	if state <= 1 {
		return 0, errors.New("initial document state should be an integer > 1")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	INSERT INTO wf_workflows(name, doctype_id, docstate_id, active)
	VALUES(?, ?, ?, 1)
	`
	res, err := tx.Exec(q, name, dtype, state)
	if err != nil {
		return 0, err
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return WorkflowID(id), nil
}

// List answers a subset of the workflows defined in the system,
// according to the given specification.
//
// Result set begins with ID >= `offset`, and has not more than
// `limit` elements.  A value of `0` for `offset` fetches from the
// beginning, while a value of `0` for `limit` fetches until the end.
func (_Workflows) List(offset, limit int64) ([]*Workflow, error) {
	if offset < 0 || limit < 0 {
		return nil, errors.New("offset and limit must be non-negative integers")
	}
	if limit == 0 {
		limit = math.MaxInt64
	}

	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON wf.doctype_id = dtm.id
	JOIN wf_docstates_master dsm ON wf.docstate_id = dsm.id
	ORDER BY wf.id
	LIMIT ? OFFSET ?
	`
	rows, err := db.Query(q, limit, offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ary := make([]*Workflow, 0, 10)
	for rows.Next() {
		var elem Workflow
		err = rows.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
			&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
		if err != nil {
			return nil, err
		}
		ary = append(ary, &elem)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return ary, nil
}

// Get retrieves the details of the requested workflow from the
// database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) Get(id WorkflowID) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON dtm.id = wf.doctype_id
	JOIN wf_docstates_master dsm ON dsm.id = wf.docstate_id
	WHERE wf.id = ?
	`
	row := db.QueryRow(q, id)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// GetByDocType retrieves the details of the requested workflow from
// the database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) GetByDocType(dtid DocTypeID) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON dtm.id = wf.doctype_id
	JOIN wf_docstates_master dsm ON dsm.id = wf.docstate_id
	WHERE wf.doctype_id = ?
	`
	row := db.QueryRow(q, dtid)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// GetByName retrieves the details of the requested workflow from the
// database.
//
// N.B.  This method retrieves the primary information of the
// workflow.  Information of the nodes comprising this workflow have
// to be fetched separately.
func (_Workflows) GetByName(name string) (*Workflow, error) {
	q := `
	SELECT wf.id, wf.name, dtm.id, dtm.name, dsm.id, dsm.name, wf.active
	FROM wf_workflows wf
	JOIN wf_doctypes_master dtm ON wf.doctype_id = dtm.id
	JOIN wf_docstates_master dsm ON wf.docstate_id = dsm.id
	WHERE wf.name = ?
	`
	row := db.QueryRow(q, name)
	var elem Workflow
	err := row.Scan(&elem.ID, &elem.Name, &elem.DocType.ID, &elem.DocType.Name,
		&elem.BeginState.ID, &elem.BeginState.Name, &elem.Active)
	if err != nil {
		return nil, err
	}

	return &elem, nil
}

// Rename assigns a new name to the given workflow.
func (_Workflows) Rename(otx *sql.Tx, id WorkflowID, name string) error {
	name = strings.TrimSpace(name)
	if name == "" {
		return errors.New("name should be non-empty")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	UPDATE wf_workflows SET name = ?
	WHERE id = ?
	`
	_, err := tx.Exec(q, name, id)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

	return nil
}

// SetActive sets the status of the workflow as either active or
// inactive, helping in workflow management and deprecation.
func (_Workflows) SetActive(otx *sql.Tx, id WorkflowID, active bool) error {
	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	var flag int
	if active {
		flag = 1
	}
	q := `
	UPDATE wf_workflows SET active = ?
	WHERE id = ?
	`
	_, err := tx.Exec(q, flag, id)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

	return nil
}

// AddNode maps the given document state to the specified node.  This
// map is consulted by the workflow when performing a state transition
// of the system.
func (_Workflows) AddNode(otx *sql.Tx, dtype DocTypeID, state DocStateID,
	ac AccessContextID, wid WorkflowID, name string, ntype NodeType) (NodeID, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return 0, errors.New("name should not be empty")
	}

	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return 0, err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	INSERT INTO wf_workflow_nodes(doctype_id, docstate_id, ac_id, workflow_id, name, type)
	VALUES(?, ?, ?, ?, ?, ?)
	`
	res, err := tx.Exec(q, dtype, state, ac, wid, name, string(ntype))
	if err != nil {
		return 0, err
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return 0, err
		}
	}

	return NodeID(id), nil
}

// RemoveNode unmaps the given document state to the specified node.
// This map is consulted by the workflow when performing a state
// transition of the system.
func (_Workflows) RemoveNode(otx *sql.Tx, wid WorkflowID, nid NodeID) error {
	var tx *sql.Tx
	if otx == nil {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()
	} else {
		tx = otx
	}

	q := `
	DELETE FROM wf_workflow_nodes
	WHERE workflow_id = ?
	AND id = ?
	`
	_, err := tx.Exec(q, wid, nid)
	if err != nil {
		return err
	}

	if otx == nil {
		err = tx.Commit()
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxd

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"

	"github.com/juju/juju/cloudconfig/containerinit"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/container"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	"github.com/juju/juju/tools/lxdclient"
)

var (
	logger = loggo.GetLogger("juju.container.lxd")
)

// XXX: should we allow managing containers on other hosts? this is
// functionality LXD gives us and from discussion juju would use eventually for
// the local provider, so the APIs probably need to be changed to pass extra
// args around. I'm punting for now.
type containerManager struct {
	name string
	// A cached client.
	client *lxdclient.Client
	// Profiles that need to be deleted when the container is destroyed
	createdProfiles []string
}

// containerManager implements container.Manager.
var _ container.Manager = (*containerManager)(nil)

func ConnectLocal(namespace string) (*lxdclient.Client, error) {
	cfg := lxdclient.Config{
		Namespace: namespace,
		Remote:    lxdclient.Local,
	}

	cfg, err := cfg.WithDefaults()
	if err != nil {
		return nil, errors.Trace(err)
	}

	client, err := lxdclient.Connect(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}

// NewContainerManager creates the entity that knows how to create and manage
// LXD containers.
// TODO(jam): This needs to grow support for things like LXC's ImageURLGetter
// functionality.
func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) {
	name := conf.PopValue(container.ConfigName)
	if name == "" {
		return nil, errors.Errorf("name is required")
	}

	conf.WarnAboutUnused()
	return &containerManager{name: name}, nil
}

func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.StatusProvisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name := names.NewMachineTag(instanceConfig.MachineId).String()
	if manager.name != "" {
		name = fmt.Sprintf("%s-%s", manager.name, name)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-environment": manager.name,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	networkProfile := fmt.Sprintf("%s-network", name)

	err = manager.createNetworkProfile(networkProfile, networkConfig)
	if err != nil {
		return
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Profiles: []string{
			networkProfile,
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.StatusProvisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		manager.client.ProfileDelete(networkProfile)
		return
	}

	callback(status.StatusRunning, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	manager.createdProfiles = append(manager.createdProfiles, networkProfile)
	return
}

func (manager *containerManager) DestroyContainer(id instance.Id) error {
	if manager.client == nil {
		var err error
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return err
		}
	}

	for _, profile := range manager.createdProfiles {
		logger.Infof("deleting profile %q", profile)
		if err := manager.client.ProfileDelete(profile); err != nil {
			logger.Warningf("discarding profile delete error: %v", err)
		}
	}

	return errors.Trace(manager.client.RemoveInstances(manager.name, string(id)))
}

func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
	result = []instance.Instance{}
	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return
		}
	}

	lxdInstances, err := manager.client.Instances(manager.name)
	if err != nil {
		return
	}

	for _, i := range lxdInstances {
		result = append(result, &lxdInstance{i.Name, manager.client})
	}

	return
}

func (manager *containerManager) IsInitialized() bool {
	if manager.client != nil {
		return true
	}

	// NewClient does a roundtrip to the server to make sure it understands
	// the versions, so all we need to do is connect above and we're done.
	var err error
	manager.client, err = ConnectLocal(manager.name)
	return err == nil
}

// HasLXDSupport returns false when this juju binary was not built with LXD
// support (i.e. it was built on a golang version < 1.2
func HasLXDSupport() bool {
	return true
}

func (manager *containerManager) createNetworkProfile(profile string, networkConfig *container.NetworkConfig) error {
	found, err := manager.client.HasProfile(profile)

	if err != nil {
		return err
	}

	if found {
		logger.Infof("deleting existing profile %q", profile)
		if err := manager.client.ProfileDelete(profile); err != nil {
			return err
		}
	}

	if err := manager.client.CreateProfile(profile, nil); err != nil {
		return err
	}

	logger.Infof("created new network profile %q", profile)

	for _, v := range networkConfig.Interfaces {
		if v.InterfaceType == network.LoopbackInterface {
			continue
		}

		if v.InterfaceType != network.EthernetInterface {
			return errors.Errorf("interface type %q not supported", v.InterfaceType)
		}

		var props = []string{}
		props = append(props, "nictype=bridged")
		props = append(props, fmt.Sprintf("parent=%v", v.ParentInterfaceName))
		props = append(props, fmt.Sprintf("name=%v", v.InterfaceName))

		if v.MACAddress != "" {
			props = append(props, fmt.Sprintf("hwaddr=%v", v.MACAddress))
		}

		if v.MTU > 0 {
			props = append(props, fmt.Sprintf("mtu=%v", v.MTU))
		}

		logger.Infof("adding nic device %q with properties %+v to profile %q",
			v.InterfaceName, props, profile)

		_, err := manager.client.ProfileDeviceAdd(profile, v.InterfaceName, "nic", props)

		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>container/lxd: fallback to "default" LXD profile<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxd

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/lxc/lxd"

	"github.com/juju/juju/cloudconfig/containerinit"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/container"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	"github.com/juju/juju/tools/lxdclient"
)

var (
	logger = loggo.GetLogger("juju.container.lxd")
)

// XXX: should we allow managing containers on other hosts? this is
// functionality LXD gives us and from discussion juju would use eventually for
// the local provider, so the APIs probably need to be changed to pass extra
// args around. I'm punting for now.
type containerManager struct {
	name string
	// A cached client.
	client *lxdclient.Client
	// Custom network profile
	networkProfile string
}

// containerManager implements container.Manager.
var _ container.Manager = (*containerManager)(nil)

func ConnectLocal(namespace string) (*lxdclient.Client, error) {
	cfg := lxdclient.Config{
		Namespace: namespace,
		Remote:    lxdclient.Local,
	}

	cfg, err := cfg.WithDefaults()
	if err != nil {
		return nil, errors.Trace(err)
	}

	client, err := lxdclient.Connect(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}

// NewContainerManager creates the entity that knows how to create and manage
// LXD containers.
// TODO(jam): This needs to grow support for things like LXC's ImageURLGetter
// functionality.
func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) {
	name := conf.PopValue(container.ConfigName)
	if name == "" {
		return nil, errors.Errorf("name is required")
	}

	conf.WarnAboutUnused()
	return &containerManager{name: name}, nil
}

func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			manager.deleteNetworkProfile()
			callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.StatusProvisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name := names.NewMachineTag(instanceConfig.MachineId).String()
	if manager.name != "" {
		name = fmt.Sprintf("%s-%s", manager.name, name)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-environment": manager.name,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	networkProfile := fmt.Sprintf("%s-network", name)

	if err = createNetworkProfile(manager.client, networkProfile); err != nil {
		return
	}

	if len(networkConfig.Interfaces) > 0 || networkConfig.Device != "" {
		manager.networkProfile = networkProfile
		if len(networkConfig.Interfaces) > 0 {
			err = networkProfileAddInterfaces(manager.client, networkProfile, networkConfig)
		} else {
			err = networkProfileAddSingleInterface(manager.client, networkProfile, networkConfig)
		}
		if err != nil {
			return
		}
	} else {
		networkProfile = "default"
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Profiles: []string{
			networkProfile,
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.StatusProvisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		manager.client.ProfileDelete(networkProfile)
		return
	}

	callback(status.StatusRunning, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}

func (manager *containerManager) DestroyContainer(id instance.Id) error {
	if manager.client == nil {
		var err error
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return err
		}
	}
	manager.deleteNetworkProfile()
	return errors.Trace(manager.client.RemoveInstances(manager.name, string(id)))
}

func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
	result = []instance.Instance{}
	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return
		}
	}

	lxdInstances, err := manager.client.Instances(manager.name)
	if err != nil {
		return
	}

	for _, i := range lxdInstances {
		result = append(result, &lxdInstance{i.Name, manager.client})
	}

	return
}

func (manager *containerManager) IsInitialized() bool {
	if manager.client != nil {
		return true
	}

	// NewClient does a roundtrip to the server to make sure it understands
	// the versions, so all we need to do is connect above and we're done.
	var err error
	manager.client, err = ConnectLocal(manager.name)
	return err == nil
}

// HasLXDSupport returns false when this juju binary was not built with LXD
// support (i.e. it was built on a golang version < 1.2
func HasLXDSupport() bool {
	return true
}

func addNetworkDeviceToProfile(client *lxdclient.Client, profile, parentDevice, deviceName, hwAddr string, mtu int) (*lxd.Response, error) {
	var props = []string{}
	props = append(props, "nictype=bridged")
	props = append(props, fmt.Sprintf("parent=%v", parentDevice))
	props = append(props, fmt.Sprintf("name=%v", deviceName))

	if hwAddr != "" {
		props = append(props, fmt.Sprintf("hwaddr=%v", hwAddr))
	}

	if mtu > 0 {
		props = append(props, fmt.Sprintf("mtu=%v", mtu))
	}

	logger.Infof("adding nic device %q with properties %+v to profile %q", deviceName, props, profile)

	return client.ProfileDeviceAdd(profile, deviceName, "nic", props)
}

func networkProfileAddSingleInterface(client *lxdclient.Client, profile string, networkConfig *container.NetworkConfig) error {
	_, err := addNetworkDeviceToProfile(client, profile, networkConfig.Device, "eth0", "", networkConfig.MTU)
	return err
}

func networkProfileAddInterfaces(client *lxdclient.Client, profile string, networkConfig *container.NetworkConfig) error {
	for _, v := range networkConfig.Interfaces {
		if v.InterfaceType == network.LoopbackInterface {
			continue
		}

		if v.InterfaceType != network.EthernetInterface {
			return errors.Errorf("interface type %q not supported", v.InterfaceType)
		}

		_, err := addNetworkDeviceToProfile(client, profile, v.ParentInterfaceName, v.InterfaceName, v.MACAddress, v.MTU)

		if err != nil {
			return err
		}
	}

	return nil
}

func createNetworkProfile(client *lxdclient.Client, profile string) error {
	found, err := client.HasProfile(profile)

	if err != nil {
		return err
	}

	if found {
		logger.Infof("deleting existing container profile %q", profile)
		if err := client.ProfileDelete(profile); err != nil {
			return err
		}
	}

	err = client.CreateProfile(profile, nil)

	if err == nil {
		logger.Infof("created new network container profile %q", profile)
	}

	return err
}

func (manager *containerManager) deleteNetworkProfile() {
	if manager.client != nil && manager.networkProfile != "" {
		logger.Infof("deleting container network profile %q", manager.networkProfile)
		if err := manager.client.ProfileDelete(manager.networkProfile); err != nil {
			logger.Warningf("discarding profile delete error: %v", err)
		}
		manager.networkProfile = ""
	}
}
<|endoftext|>


<commit_before>package neoutils

import (
	"errors"
	"fmt"
	"log"
	"net/url"
	"sync"
	"time"

	"github.com/jmcvetta/neoism"
)

var (
	urlError          = errors.New("inappropriate url")
	notConnectedError = errors.New("not connected to neo4j database")
)

func connectAuto(neoURL string, connect func() (NeoConnection, error), delay time.Duration) (NeoConnection, error) {

	// check that at least we have a valid url
	parsed, _ := url.Parse(neoURL)
	if parsed.Host == "" {
		return nil, urlError
	}

	a := &AutoConnectTransactional{
		url:          neoURL,
		connect:      connect,
		needsConnect: make(chan struct{}, 1),
		delay:        delay,
	}

	a.needsConnect <- struct{}{}

	go a.mainLoop()
	return a, nil
}

type AutoConnectTransactional struct {
	url     string
	connect func() (NeoConnection, error)
	delay   time.Duration

	lk          sync.RWMutex
	conn        NeoConnection
	indexes     []map[string]string
	constraints []map[string]string

	needsConnect chan struct{}
}

func (a *AutoConnectTransactional) mainLoop() {
	for {
		<-a.needsConnect

		for {
			err := a.doConnect()
			if err == nil {
				break
			}
			log.Printf("connection to neo4j failed. Sleeping for %s : %v\n", a.delay, err.Error())
			time.Sleep(a.delay)
		}

		log.Printf("connected to %v\n", a.url)
	}
}

func (a *AutoConnectTransactional) doConnect() error {
	newConn, err := a.connect()
	if err != nil {
		return err
	}

	return a.postConnect(newConn)
}

// run tasks that need to happen once we get a (new) connection
func (a *AutoConnectTransactional) postConnect(newConn NeoConnection) error {
	a.lk.Lock()
	defer a.lk.Unlock()

	a.conn = newConn

	for _, i := range a.indexes {
		if err := a.conn.EnsureIndexes(i); err != nil {
			return fmt.Errorf("failed to apply indexes: %v\n", err)
		}
	}
	a.indexes = nil
	for _, c := range a.constraints {
		if err := a.conn.EnsureConstraints(c); err != nil {
			return fmt.Errorf("failed to apply constraints: %v\n", err)
		}
	}
	a.constraints = nil
	return nil
}

func (a *AutoConnectTransactional) String() string {
	return fmt.Sprintf("AutoConnectDb(%v)", a.url)
}

func (a *AutoConnectTransactional) CypherBatch(queries []*neoism.CypherQuery) error {
	a.lk.RLock()
	defer a.lk.RUnlock()
	if a.conn == nil {
		return notConnectedError
	}
	err := a.conn.CypherBatch(queries)
	if err != nil {

		needReconnect := false

		switch e := err.(type) {
		case *url.Error:
			if !e.Temporary() {
				needReconnect = true
			}
		default:
			log.Printf("Unhandled error type. Assuming a reconnect is required %T %v\n", err, err)
			needReconnect = true
		}

		if needReconnect {
			select {
			case a.needsConnect <- struct{}{}:
				// request a reconnect
			default:
				// reconnect already queued
			}
		}

		return err
	}
	return nil
}

func (a *AutoConnectTransactional) EnsureConstraints(constraints map[string]string) error {
	a.lk.Lock()
	defer a.lk.Unlock()
	a.constraints = append(a.constraints, constraints)
	select {
	case a.needsConnect <- struct{}{}:
	default:
	}
	return nil
}

func (a *AutoConnectTransactional) EnsureIndexes(indexes map[string]string) error {
	a.lk.Lock()
	defer a.lk.Unlock()
	a.indexes = append(a.indexes, indexes)
	select {
	case a.needsConnect <- struct{}{}:
	default:
	}
	return nil
}
<commit_msg>Don't reconnect on transaction error<commit_after>package neoutils

import (
	"errors"
	"fmt"
	"log"
	"net/url"
	"sync"
	"time"

	"github.com/Financial-Times/up-rw-app-api-go/rwapi"
	"github.com/jmcvetta/neoism"
)

var (
	urlError          = errors.New("inappropriate url")
	notConnectedError = errors.New("not connected to neo4j database")
)

func connectAuto(neoURL string, connect func() (NeoConnection, error), delay time.Duration) (NeoConnection, error) {

	// check that at least we have a valid url
	parsed, _ := url.Parse(neoURL)
	if parsed.Host == "" {
		return nil, urlError
	}

	a := &AutoConnectTransactional{
		url:          neoURL,
		connect:      connect,
		needsConnect: make(chan struct{}, 1),
		delay:        delay,
	}

	a.needsConnect <- struct{}{}

	go a.mainLoop()
	return a, nil
}

type AutoConnectTransactional struct {
	url     string
	connect func() (NeoConnection, error)
	delay   time.Duration

	lk          sync.RWMutex
	conn        NeoConnection
	indexes     []map[string]string
	constraints []map[string]string

	needsConnect chan struct{}
}

func (a *AutoConnectTransactional) mainLoop() {
	for {
		<-a.needsConnect

		for {
			err := a.doConnect()
			if err == nil {
				break
			}
			log.Printf("connection to neo4j failed. Sleeping for %s : %v\n", a.delay, err.Error())
			time.Sleep(a.delay)
		}

		log.Printf("connected to %v\n", a.url)
	}
}

func (a *AutoConnectTransactional) doConnect() error {
	newConn, err := a.connect()
	if err != nil {
		return err
	}

	return a.postConnect(newConn)
}

// run tasks that need to happen once we get a (new) connection
func (a *AutoConnectTransactional) postConnect(newConn NeoConnection) error {
	a.lk.Lock()
	defer a.lk.Unlock()

	a.conn = newConn

	for _, i := range a.indexes {
		if err := a.conn.EnsureIndexes(i); err != nil {
			return fmt.Errorf("failed to apply indexes: %v\n", err)
		}
	}
	a.indexes = nil
	for _, c := range a.constraints {
		if err := a.conn.EnsureConstraints(c); err != nil {
			return fmt.Errorf("failed to apply constraints: %v\n", err)
		}
	}
	a.constraints = nil
	return nil
}

func (a *AutoConnectTransactional) String() string {
	return fmt.Sprintf("AutoConnectDb(%v)", a.url)
}

func (a *AutoConnectTransactional) CypherBatch(queries []*neoism.CypherQuery) error {
	a.lk.RLock()
	defer a.lk.RUnlock()
	if a.conn == nil {
		return notConnectedError
	}
	err := a.conn.CypherBatch(queries)
	if err != nil {

		needReconnect := false

		switch e := err.(type) {
		case rwapi.ConstraintOrTransactionError:
			// no reconnect needed
		case *url.Error:
			if !e.Temporary() {
				needReconnect = true
			}
		default:
			log.Printf("Unhandled error type. Assuming a reconnect is required %T %v\n", err, err)
			needReconnect = true
		}

		if needReconnect {
			select {
			case a.needsConnect <- struct{}{}:
				// request a reconnect
			default:
				// reconnect already queued
			}
		}

		return err
	}
	return nil
}

func (a *AutoConnectTransactional) EnsureConstraints(constraints map[string]string) error {
	a.lk.Lock()
	defer a.lk.Unlock()
	a.constraints = append(a.constraints, constraints)
	select {
	case a.needsConnect <- struct{}{}:
	default:
	}
	return nil
}

func (a *AutoConnectTransactional) EnsureIndexes(indexes map[string]string) error {
	a.lk.Lock()
	defer a.lk.Unlock()
	a.indexes = append(a.indexes, indexes)
	select {
	case a.needsConnect <- struct{}{}:
	default:
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssa

// nilcheckelim eliminates unnecessary nil checks.
// runs on machine-independent code.
func nilcheckelim(f *Func) {
	// A nil check is redundant if the same nil check was successful in a
	// dominating block. The efficacy of this pass depends heavily on the
	// efficacy of the cse pass.
	sdom := f.sdom()

	// TODO: Eliminate more nil checks.
	// We can recursively remove any chain of fixed offset calculations,
	// i.e. struct fields and array elements, even with non-constant
	// indices: x is non-nil iff x.a.b[i].c is.

	type walkState int
	const (
		Work     walkState = iota // process nil checks and traverse to dominees
		ClearPtr                  // forget the fact that ptr is nil
	)

	type bp struct {
		block *Block // block, or nil in ClearPtr state
		ptr   *Value // if non-nil, ptr that is to be cleared in ClearPtr state
		op    walkState
	}

	work := make([]bp, 0, 256)
	work = append(work, bp{block: f.Entry})

	// map from value ID to bool indicating if value is known to be non-nil
	// in the current dominator path being walked. This slice is updated by
	// walkStates to maintain the known non-nil values.
	nonNilValues := make([]bool, f.NumValues())

	// make an initial pass identifying any non-nil values
	for _, b := range f.Blocks {
		// a value resulting from taking the address of a
		// value, or a value constructed from an offset of a
		// non-nil ptr (OpAddPtr) implies it is non-nil
		for _, v := range b.Values {
			if v.Op == OpAddr || v.Op == OpAddPtr {
				nonNilValues[v.ID] = true
			} else if v.Op == OpPhi {
				// phis whose arguments are all non-nil
				// are non-nil
				argsNonNil := true
				for _, a := range v.Args {
					if !nonNilValues[a.ID] {
						argsNonNil = false
					}
				}
				if argsNonNil {
					nonNilValues[v.ID] = true
				}
			}
		}
	}

	// allocate auxiliary date structures for computing store order
	sset := f.newSparseSet(f.NumValues())
	defer f.retSparseSet(sset)
	storeNumber := make([]int32, f.NumValues())

	// perform a depth first walk of the dominee tree
	for len(work) > 0 {
		node := work[len(work)-1]
		work = work[:len(work)-1]

		switch node.op {
		case Work:
			b := node.block

			// First, see if we're dominated by an explicit nil check.
			if len(b.Preds) == 1 {
				p := b.Preds[0].b
				if p.Kind == BlockIf && p.Control.Op == OpIsNonNil && p.Succs[0].b == b {
					ptr := p.Control.Args[0]
					if !nonNilValues[ptr.ID] {
						nonNilValues[ptr.ID] = true
						work = append(work, bp{op: ClearPtr, ptr: ptr})
					}
				}
			}

			// Next, order values in the current block w.r.t. stores.
			b.Values = storeOrder(b.Values, sset, storeNumber)

			// Next, process values in the block.
			i := 0
			for _, v := range b.Values {
				b.Values[i] = v
				i++
				switch v.Op {
				case OpIsNonNil:
					ptr := v.Args[0]
					if nonNilValues[ptr.ID] {
						// This is a redundant explicit nil check.
						v.reset(OpConstBool)
						v.AuxInt = 1 // true
					}
				case OpNilCheck:
					ptr := v.Args[0]
					if nonNilValues[ptr.ID] {
						// This is a redundant implicit nil check.
						// Logging in the style of the former compiler -- and omit line 1,
						// which is usually in generated code.
						if f.Config.Debug_checknil() && v.Pos.Line() > 1 {
							f.Config.Warnl(v.Pos, "removed nil check")
						}
						v.reset(OpUnknown)
						// TODO: f.freeValue(v)
						i--
						continue
					}
					// Record the fact that we know ptr is non nil, and remember to
					// undo that information when this dominator subtree is done.
					nonNilValues[ptr.ID] = true
					work = append(work, bp{op: ClearPtr, ptr: ptr})
				}
			}
			for j := i; j < len(b.Values); j++ {
				b.Values[j] = nil
			}
			b.Values = b.Values[:i]

			// Add all dominated blocks to the work list.
			for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
				work = append(work, bp{op: Work, block: w})
			}

		case ClearPtr:
			nonNilValues[node.ptr.ID] = false
			continue
		}
	}
}

// All platforms are guaranteed to fault if we load/store to anything smaller than this address.
//
// This should agree with minLegalPointer in the runtime.
const minZeroPage = 4096

// nilcheckelim2 eliminates unnecessary nil checks.
// Runs after lowering and scheduling.
func nilcheckelim2(f *Func) {
	unnecessary := f.newSparseSet(f.NumValues())
	defer f.retSparseSet(unnecessary)
	for _, b := range f.Blocks {
		// Walk the block backwards. Find instructions that will fault if their
		// input pointer is nil. Remove nil checks on those pointers, as the
		// faulting instruction effectively does the nil check for free.
		unnecessary.clear()
		for i := len(b.Values) - 1; i >= 0; i-- {
			v := b.Values[i]
			if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
				if f.Config.Debug_checknil() && v.Pos.Line() > 1 {
					f.Config.Warnl(v.Pos, "removed nil check")
				}
				v.reset(OpUnknown)
				continue
			}
			if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
				if v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive {
					// These ops don't really change memory.
					continue
				}
				// This op changes memory.  Any faulting instruction after v that
				// we've recorded in the unnecessary map is now obsolete.
				unnecessary.clear()
			}

			// Find any pointers that this op is guaranteed to fault on if nil.
			var ptrstore [2]*Value
			ptrs := ptrstore[:0]
			if opcodeTable[v.Op].faultOnNilArg0 {
				ptrs = append(ptrs, v.Args[0])
			}
			if opcodeTable[v.Op].faultOnNilArg1 {
				ptrs = append(ptrs, v.Args[1])
			}
			for _, ptr := range ptrs {
				// Check to make sure the offset is small.
				switch opcodeTable[v.Op].auxType {
				case auxSymOff:
					if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
						continue
					}
				case auxSymValAndOff:
					off := ValAndOff(v.AuxInt).Off()
					if v.Aux != nil || off < 0 || off >= minZeroPage {
						continue
					}
				case auxInt32:
					// Mips uses this auxType for atomic add constant. It does not affect the effective address.
				case auxInt64:
					// ARM uses this auxType for duffcopy/duffzero/alignment info.
					// It does not affect the effective address.
				case auxNone:
					// offset is zero.
				default:
					v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
				}
				// This instruction is guaranteed to fault if ptr is nil.
				// Any previous nil check op is unnecessary.
				unnecessary.add(ptr.ID)
			}
		}
		// Remove values we've clobbered with OpUnknown.
		i := 0
		for _, v := range b.Values {
			if v.Op != OpUnknown {
				b.Values[i] = v
				i++
			}
		}
		for j := i; j < len(b.Values); j++ {
			b.Values[j] = nil
		}
		b.Values = b.Values[:i]

		// TODO: if b.Kind == BlockPlain, start the analysis in the subsequent block to find
		// more unnecessary nil checks.  Would fix test/nilptr3_ssa.go:157.
	}
}
<commit_msg>cmd/compile: eliminate more nil checks of phis<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssa

// nilcheckelim eliminates unnecessary nil checks.
// runs on machine-independent code.
func nilcheckelim(f *Func) {
	// A nil check is redundant if the same nil check was successful in a
	// dominating block. The efficacy of this pass depends heavily on the
	// efficacy of the cse pass.
	sdom := f.sdom()

	// TODO: Eliminate more nil checks.
	// We can recursively remove any chain of fixed offset calculations,
	// i.e. struct fields and array elements, even with non-constant
	// indices: x is non-nil iff x.a.b[i].c is.

	type walkState int
	const (
		Work     walkState = iota // process nil checks and traverse to dominees
		ClearPtr                  // forget the fact that ptr is nil
	)

	type bp struct {
		block *Block // block, or nil in ClearPtr state
		ptr   *Value // if non-nil, ptr that is to be cleared in ClearPtr state
		op    walkState
	}

	work := make([]bp, 0, 256)
	work = append(work, bp{block: f.Entry})

	// map from value ID to bool indicating if value is known to be non-nil
	// in the current dominator path being walked. This slice is updated by
	// walkStates to maintain the known non-nil values.
	nonNilValues := make([]bool, f.NumValues())

	// make an initial pass identifying any non-nil values
	for _, b := range f.Blocks {
		for _, v := range b.Values {
			// a value resulting from taking the address of a
			// value, or a value constructed from an offset of a
			// non-nil ptr (OpAddPtr) implies it is non-nil
			if v.Op == OpAddr || v.Op == OpAddPtr {
				nonNilValues[v.ID] = true
			}
		}
	}

	for changed := true; changed; {
		changed = false
		for _, b := range f.Blocks {
			for _, v := range b.Values {
				// phis whose arguments are all non-nil
				// are non-nil
				if v.Op == OpPhi {
					argsNonNil := true
					for _, a := range v.Args {
						if !nonNilValues[a.ID] {
							argsNonNil = false
							break
						}
					}
					if argsNonNil {
						if !nonNilValues[v.ID] {
							changed = true
						}
						nonNilValues[v.ID] = true
					}
				}
			}
		}
	}

	// allocate auxiliary date structures for computing store order
	sset := f.newSparseSet(f.NumValues())
	defer f.retSparseSet(sset)
	storeNumber := make([]int32, f.NumValues())

	// perform a depth first walk of the dominee tree
	for len(work) > 0 {
		node := work[len(work)-1]
		work = work[:len(work)-1]

		switch node.op {
		case Work:
			b := node.block

			// First, see if we're dominated by an explicit nil check.
			if len(b.Preds) == 1 {
				p := b.Preds[0].b
				if p.Kind == BlockIf && p.Control.Op == OpIsNonNil && p.Succs[0].b == b {
					ptr := p.Control.Args[0]
					if !nonNilValues[ptr.ID] {
						nonNilValues[ptr.ID] = true
						work = append(work, bp{op: ClearPtr, ptr: ptr})
					}
				}
			}

			// Next, order values in the current block w.r.t. stores.
			b.Values = storeOrder(b.Values, sset, storeNumber)

			// Next, process values in the block.
			i := 0
			for _, v := range b.Values {
				b.Values[i] = v
				i++
				switch v.Op {
				case OpIsNonNil:
					ptr := v.Args[0]
					if nonNilValues[ptr.ID] {
						// This is a redundant explicit nil check.
						v.reset(OpConstBool)
						v.AuxInt = 1 // true
					}
				case OpNilCheck:
					ptr := v.Args[0]
					if nonNilValues[ptr.ID] {
						// This is a redundant implicit nil check.
						// Logging in the style of the former compiler -- and omit line 1,
						// which is usually in generated code.
						if f.Config.Debug_checknil() && v.Pos.Line() > 1 {
							f.Config.Warnl(v.Pos, "removed nil check")
						}
						v.reset(OpUnknown)
						// TODO: f.freeValue(v)
						i--
						continue
					}
					// Record the fact that we know ptr is non nil, and remember to
					// undo that information when this dominator subtree is done.
					nonNilValues[ptr.ID] = true
					work = append(work, bp{op: ClearPtr, ptr: ptr})
				}
			}
			for j := i; j < len(b.Values); j++ {
				b.Values[j] = nil
			}
			b.Values = b.Values[:i]

			// Add all dominated blocks to the work list.
			for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
				work = append(work, bp{op: Work, block: w})
			}

		case ClearPtr:
			nonNilValues[node.ptr.ID] = false
			continue
		}
	}
}

// All platforms are guaranteed to fault if we load/store to anything smaller than this address.
//
// This should agree with minLegalPointer in the runtime.
const minZeroPage = 4096

// nilcheckelim2 eliminates unnecessary nil checks.
// Runs after lowering and scheduling.
func nilcheckelim2(f *Func) {
	unnecessary := f.newSparseSet(f.NumValues())
	defer f.retSparseSet(unnecessary)
	for _, b := range f.Blocks {
		// Walk the block backwards. Find instructions that will fault if their
		// input pointer is nil. Remove nil checks on those pointers, as the
		// faulting instruction effectively does the nil check for free.
		unnecessary.clear()
		for i := len(b.Values) - 1; i >= 0; i-- {
			v := b.Values[i]
			if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
				if f.Config.Debug_checknil() && v.Pos.Line() > 1 {
					f.Config.Warnl(v.Pos, "removed nil check")
				}
				v.reset(OpUnknown)
				continue
			}
			if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
				if v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive {
					// These ops don't really change memory.
					continue
				}
				// This op changes memory.  Any faulting instruction after v that
				// we've recorded in the unnecessary map is now obsolete.
				unnecessary.clear()
			}

			// Find any pointers that this op is guaranteed to fault on if nil.
			var ptrstore [2]*Value
			ptrs := ptrstore[:0]
			if opcodeTable[v.Op].faultOnNilArg0 {
				ptrs = append(ptrs, v.Args[0])
			}
			if opcodeTable[v.Op].faultOnNilArg1 {
				ptrs = append(ptrs, v.Args[1])
			}
			for _, ptr := range ptrs {
				// Check to make sure the offset is small.
				switch opcodeTable[v.Op].auxType {
				case auxSymOff:
					if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
						continue
					}
				case auxSymValAndOff:
					off := ValAndOff(v.AuxInt).Off()
					if v.Aux != nil || off < 0 || off >= minZeroPage {
						continue
					}
				case auxInt32:
					// Mips uses this auxType for atomic add constant. It does not affect the effective address.
				case auxInt64:
					// ARM uses this auxType for duffcopy/duffzero/alignment info.
					// It does not affect the effective address.
				case auxNone:
					// offset is zero.
				default:
					v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
				}
				// This instruction is guaranteed to fault if ptr is nil.
				// Any previous nil check op is unnecessary.
				unnecessary.add(ptr.ID)
			}
		}
		// Remove values we've clobbered with OpUnknown.
		i := 0
		for _, v := range b.Values {
			if v.Op != OpUnknown {
				b.Values[i] = v
				i++
			}
		}
		for j := i; j < len(b.Values); j++ {
			b.Values[j] = nil
		}
		b.Values = b.Values[:i]

		// TODO: if b.Kind == BlockPlain, start the analysis in the subsequent block to find
		// more unnecessary nil checks.  Would fix test/nilptr3_ssa.go:157.
	}
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Douglas Thrift
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main
<commit_msg>Add Mac OS X stub for rdpLaunchNative.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Douglas Thrift
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

func rdpLaunchNative(instance *Instance, private bool, index int, arguments []string, prompt bool, username string) error {
	return nil
}
<|endoftext|>


<commit_before>package flux

import (
	"sync"
	"testing"
)

func TestReactiveStack(t *testing.T) {

	ws := new(sync.WaitGroup)
	gen := ReactIdentity()

	ws.Add(2)
	mn := gen.React(func(k ReactiveStacks) {
		func() {
		mloop:
			for {
				select {
				case <-k.In():
					ws.Done()
				case <-k.Feed().Out():
					ws.Done()
				case <-gen.Closed():
					k.End()
					break mloop
				}
			}
		}()
	})

	gen.In() <- 2
	mn.In() <- 40

	ws.Wait()
	gen.End()

}
<commit_msg>adding base test<commit_after>package flux

import (
	"sync"
	"testing"
)

//TestBasicReaction provides a test case for a simple two-way reaction i.e receive and react infinitely
//to data being sent into a pipe until that pipe is closed
func TestBasicReaction(t *testing.T) {

	ws := new(sync.WaitGroup)

	//ReactIdentity returns reactor that simply pipes what input it gets to its output if it has a connection

	//collect is the root reactor
	collect := ReactIdentity()

	ws.Add(2)

	//.React() is the simple approach of branching out from a root reactor,you receive the new reactor its self
	//but are able to get the parent reactor using the .Feed() function and collect the data from its Out() pipe
	//all reactor provide a .Close() channel that lets you listen in for when to stop operations,this way you get to decide
	//if you wish to stop the new reactor along
	mn := collect.React(func(k ReactiveStacks) {
		func() {
		mloop:
			for {
				select {
				case <-k.In():
					ws.Done()
				case <-k.Feed().Out():
					ws.Done()
				case <-k.Feed().Closed():
					k.End()
					break mloop
				}
			}
		}()
	})

	/*we can emit/send data for reaction using the in-channel returned bythe In() function*/
	collect.In() <- 2 //sends data into the root reactor
	mn.In() <- 40     //sends data only into the current reactor

	ws.Wait()

	//Reactors are closed by calling the .End() function
	collect.End()

}
<|endoftext|>


<commit_before>package alog

import (
	"strings"
	"testing"
)

// A test appender that adds messages to a list
type TestAppender struct {
	MessageQueue chan string
	quit         chan int
	logs         []string
}

func (app *TestAppender) StartListening(l *Logger) {
	for {
		select {
		case msg := <-app.MessageQueue:
			app.logs = append(app.logs, msg)
		case <-app.quit:
			l.wg.Done()
			return
		}
	}
}

func (app *TestAppender) Append(msg string) {
	app.MessageQueue <- msg
}

// Finalize triggers the end of the logging loop
func (app *TestAppender) Finalize() {
	app.quit <- 1
}
func TestLogger(t *testing.T) {
	var appender TestAppender
	appender.MessageQueue = make(chan string)
	appender.logs = make([]string, 0)
	appender.quit = make(chan int)
	logger := NewLogger("testlogger", []Appender{&appender})
	if logger == nil {
		t.Fail()
	}
	if len(logger.appenders) != 1 {
		t.Fail()
	}
	logger.Log("Test message", INFO)
	if len(appender.logs) != 1 {
		t.Fail()
	}
	if strings.Compare(appender.logs[0], "Test message") != 0 {
		t.Fail()
	}
	logger.Finalize()
}
<commit_msg>Fixed test<commit_after>package alog

import (
	"strings"
	"testing"
	"time"
)

// A test appender that adds messages to a list
type TestAppender struct {
	MessageQueue chan string
	quit         chan int
	logs         []string
}

func (app *TestAppender) StartListening(l *Logger) {
	for {
		select {
		case msg := <-app.MessageQueue:
			app.logs = append(app.logs, msg)
		case <-app.quit:
			l.wg.Done()
			return
		}
	}
}

func (app *TestAppender) Append(msg string) {
	app.MessageQueue <- msg
}

// Finalize triggers the end of the logging loop
func (app *TestAppender) Finalize() {
	app.quit <- 1
}
func TestLogger(t *testing.T) {
	var appender TestAppender
	appender.MessageQueue = make(chan string)
	appender.logs = make([]string, 0)
	appender.quit = make(chan int)
	logger := NewLogger("testlogger", []Appender{&appender})
	if logger == nil {
		t.Fail()
	}
	if len(logger.appenders) != 1 {
		t.Fail()
	}
	logger.Log("Test message", INFO)
	// Wait a reasonable time for the message to be logged
	time.Sleep(100 * time.Millisecond)
	if len(appender.logs) != 1 {
		t.Fail()
	}
	if strings.Compare(appender.logs[0], "Test message") != 0 {
		t.Fail()
	}
	logger.Finalize()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

var analyticsPath = filepath.Join(CacheHome, "analytics.json")

type analyticsBody struct {
	Schema   int                `json:"schema"`
	Commands []AnalyticsCommand `json:"commands"`
	User     string             `json:"user,omitempty"`
}

var currentAnalyticsCommand = &AnalyticsCommand{
	Timestamp: time.Now().Unix(),
	OS:        runtime.GOOS,
	Arch:      runtime.GOARCH,
	Language:  "go",
	Valid:     true,
}

// AnalyticsCommand represents an analytics command
type AnalyticsCommand struct {
	Command       string `json:"command"`
	Plugin        string `json:"plugin,omitempty"`
	PluginVersion string `json:"plugin_version,omitempty"`
	Timestamp     int64  `json:"timestamp"`
	Version       string `json:"version"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	Language      string `json:"language"`
	Status        int    `json:"status"`
	Runtime       int64  `json:"runtime"`
	Valid         bool   `json:"valid"`
	start         time.Time
}

// RecordStart marks when a command was started (for tracking runtime)
func (c *AnalyticsCommand) RecordStart() {
	c.Version = Version
	c.start = time.Now()
}

// RecordEnd marks when a command was completed
// and records it to the analytics file
func (c *AnalyticsCommand) RecordEnd(status int) {
	if c == nil || skipAnalytics() || len(Args) < 2 || (c.Valid && c.start.IsZero()) {
		return
	}
	c.Command = Args[1]
	c.Status = status
	if !c.start.IsZero() {
		c.Runtime = (time.Now().UnixNano() - c.start.UnixNano()) / 1000000
	}
	file := readAnalyticsFile()
	file.Commands = append(file.Commands, *c)
	LogIfError(writeAnalyticsFile(file))
}

func readAnalyticsFile() (file analyticsBody) {
	f, err := os.Open(analyticsPath)
	if err != nil {
		if !os.IsNotExist(err) {
			LogIfError(err)
		}
		return
	}
	if err := json.NewDecoder(f).Decode(&file); err != nil {
		LogIfError(err)
	}
	if file.Schema != 1 {
		return analyticsBody{Schema: 1}
	}
	return file
}

func writeAnalyticsFile(file analyticsBody) error {
	data, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(analyticsPath, data, 0644)
}

// SubmitAnalytics sends the analytics info to the analytics service
func SubmitAnalytics() {
	if skipAnalytics() {
		return
	}
	file := readAnalyticsFile()
	lockfile := filepath.Join(CacheHome, "analytics.lock")
	golock.Lock(lockfile)
	defer golock.Unlock(lockfile)
	file = readAnalyticsFile() // read commands again in case it was locked
	file.User = netrcLogin()

	host := os.Getenv("HEROKU_ANALYTICS_HOST")
	if host == "" {
		host = "https://cli-analytics.heroku.com"
	}

	resp, err := sling.New().Base(host).Post("/record").BodyJSON(file).ReceiveSuccess(nil)
	if err != nil {
		LogIfError(err)
		return
	}
	LogIfError(getHTTPError(resp))
	writeAnalyticsFile(analyticsBody{Schema: 1})
}

func skipAnalytics() bool {
	return os.Getenv("TESTING") == ONE || (config.SkipAnalytics != nil && *config.SkipAnalytics) || netrcLogin() == ""
}
<commit_msg>omit commands when empty<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

var analyticsPath = filepath.Join(CacheHome, "analytics.json")

type analyticsBody struct {
	Schema   int                `json:"schema"`
	Commands []AnalyticsCommand `json:"commands,omitempty"`
	User     string             `json:"user,omitempty"`
}

var currentAnalyticsCommand = &AnalyticsCommand{
	Timestamp: time.Now().Unix(),
	OS:        runtime.GOOS,
	Arch:      runtime.GOARCH,
	Language:  "go",
	Valid:     true,
}

// AnalyticsCommand represents an analytics command
type AnalyticsCommand struct {
	Command       string `json:"command"`
	Plugin        string `json:"plugin,omitempty"`
	PluginVersion string `json:"plugin_version,omitempty"`
	Timestamp     int64  `json:"timestamp"`
	Version       string `json:"version"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	Language      string `json:"language"`
	Status        int    `json:"status"`
	Runtime       int64  `json:"runtime"`
	Valid         bool   `json:"valid"`
	start         time.Time
}

// RecordStart marks when a command was started (for tracking runtime)
func (c *AnalyticsCommand) RecordStart() {
	c.Version = Version
	c.start = time.Now()
}

// RecordEnd marks when a command was completed
// and records it to the analytics file
func (c *AnalyticsCommand) RecordEnd(status int) {
	if c == nil || skipAnalytics() || len(Args) < 2 || (c.Valid && c.start.IsZero()) {
		return
	}
	c.Command = Args[1]
	c.Status = status
	if !c.start.IsZero() {
		c.Runtime = (time.Now().UnixNano() - c.start.UnixNano()) / 1000000
	}
	file := readAnalyticsFile()
	file.Commands = append(file.Commands, *c)
	LogIfError(writeAnalyticsFile(file))
}

func readAnalyticsFile() (file analyticsBody) {
	f, err := os.Open(analyticsPath)
	if err != nil {
		if !os.IsNotExist(err) {
			LogIfError(err)
		}
		return
	}
	if err := json.NewDecoder(f).Decode(&file); err != nil {
		LogIfError(err)
	}
	if file.Schema != 1 {
		return analyticsBody{Schema: 1}
	}
	return file
}

func writeAnalyticsFile(file analyticsBody) error {
	data, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(analyticsPath, data, 0644)
}

// SubmitAnalytics sends the analytics info to the analytics service
func SubmitAnalytics() {
	if skipAnalytics() {
		return
	}
	file := readAnalyticsFile()
	lockfile := filepath.Join(CacheHome, "analytics.lock")
	golock.Lock(lockfile)
	defer golock.Unlock(lockfile)
	file = readAnalyticsFile() // read commands again in case it was locked
	file.User = netrcLogin()

	host := os.Getenv("HEROKU_ANALYTICS_HOST")
	if host == "" {
		host = "https://cli-analytics.heroku.com"
	}

	resp, err := sling.New().Base(host).Post("/record").BodyJSON(file).ReceiveSuccess(nil)
	if err != nil {
		LogIfError(err)
		return
	}
	LogIfError(getHTTPError(resp))
	writeAnalyticsFile(analyticsBody{Schema: 1})
}

func skipAnalytics() bool {
	return os.Getenv("TESTING") == ONE || (config.SkipAnalytics != nil && *config.SkipAnalytics) || netrcLogin() == ""
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/gin-gonic/gin"
)

// ClientRequest TODO
type ClientRequest struct {
	Timestamp    time.Time `xorm:"'timestamp' pk notnull"`
	IP           string    `xorm:"'ip' pk notnull"`
	URL          string    `xorm:"'url' notnull"`
	UserAgent    string    `xorm:"'user-agent'"`
	Referer      string    `xorm:"'referer'"`
	HTTPCode     int       `xorm:"'httpcode'"`
	ResponseTime time.Duration
}

// Analytics TODO
func Analytics(c *gin.Context) {
	// Get the current time
	then := time.Now()

	// Let the rest of the request happen
	c.Next()

	clientip := c.Request.Header.Get("X-Forwarded-For")
	if clientip == "" {
		clientip = c.Request.RemoteAddr
	}
	// Build our client request record
	cr := ClientRequest{
		Timestamp:    time.Now(),
		UserAgent:    c.Request.UserAgent(),
		IP:           clientip,
		URL:          c.Request.RequestURI,
		Referer:      c.Request.Referer(),
		HTTPCode:     c.Writer.Status(),
		ResponseTime: time.Since(then),
	}
	// log this to the database
	orm.Insert(cr)
}
<commit_msg>Maybe order matters?<commit_after>package main

import (
	"time"

	"github.com/gin-gonic/gin"
)

// ClientRequest TODO
type ClientRequest struct {
	Timestamp    time.Time `xorm:"pk not null 'timestamp'"`
	IP           string    `xorm:"pk not null 'ip'"`
	URL          string    `xorm:"not null 'url'"`
	UserAgent    string    `xorm:"'user-agent'"`
	Referer      string    `xorm:"'referer'"`
	HTTPCode     int       `xorm:"'httpcode'"`
	ResponseTime time.Duration
}

// Analytics TODO
func Analytics(c *gin.Context) {
	// Get the current time
	then := time.Now()

	// Let the rest of the request happen
	c.Next()

	clientip := c.Request.Header.Get("X-Forwarded-For")
	if clientip == "" {
		clientip = c.Request.RemoteAddr
	}
	// Build our client request record
	cr := ClientRequest{
		Timestamp:    time.Now(),
		UserAgent:    c.Request.UserAgent(),
		IP:           clientip,
		URL:          c.Request.RequestURI,
		Referer:      c.Request.Referer(),
		HTTPCode:     c.Writer.Status(),
		ResponseTime: time.Since(then),
	}
	// log this to the database
	orm.Insert(cr)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/conformal/btcjson"
	"github.com/conformal/go-flags"
	"github.com/davecgh/go-spew/spew"
	"os"
	"strconv"
)

type config struct {
	Help        bool   `short:"h" long:"help" description:"Help"`
	RpcUser     string `short:"u" description:"RPC username"`
	RpcPassword string `short:"P" long:"rpcpass" description:"RPC password"`
	RpcServer   string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
}

// conversionHandler is a handler that is used to convert parameters from the
// command line to a specific type.  This is needed since the btcjson API
// expects certain types for various parameters.
type conversionHandler func(string) (interface{}, error)

// displayHandler is a handler that takes an interface and displays it to
// standard out.  It is used by the handler data to type assert replies and
// show them formatted as desired.
type displayHandler func(interface{}) error

// handlerData contains information about how a command should be handled.
type handlerData struct {
	requiredArgs       int
	optionalArgs       int
	displayHandler     displayHandler
	conversionHandlers []conversionHandler
}

var (
	ErrNoData           = errors.New("No data returned.")
	ErrNoDisplayHandler = errors.New("No display handler specified.")
	ErrUsage            = errors.New("btcctl usage") // Real usage is shown.
)

// commandHandlers is a map of commands and associate handler data
// that is used to validate correctness and perform the command.
var commandHandlers = map[string]*handlerData{
	"addnode":              &handlerData{2, 0, displaySpewDump, nil},
	"decoderawtransaction": &handlerData{1, 0, displaySpewDump, nil},
	"getbestblockhash":     &handlerData{0, 0, displayGeneric, nil},
	"getblock":             &handlerData{1, 0, displaySpewDump, nil},
	"getblockcount":        &handlerData{0, 0, displayFloat64, nil},
	"getblockhash":         &handlerData{1, 0, displayGeneric, []conversionHandler{toInt}},
	"getconnectioncount":   &handlerData{0, 0, displayFloat64, nil},
	"getdifficulty":        &handlerData{0, 0, displayFloat64, nil},
	"getgenerate":          &handlerData{0, 0, displayGeneric, nil},
	"getpeerinfo":          &handlerData{0, 0, displaySpewDump, nil},
	"getrawmempool":        &handlerData{0, 0, displaySpewDump, nil},
	"getrawtransaction":    &handlerData{1, 1, displaySpewDump, []conversionHandler{nil, toInt}},
	"stop":                 &handlerData{0, 0, displayGeneric, nil},
}

// toInt attempt to convert the passed string to an integer.  It returns the
// integer packed into interface so it can be used in the calls which expect
// interfaces.  An error will be returned if the string can't be converted to an
// integer.
func toInt(val string) (interface{}, error) {
	idx, err := strconv.Atoi(val)
	if err != nil {
		return nil, err
	}

	return idx, nil
}

// displayGeneric is a displayHandler that simply displays the passed interface
// using fmt.Println.
func displayGeneric(reply interface{}) error {
	fmt.Println(reply)
	return nil
}

// displayFloat64 is a displayHandler that ensures the concrete type of the
// passed interface is a float64 and displays it using fmt.Println.  An error
// is returned if a float64 is not passed.
func displayFloat64(reply interface{}) error {
	if val, ok := reply.(float64); ok {
		fmt.Println(val)
		return nil
	}

	return fmt.Errorf("reply type is not a float64: %v", spew.Sdump(reply))
}

// displaySpewDump is a displayHandler that simply uses spew.Dump to display the
// passed interface.
func displaySpewDump(reply interface{}) error {
	spew.Dump(reply)
	return nil
}

// send sends a JSON-RPC command to the specified RPC server and examines the
// results for various error conditions.  It either returns a valid result or
// an appropriate error.
func send(cfg *config, msg []byte) (interface{}, error) {
	reply, err := btcjson.RpcCommand(cfg.RpcUser, cfg.RpcPassword, cfg.RpcServer, msg)
	if err != nil {
		return nil, err
	}

	if reply.Error != nil {
		return nil, reply.Error
	}

	if reply.Result == nil {
		return nil, ErrNoData
	}

	return reply.Result, nil
}

// sendCommand creates a JSON-RPC command using the passed command and arguments
// and then sends it.  A prefix is added to any errors that occur indicating
// what step failed.
func sendCommand(cfg *config, command string, args ...interface{}) (interface{}, error) {
	msg, err := btcjson.CreateMessage(command, args...)
	if err != nil {
		return nil, fmt.Errorf("CreateMessage: %v", err.Error())
	}

	reply, err := send(cfg, msg)
	if err != nil {
		return nil, fmt.Errorf("RpcCommand: %v", err.Error())
	}

	return reply, nil
}

// commandHandler handles commands provided via the cli using the specific
// handler data to instruct the handler what to do.
func commandHandler(cfg *config, command string, data *handlerData, args []string) error {
	// Ensure the number of arguments are the expected value.
	if len(args) < data.requiredArgs {
		return ErrUsage
	}
	if len(args) > data.requiredArgs+data.optionalArgs {
		return ErrUsage
	}

	// Ensure the number of conversion handlers is valid if any are
	// specified.
	convHandlers := data.conversionHandlers
	if convHandlers != nil && len(convHandlers) < len(args) {
		return fmt.Errorf("The number of conversion handlers is invalid.")
	}

	// Convert input parameters per the conversion handlers.
	iargs := make([]interface{}, len(args))
	for i, arg := range args {
		iargs[i] = arg
	}
	for i := range iargs {
		if convHandlers != nil {
			converter := convHandlers[i]
			if converter != nil {
				convertedArg, err := converter(args[i])
				if err != nil {
					return err
				}
				iargs[i] = convertedArg
			}
		}
	}

	// Create and send the appropriate JSON-RPC command.
	reply, err := sendCommand(cfg, command, iargs...)
	if err != nil {
		return err
	}

	// Ensure there is a valid display handler and use it to display the
	// results of the JSON-RPC command.
	if data.displayHandler == nil {
		return ErrNoDisplayHandler
	}
	err = data.displayHandler(reply)
	if err != nil {
		return err
	}

	return nil
}

// usage displays the command usage.
func usage(parser *flags.Parser) {
	parser.WriteHelp(os.Stderr)
	fmt.Fprintf(os.Stderr,
		"\nCommands:\n"+
			"\taddnode <ip> <add/remove/onetry>\n"+
			"\tdecoderawtransaction <txhash>\n"+
			"\tgetbestblockhash\n"+
			"\tgetblock <blockhash>\n"+
			"\tgetblockcount\n"+
			"\tgetblockhash <blocknumber>\n"+
			"\tgetconnectioncount\n"+
			"\tgetdifficulty\n"+
			"\tgetgenerate\n"+
			"\tgetpeerinfo\n"+
			"\tgetrawmempool\n"+
			"\tgetrawtransaction <txhash> [verbose=0]\n"+
			"\tstop\n")
	os.Exit(1)
}

func main() {
	// Parse command line and show usage if needed.
	cfg := config{
		RpcServer: "127.0.0.1:8334",
	}
	parser := flags.NewParser(&cfg, flags.PassDoubleDash)
	args, err := parser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			usage(parser)
		}
		return
	}
	if cfg.Help {
		usage(parser)
		return
	}

	// Display usage if the command is not supported.
	data, exists := commandHandlers[args[0]]
	if !exists {
		fmt.Fprintf(os.Stderr, "Unrecognized command: %s\n", args[0])
		usage(parser)
		return
	}

	// Execute the command.
	err = commandHandler(&cfg, args[0], data, args[1:])
	if err != nil {
		if err == ErrUsage {
			usage(parser)
			return
		}

		fmt.Fprintf(os.Stderr, "%v\n", err)
	}
}
<commit_msg>Perform display handler check before issues RPC.<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/conformal/btcjson"
	"github.com/conformal/go-flags"
	"github.com/davecgh/go-spew/spew"
	"os"
	"strconv"
)

type config struct {
	Help        bool   `short:"h" long:"help" description:"Help"`
	RpcUser     string `short:"u" description:"RPC username"`
	RpcPassword string `short:"P" long:"rpcpass" description:"RPC password"`
	RpcServer   string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
}

// conversionHandler is a handler that is used to convert parameters from the
// command line to a specific type.  This is needed since the btcjson API
// expects certain types for various parameters.
type conversionHandler func(string) (interface{}, error)

// displayHandler is a handler that takes an interface and displays it to
// standard out.  It is used by the handler data to type assert replies and
// show them formatted as desired.
type displayHandler func(interface{}) error

// handlerData contains information about how a command should be handled.
type handlerData struct {
	requiredArgs       int
	optionalArgs       int
	displayHandler     displayHandler
	conversionHandlers []conversionHandler
}

var (
	ErrNoData           = errors.New("No data returned.")
	ErrNoDisplayHandler = errors.New("No display handler specified.")
	ErrUsage            = errors.New("btcctl usage") // Real usage is shown.
)

// commandHandlers is a map of commands and associate handler data
// that is used to validate correctness and perform the command.
var commandHandlers = map[string]*handlerData{
	"addnode":              &handlerData{2, 0, displaySpewDump, nil},
	"decoderawtransaction": &handlerData{1, 0, displaySpewDump, nil},
	"getbestblockhash":     &handlerData{0, 0, displayGeneric, nil},
	"getblock":             &handlerData{1, 0, displaySpewDump, nil},
	"getblockcount":        &handlerData{0, 0, displayFloat64, nil},
	"getblockhash":         &handlerData{1, 0, displayGeneric, []conversionHandler{toInt}},
	"getconnectioncount":   &handlerData{0, 0, displayFloat64, nil},
	"getdifficulty":        &handlerData{0, 0, displayFloat64, nil},
	"getgenerate":          &handlerData{0, 0, displayGeneric, nil},
	"getpeerinfo":          &handlerData{0, 0, displaySpewDump, nil},
	"getrawmempool":        &handlerData{0, 0, displaySpewDump, nil},
	"getrawtransaction":    &handlerData{1, 1, displaySpewDump, []conversionHandler{nil, toInt}},
	"stop":                 &handlerData{0, 0, displayGeneric, nil},
}

// toInt attempts to convert the passed string to an integer.  It returns the
// integer packed into an interface so it can be used in the calls which expect
// interfaces.  An error will be returned if the string can't be converted to an
// integer.
func toInt(val string) (interface{}, error) {
	idx, err := strconv.Atoi(val)
	if err != nil {
		return nil, err
	}

	return idx, nil
}

// displayGeneric is a displayHandler that simply displays the passed interface
// using fmt.Println.
func displayGeneric(reply interface{}) error {
	fmt.Println(reply)
	return nil
}

// displayFloat64 is a displayHandler that ensures the concrete type of the
// passed interface is a float64 and displays it using fmt.Println.  An error
// is returned if a float64 is not passed.
func displayFloat64(reply interface{}) error {
	if val, ok := reply.(float64); ok {
		fmt.Println(val)
		return nil
	}

	return fmt.Errorf("reply type is not a float64: %v", spew.Sdump(reply))
}

// displaySpewDump is a displayHandler that simply uses spew.Dump to display the
// passed interface.
func displaySpewDump(reply interface{}) error {
	spew.Dump(reply)
	return nil
}

// send sends a JSON-RPC command to the specified RPC server and examines the
// results for various error conditions.  It either returns a valid result or
// an appropriate error.
func send(cfg *config, msg []byte) (interface{}, error) {
	reply, err := btcjson.RpcCommand(cfg.RpcUser, cfg.RpcPassword, cfg.RpcServer, msg)
	if err != nil {
		return nil, err
	}

	if reply.Error != nil {
		return nil, reply.Error
	}

	if reply.Result == nil {
		return nil, ErrNoData
	}

	return reply.Result, nil
}

// sendCommand creates a JSON-RPC command using the passed command and arguments
// and then sends it.  A prefix is added to any errors that occur indicating
// what step failed.
func sendCommand(cfg *config, command string, args ...interface{}) (interface{}, error) {
	msg, err := btcjson.CreateMessage(command, args...)
	if err != nil {
		return nil, fmt.Errorf("CreateMessage: %v", err.Error())
	}

	reply, err := send(cfg, msg)
	if err != nil {
		return nil, fmt.Errorf("RpcCommand: %v", err.Error())
	}

	return reply, nil
}

// commandHandler handles commands provided via the cli using the specific
// handler data to instruct the handler what to do.
func commandHandler(cfg *config, command string, data *handlerData, args []string) error {
	// Ensure the number of arguments are the expected value.
	if len(args) < data.requiredArgs {
		return ErrUsage
	}
	if len(args) > data.requiredArgs+data.optionalArgs {
		return ErrUsage
	}

	// Ensure there is a display handler.
	if data.displayHandler == nil {
		return ErrNoDisplayHandler
	}

	// Ensure the number of conversion handlers is valid if any are
	// specified.
	convHandlers := data.conversionHandlers
	if convHandlers != nil && len(convHandlers) < len(args) {
		return fmt.Errorf("The number of conversion handlers is invalid.")
	}

	// Convert input parameters per the conversion handlers.
	iargs := make([]interface{}, len(args))
	for i, arg := range args {
		iargs[i] = arg
	}
	for i := range iargs {
		if convHandlers != nil {
			converter := convHandlers[i]
			if converter != nil {
				convertedArg, err := converter(args[i])
				if err != nil {
					return err
				}
				iargs[i] = convertedArg
			}
		}
	}

	// Create and send the appropriate JSON-RPC command.
	reply, err := sendCommand(cfg, command, iargs...)
	if err != nil {
		return err
	}

	// Display the results of the JSON-RPC command using the provided
	// display handler.
	err = data.displayHandler(reply)
	if err != nil {
		return err
	}

	return nil
}

// usage displays the command usage.
func usage(parser *flags.Parser) {
	parser.WriteHelp(os.Stderr)
	fmt.Fprintf(os.Stderr,
		"\nCommands:\n"+
			"\taddnode <ip> <add/remove/onetry>\n"+
			"\tdecoderawtransaction <txhash>\n"+
			"\tgetbestblockhash\n"+
			"\tgetblock <blockhash>\n"+
			"\tgetblockcount\n"+
			"\tgetblockhash <blocknumber>\n"+
			"\tgetconnectioncount\n"+
			"\tgetdifficulty\n"+
			"\tgetgenerate\n"+
			"\tgetpeerinfo\n"+
			"\tgetrawmempool\n"+
			"\tgetrawtransaction <txhash> [verbose=0]\n"+
			"\tstop\n")
	os.Exit(1)
}

func main() {
	// Parse command line and show usage if needed.
	cfg := config{
		RpcServer: "127.0.0.1:8334",
	}
	parser := flags.NewParser(&cfg, flags.PassDoubleDash)
	args, err := parser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			usage(parser)
		}
		return
	}
	if cfg.Help {
		usage(parser)
		return
	}

	// Display usage if the command is not supported.
	data, exists := commandHandlers[args[0]]
	if !exists {
		fmt.Fprintf(os.Stderr, "Unrecognized command: %s\n", args[0])
		usage(parser)
		return
	}

	// Execute the command.
	err = commandHandler(&cfg, args[0], data, args[1:])
	if err != nil {
		if err == ErrUsage {
			usage(parser)
			return
		}

		fmt.Fprintf(os.Stderr, "%v\n", err)
	}
}
<|endoftext|>


<commit_before>package netlink

import (
	"net"
	"os"
	"syscall"
	"testing"
)

func TestAddr(t *testing.T) {
	if os.Getenv("TRAVIS_BUILD_DIR") != "" {
		t.Skipf("Fails in travis with: addr_test.go:68: Address flags not set properly, got=0, expected=128")
	}
	// TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels?
	var address = &net.IPNet{net.IPv4(127, 0, 0, 2), net.CIDRMask(24, 32)}
	var addrTests = []struct {
		addr     *Addr
		expected *Addr
	}{
		{
			&Addr{IPNet: address},
			&Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
		},
		{
			&Addr{IPNet: address, Label: "local"},
			&Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
		},
		{
			&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
		},
		{
			&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
		},
		{
			&Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE},
		},
	}

	tearDown := setUpNetlinkTest(t)
	defer tearDown()

	link, err := LinkByName("lo")
	if err != nil {
		t.Fatal(err)
	}

	for _, tt := range addrTests {
		if err = AddrAdd(link, tt.addr); err != nil {
			t.Fatal(err)
		}

		addrs, err := AddrList(link, FAMILY_ALL)
		if err != nil {
			t.Fatal(err)
		}

		if len(addrs) != 1 {
			t.Fatal("Address not added properly")
		}

		if !addrs[0].Equal(*tt.expected) {
			t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected)
		}

		if addrs[0].Label != tt.expected.Label {
			t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label)
		}

		if addrs[0].Flags != tt.expected.Flags {
			t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags)
		}

		if addrs[0].Scope != tt.expected.Scope {
			t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope)
		}

		if err = AddrDel(link, tt.addr); err != nil {
			t.Fatal(err)
		}

		addrs, err = AddrList(link, FAMILY_ALL)
		if err != nil {
			t.Fatal(err)
		}

		if len(addrs) != 0 {
			t.Fatal("Address not removed properly")
		}
	}
}
<commit_msg>add test for addList with wrong family number<commit_after>package netlink

import (
	"net"
	"os"
	"syscall"
	"testing"
)

func TestAddr(t *testing.T) {
	if os.Getenv("TRAVIS_BUILD_DIR") != "" {
		t.Skipf("Fails in travis with: addr_test.go:68: Address flags not set properly, got=0, expected=128")
	}
	// TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels?
	var address = &net.IPNet{net.IPv4(127, 0, 0, 2), net.CIDRMask(24, 32)}
	var addrTests = []struct {
		addr     *Addr
		expected *Addr
	}{
		{
			&Addr{IPNet: address},
			&Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
		},
		{
			&Addr{IPNet: address, Label: "local"},
			&Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
		},
		{
			&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
		},
		{
			&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
		},
		{
			&Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE},
			&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE},
		},
	}

	tearDown := setUpNetlinkTest(t)
	defer tearDown()

	link, err := LinkByName("lo")
	if err != nil {
		t.Fatal(err)
	}

	for _, tt := range addrTests {
		if err = AddrAdd(link, tt.addr); err != nil {
			t.Fatal(err)
		}

		addrs, err := AddrList(link, FAMILY_ALL)
		if err != nil {
			t.Fatal(err)
		}

		if len(addrs) != 1 {
			t.Fatal("Address not added properly")
		}

		if !addrs[0].Equal(*tt.expected) {
			t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected)
		}

		if addrs[0].Label != tt.expected.Label {
			t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label)
		}

		if addrs[0].Flags != tt.expected.Flags {
			t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags)
		}

		if addrs[0].Scope != tt.expected.Scope {
			t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope)
		}

		// Pass FAMILY_V4, we should get the same results as FAMILY_ALL
		addrs, err = AddrList(link, FAMILY_V4)
		if err != nil {
			t.Fatal(err)
		}
		if len(addrs) != 1 {
			t.Fatal("Address not added properly")
		}

		// Pass a wrong family number, we should get nil list
		addrs, err = AddrList(link, 0x8)
		if err != nil {
			t.Fatal(err)
		}

		if len(addrs) != 0 {
			t.Fatal("Address not expected")
		}

		if err = AddrDel(link, tt.addr); err != nil {
			t.Fatal(err)
		}

		addrs, err = AddrList(link, FAMILY_ALL)
		if err != nil {
			t.Fatal(err)
		}

		if len(addrs) != 0 {
			t.Fatal("Address not removed properly")
		}
	}
}
<|endoftext|>