<commit_before>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// VIPPoolMember represents a combination of node and port as a member of a VIP pool.
type VIPPoolMember struct {
	ID              string           `json:"id"`
	Pool            EntityReference  `json:"pool"`
	Node            VIPNodeReference `json:"node"`
	Port            *int             `json:"port,omitempty"`
	Status          string           `json:"status"`
	State           string           `json:"state"`
	NetworkDomainID string           `json:"networkDomainId"`
	DatacenterID    string           `json:"datacenterId"`
	CreateTime      string           `json:"createTime"`
}

// VIPPoolMembers represents a page of VIPPoolMember results.
type VIPPoolMembers struct {
	Items []VIPPoolMember `json:"poolMember"`

	PagedResult
}

// Request body for adding a VIP pool member.
type addPoolMember struct {
	PoolID string `json:"poolId"`
	NodeID string `json:"nodeId"`
	Status string `json:"status"`
	Port   *int   `json:"port,omitempty"`
}

// Request body for updating a VIP pool member.
type editPoolMember struct {
	ID     string `json:"id"`
	Status string `json:"status"`
}

// Request body for removing a VIP pool member.
type removePoolMember struct {
	ID string `json:"id"`
}

// ListVIPPoolMembers retrieves a list of all members of the specified VIP pool.
func (client *Client) ListVIPPoolMembers(poolID string, paging *Paging) (members *VIPPoolMembers, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember?poolId=%s&%s",
		organizationID,
		url.QueryEscape(poolID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list members of VIP pool '%s' failed with status code %d (%s): %s", poolID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	members = &VIPPoolMembers{}
	err = json.Unmarshal(responseBody, members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// ListVIPPoolMembershipsInNetworkDomain retrieves a list of all VIP pool memberships of the specified network domain.
func (client *Client) ListVIPPoolMembershipsInNetworkDomain(networkDomainID string, paging *Paging) (members *VIPPoolMembers, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember?networkDomainId=%s&%s",
		organizationID,
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list all VIP pool memberships in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	members = &VIPPoolMembers{}
	err = json.Unmarshal(responseBody, members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// GetVIPPoolMember retrieves the VIP pool member with the specified Id.
// Returns nil if no VIP pool member is found with the specified Id.
func (client *Client) GetVIPPoolMember(id string) (member *VIPPoolMember, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember/%s", organizationID, id)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve VIP pool with Id '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	member = &VIPPoolMember{}
	err = json.Unmarshal(responseBody, member)
	if err != nil {
		return nil, err
	}

	return member, nil
}

// AddVIPPoolMember adds a VIP node as a member of a VIP pool.
// State must be one of VIPNodeStatusEnabled, VIPNodeStatusDisabled, or VIPNodeStatusDisabled
// Returns the member ID (uniquely identifies this combination of node, pool, and port).
func (client *Client) AddVIPPoolMember(poolID string, nodeID string, status string, port *int) (poolMemberID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/addPoolMember", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &addPoolMember{
		PoolID: poolID,
		NodeID: nodeID,
		Status: status,
		Port:   port,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if statusCode != http.StatusOK {
		return "", apiResponse.ToError("Request to add VIP node '%s' as a member of pool '%s' failed with status code %d (%s): %s", nodeID, poolID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "poolMemberId", "value": "the-Id-of-the-new-pool-member" }
	poolMemberIDMessage := apiResponse.GetFieldMessage("poolMemberId")
	if poolMemberIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'poolMemberId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *poolMemberIDMessage, nil
}

// EditVIPPoolMember updates the status of an existing VIP pool member.
// status can be VIPNodeStatusEnabled, VIPNodeStatusDisabled, or VIPNodeStatusForcedOffline
func (client *Client) EditVIPPoolMember(id string, status string) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/editPoolMember", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &editPoolMember{
		ID:     id,
		Status: status,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to edit VIP pool member '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// RemoveVIPPoolMember removes a VIP pool member.
func (client *Client) RemoveVIPPoolMember(id string) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/removePoolMember", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &removePoolMember{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to remove member '%s' from its pool failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<commit_msg>Escape query parameters for VIP pool member request URIs (#7).<commit_after>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// VIPPoolMember represents a combination of node and port as a member of a VIP pool.
type VIPPoolMember struct {
	ID              string           `json:"id"`
	Pool            EntityReference  `json:"pool"`
	Node            VIPNodeReference `json:"node"`
	Port            *int             `json:"port,omitempty"`
	Status          string           `json:"status"`
	State           string           `json:"state"`
	NetworkDomainID string           `json:"networkDomainId"`
	DatacenterID    string           `json:"datacenterId"`
	CreateTime      string           `json:"createTime"`
}

// VIPPoolMembers represents a page of VIPPoolMember results.
type VIPPoolMembers struct {
	Items []VIPPoolMember `json:"poolMember"`

	PagedResult
}

// Request body for adding a VIP pool member.
type addPoolMember struct {
	PoolID string `json:"poolId"`
	NodeID string `json:"nodeId"`
	Status string `json:"status"`
	Port   *int   `json:"port,omitempty"`
}

// Request body for updating a VIP pool member.
type editPoolMember struct {
	ID     string `json:"id"`
	Status string `json:"status"`
}

// Request body for removing a VIP pool member.
type removePoolMember struct {
	ID string `json:"id"`
}

// ListVIPPoolMembers retrieves a list of all members of the specified VIP pool.
func (client *Client) ListVIPPoolMembers(poolID string, paging *Paging) (members *VIPPoolMembers, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember?poolId=%s&%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(poolID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list members of VIP pool '%s' failed with status code %d (%s): %s", poolID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	members = &VIPPoolMembers{}
	err = json.Unmarshal(responseBody, members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// ListVIPPoolMembershipsInNetworkDomain retrieves a list of all VIP pool memberships of the specified network domain.
func (client *Client) ListVIPPoolMembershipsInNetworkDomain(networkDomainID string, paging *Paging) (members *VIPPoolMembers, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember?networkDomainId=%s&%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list all VIP pool memberships in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	members = &VIPPoolMembers{}
	err = json.Unmarshal(responseBody, members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// GetVIPPoolMember retrieves the VIP pool member with the specified Id.
// Returns nil if no VIP pool member is found with the specified Id.
func (client *Client) GetVIPPoolMember(id string) (member *VIPPoolMember, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/poolMember/%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(id),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve VIP pool with Id '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	member = &VIPPoolMember{}
	err = json.Unmarshal(responseBody, member)
	if err != nil {
		return nil, err
	}

	return member, nil
}

// AddVIPPoolMember adds a VIP node as a member of a VIP pool.
// State must be one of VIPNodeStatusEnabled, VIPNodeStatusDisabled, or VIPNodeStatusDisabled
// Returns the member ID (uniquely identifies this combination of node, pool, and port).
func (client *Client) AddVIPPoolMember(poolID string, nodeID string, status string, port *int) (poolMemberID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/addPoolMember",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &addPoolMember{
		PoolID: poolID,
		NodeID: nodeID,
		Status: status,
		Port:   port,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if statusCode != http.StatusOK {
		return "", apiResponse.ToError("Request to add VIP node '%s' as a member of pool '%s' failed with status code %d (%s): %s", nodeID, poolID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "poolMemberId", "value": "the-Id-of-the-new-pool-member" }
	poolMemberIDMessage := apiResponse.GetFieldMessage("poolMemberId")
	if poolMemberIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'poolMemberId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *poolMemberIDMessage, nil
}

// EditVIPPoolMember updates the status of an existing VIP pool member.
// status can be VIPNodeStatusEnabled, VIPNodeStatusDisabled, or VIPNodeStatusForcedOffline
func (client *Client) EditVIPPoolMember(id string, status string) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/editPoolMember",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &editPoolMember{
		ID:     id,
		Status: status,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to edit VIP pool member '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// RemoveVIPPoolMember removes a VIP pool member.
func (client *Client) RemoveVIPPoolMember(id string) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/removePoolMember",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &removePoolMember{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to remove member '%s' from its pool failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<|endoftext|>


<commit_before>package consul_test

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	consulapi "github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/sdk/testutil"
	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/allocrunner/taskrunner"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/devicemanager"
	"github.com/hashicorp/nomad/client/pluginmanager/drivermanager"
	"github.com/hashicorp/nomad/client/state"
	"github.com/hashicorp/nomad/client/vaultclient"
	"github.com/hashicorp/nomad/command/agent/consul"
	"github.com/hashicorp/nomad/helper/testlog"
	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/stretchr/testify/require"
)

type mockUpdater struct {
	logger log.Logger
}

func (m *mockUpdater) TaskStateUpdated() {
	m.logger.Named("mock.updater").Debug("Update!")
}

// TestConsul_Integration asserts TaskRunner properly registers and deregisters
// services and checks with Consul using an embedded Consul agent.
func TestConsul_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("-short set; skipping")
	}
	require := require.New(t)

	// Create an embedded Consul server
	testconsul, err := testutil.NewTestServerConfig(func(c *testutil.TestServerConfig) {
		// If -v wasn't specified squelch consul logging
		if !testing.Verbose() {
			c.Stdout = ioutil.Discard
			c.Stderr = ioutil.Discard
		}
	})
	if err != nil {
		t.Fatalf("error starting test consul server: %v", err)
	}
	defer testconsul.Stop()

	conf := config.DefaultConfig()
	conf.Node = mock.Node()
	conf.ConsulConfig.Addr = testconsul.HTTPAddr
	consulConfig, err := conf.ConsulConfig.ApiConfig()
	if err != nil {
		t.Fatalf("error generating consul config: %v", err)
	}

	conf.StateDir, err = ioutil.TempDir("", "nomadtest-consulstate")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}
	defer os.RemoveAll(conf.StateDir)
	conf.AllocDir, err = ioutil.TempDir("", "nomdtest-consulalloc")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}
	defer os.RemoveAll(conf.AllocDir)

	alloc := mock.Alloc()
	task := alloc.Job.TaskGroups[0].Tasks[0]
	task.Driver = "mock_driver"
	task.Config = map[string]interface{}{
		"run_for": "1h",
	}

	// Choose a port that shouldn't be in use
	netResource := &structs.NetworkResource{
		Device:        "eth0",
		IP:            "127.0.0.1",
		MBits:         50,
		ReservedPorts: []structs.Port{{Label: "http", Value: 3}},
	}
	alloc.AllocatedResources.Tasks["web"].Networks[0] = netResource

	task.Services = []*structs.Service{
		{
			Name:      "httpd",
			PortLabel: "http",
			Tags:      []string{"nomad", "test", "http"},
			Checks: []*structs.ServiceCheck{
				{
					Name:     "httpd-http-check",
					Type:     "http",
					Path:     "/",
					Protocol: "http",
					Interval: 9000 * time.Hour,
					Timeout:  1, // fail as fast as possible
				},
				{
					Name:     "httpd-script-check",
					Type:     "script",
					Command:  "/bin/true",
					Interval: 10 * time.Second,
					Timeout:  10 * time.Second,
				},
			},
		},
		{
			Name:      "httpd2",
			PortLabel: "http",
			Tags: []string{
				"test",
				// Use URL-unfriendly tags to test #3620
				"public-test.ettaviation.com:80/ redirect=302,https://test.ettaviation.com",
				"public-test.ettaviation.com:443/",
			},
		},
	}

	logger := testlog.HCLogger(t)
	logUpdate := &mockUpdater{logger}
	allocDir := allocdir.NewAllocDir(logger, filepath.Join(conf.AllocDir, alloc.ID))
	if err := allocDir.Build(); err != nil {
		t.Fatalf("error building alloc dir: %v", err)
	}
	taskDir := allocDir.NewTaskDir(task.Name)
	vclient := vaultclient.NewMockVaultClient()
	consulClient, err := consulapi.NewClient(consulConfig)
	require.Nil(err)

	serviceClient := consul.NewServiceClient(consulClient.Agent(), testlog.HCLogger(t), true)
	defer serviceClient.Shutdown() // just-in-case cleanup
	consulRan := make(chan struct{})
	go func() {
		serviceClient.Run()
		close(consulRan)
	}()

	// Build the config
	config := &taskrunner.Config{
		Alloc:         alloc,
		ClientConfig:  conf,
		Consul:        serviceClient,
		Task:          task,
		TaskDir:       taskDir,
		Logger:        logger,
		Vault:         vclient,
		StateDB:       state.NoopDB{},
		StateUpdater:  logUpdate,
		DeviceManager: devicemanager.NoopMockManager(),
		DriverManager: drivermanager.TestDriverManager(t),
	}

	tr, err := taskrunner.NewTaskRunner(config)
	require.NoError(err)
	go tr.Run()
	defer func() {
		// Make sure we always shutdown task runner when the test exits
		select {
		case <-tr.WaitCh():
			// Exited cleanly, no need to kill
		default:
			tr.Kill(context.Background(), &structs.TaskEvent{}) // just in case
		}
	}()

	// Block waiting for the service to appear
	catalog := consulClient.Catalog()
	res, meta, err := catalog.Service("httpd2", "test", nil)
	require.Nil(err)

	for i := 0; len(res) == 0 && i < 10; i++ {
		//Expected initial request to fail, do a blocking query
		res, meta, err = catalog.Service("httpd2", "test", &consulapi.QueryOptions{WaitIndex: meta.LastIndex + 1, WaitTime: 3 * time.Second})
		if err != nil {
			t.Fatalf("error querying for service: %v", err)
		}
	}
	require.Len(res, 1)

	// Truncate results
	res = res[:]

	// Assert the service with the checks exists
	for i := 0; len(res) == 0 && i < 10; i++ {
		res, meta, err = catalog.Service("httpd", "http", &consulapi.QueryOptions{WaitIndex: meta.LastIndex + 1, WaitTime: 3 * time.Second})
		require.Nil(err)
	}
	require.Len(res, 1)

	// Assert the script check passes (mock_driver script checks always
	// pass) after having time to run once
	time.Sleep(2 * time.Second)
	checks, _, err := consulClient.Health().Checks("httpd", nil)
	require.Nil(err)
	require.Len(checks, 2)

	for _, check := range checks {
		if expected := "httpd"; check.ServiceName != expected {
			t.Fatalf("expected checks to be for %q but found service name = %q", expected, check.ServiceName)
		}
		switch check.Name {
		case "httpd-http-check":
			// Port check should fail
			if expected := consulapi.HealthCritical; check.Status != expected {
				t.Errorf("expected %q status to be %q but found %q", check.Name, expected, check.Status)
			}
		case "httpd-script-check":
			// mock_driver script checks always succeed
			if expected := consulapi.HealthPassing; check.Status != expected {
				t.Errorf("expected %q status to be %q but found %q", check.Name, expected, check.Status)
			}
		default:
			t.Errorf("unexpected check %q with status %q", check.Name, check.Status)
		}
	}

	// Assert the service client returns all the checks for the allocation.
	reg, err := serviceClient.AllocRegistrations(alloc.ID)
	if err != nil {
		t.Fatalf("unexpected error retrieving allocation checks: %v", err)
	}
	if reg == nil {
		t.Fatalf("Unexpected nil allocation registration")
	}
	if snum := reg.NumServices(); snum != 2 {
		t.Fatalf("Unexpected number of services registered. Got %d; want 2", snum)
	}
	if cnum := reg.NumChecks(); cnum != 2 {
		t.Fatalf("Unexpected number of checks registered. Got %d; want 2", cnum)
	}

	logger.Debug("killing task")

	// Kill the task
	tr.Kill(context.Background(), &structs.TaskEvent{})

	select {
	case <-tr.WaitCh():
	case <-time.After(10 * time.Second):
		t.Fatalf("timed out waiting for Run() to exit")
	}

	// Shutdown Consul ServiceClient to ensure all pending operations complete
	if err := serviceClient.Shutdown(); err != nil {
		t.Errorf("error shutting down Consul ServiceClient: %v", err)
	}

	// Ensure Consul is clean
	services, _, err := catalog.Services(nil)
	require.Nil(err)
	require.Len(services, 1)
	require.Contains(services, "consul")
}
<commit_msg>mock task hook coordinator in consul integration test<commit_after>package consul_test

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	consulapi "github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/sdk/testutil"
	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/allocrunner/taskrunner"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/devicemanager"
	"github.com/hashicorp/nomad/client/pluginmanager/drivermanager"
	"github.com/hashicorp/nomad/client/state"
	"github.com/hashicorp/nomad/client/vaultclient"
	"github.com/hashicorp/nomad/command/agent/consul"
	"github.com/hashicorp/nomad/helper/testlog"
	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/stretchr/testify/require"
)

type mockUpdater struct {
	logger log.Logger
}

func (m *mockUpdater) TaskStateUpdated() {
	m.logger.Named("mock.updater").Debug("Update!")
}

// TestConsul_Integration asserts TaskRunner properly registers and deregisters
// services and checks with Consul using an embedded Consul agent.
func TestConsul_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("-short set; skipping")
	}
	require := require.New(t)

	// Create an embedded Consul server
	testconsul, err := testutil.NewTestServerConfig(func(c *testutil.TestServerConfig) {
		// If -v wasn't specified squelch consul logging
		if !testing.Verbose() {
			c.Stdout = ioutil.Discard
			c.Stderr = ioutil.Discard
		}
	})
	if err != nil {
		t.Fatalf("error starting test consul server: %v", err)
	}
	defer testconsul.Stop()

	conf := config.DefaultConfig()
	conf.Node = mock.Node()
	conf.ConsulConfig.Addr = testconsul.HTTPAddr
	consulConfig, err := conf.ConsulConfig.ApiConfig()
	if err != nil {
		t.Fatalf("error generating consul config: %v", err)
	}

	conf.StateDir, err = ioutil.TempDir("", "nomadtest-consulstate")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}
	defer os.RemoveAll(conf.StateDir)
	conf.AllocDir, err = ioutil.TempDir("", "nomdtest-consulalloc")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}
	defer os.RemoveAll(conf.AllocDir)

	alloc := mock.Alloc()
	task := alloc.Job.TaskGroups[0].Tasks[0]
	task.Driver = "mock_driver"
	task.Config = map[string]interface{}{
		"run_for": "1h",
	}

	// Choose a port that shouldn't be in use
	netResource := &structs.NetworkResource{
		Device:        "eth0",
		IP:            "127.0.0.1",
		MBits:         50,
		ReservedPorts: []structs.Port{{Label: "http", Value: 3}},
	}
	alloc.AllocatedResources.Tasks["web"].Networks[0] = netResource

	task.Services = []*structs.Service{
		{
			Name:      "httpd",
			PortLabel: "http",
			Tags:      []string{"nomad", "test", "http"},
			Checks: []*structs.ServiceCheck{
				{
					Name:     "httpd-http-check",
					Type:     "http",
					Path:     "/",
					Protocol: "http",
					Interval: 9000 * time.Hour,
					Timeout:  1, // fail as fast as possible
				},
				{
					Name:     "httpd-script-check",
					Type:     "script",
					Command:  "/bin/true",
					Interval: 10 * time.Second,
					Timeout:  10 * time.Second,
				},
			},
		},
		{
			Name:      "httpd2",
			PortLabel: "http",
			Tags: []string{
				"test",
				// Use URL-unfriendly tags to test #3620
				"public-test.ettaviation.com:80/ redirect=302,https://test.ettaviation.com",
				"public-test.ettaviation.com:443/",
			},
		},
	}

	logger := testlog.HCLogger(t)
	logUpdate := &mockUpdater{logger}
	allocDir := allocdir.NewAllocDir(logger, filepath.Join(conf.AllocDir, alloc.ID))
	if err := allocDir.Build(); err != nil {
		t.Fatalf("error building alloc dir: %v", err)
	}
	taskDir := allocDir.NewTaskDir(task.Name)
	vclient := vaultclient.NewMockVaultClient()
	consulClient, err := consulapi.NewClient(consulConfig)
	require.Nil(err)

	serviceClient := consul.NewServiceClient(consulClient.Agent(), testlog.HCLogger(t), true)
	defer serviceClient.Shutdown() // just-in-case cleanup
	consulRan := make(chan struct{})
	go func() {
		serviceClient.Run()
		close(consulRan)
	}()

	// Create a closed channel to mock TaskHookCoordinator.startConditionForTask.
	// Closed channel indicates this task is not blocked on prestart hooks.
	closedCh := make(chan struct{})
	close(closedCh)

	// Build the config
	config := &taskrunner.Config{
		Alloc:                alloc,
		ClientConfig:         conf,
		Consul:               serviceClient,
		Task:                 task,
		TaskDir:              taskDir,
		Logger:               logger,
		Vault:                vclient,
		StateDB:              state.NoopDB{},
		StateUpdater:         logUpdate,
		DeviceManager:        devicemanager.NoopMockManager(),
		DriverManager:        drivermanager.TestDriverManager(t),
		StartConditionMetCtx: closedCh,
	}

	tr, err := taskrunner.NewTaskRunner(config)
	require.NoError(err)
	go tr.Run()
	defer func() {
		// Make sure we always shutdown task runner when the test exits
		select {
		case <-tr.WaitCh():
			// Exited cleanly, no need to kill
		default:
			tr.Kill(context.Background(), &structs.TaskEvent{}) // just in case
		}
	}()

	// Block waiting for the service to appear
	catalog := consulClient.Catalog()
	res, meta, err := catalog.Service("httpd2", "test", nil)
	require.Nil(err)

	for i := 0; len(res) == 0 && i < 10; i++ {
		//Expected initial request to fail, do a blocking query
		res, meta, err = catalog.Service("httpd2", "test", &consulapi.QueryOptions{WaitIndex: meta.LastIndex + 1, WaitTime: 3 * time.Second})
		if err != nil {
			t.Fatalf("error querying for service: %v", err)
		}
	}
	require.Len(res, 1)

	// Truncate results
	res = res[:]

	// Assert the service with the checks exists
	for i := 0; len(res) == 0 && i < 10; i++ {
		res, meta, err = catalog.Service("httpd", "http", &consulapi.QueryOptions{WaitIndex: meta.LastIndex + 1, WaitTime: 3 * time.Second})
		require.Nil(err)
	}
	require.Len(res, 1)

	// Assert the script check passes (mock_driver script checks always
	// pass) after having time to run once
	time.Sleep(2 * time.Second)
	checks, _, err := consulClient.Health().Checks("httpd", nil)
	require.Nil(err)
	require.Len(checks, 2)

	for _, check := range checks {
		if expected := "httpd"; check.ServiceName != expected {
			t.Fatalf("expected checks to be for %q but found service name = %q", expected, check.ServiceName)
		}
		switch check.Name {
		case "httpd-http-check":
			// Port check should fail
			if expected := consulapi.HealthCritical; check.Status != expected {
				t.Errorf("expected %q status to be %q but found %q", check.Name, expected, check.Status)
			}
		case "httpd-script-check":
			// mock_driver script checks always succeed
			if expected := consulapi.HealthPassing; check.Status != expected {
				t.Errorf("expected %q status to be %q but found %q", check.Name, expected, check.Status)
			}
		default:
			t.Errorf("unexpected check %q with status %q", check.Name, check.Status)
		}
	}

	// Assert the service client returns all the checks for the allocation.
	reg, err := serviceClient.AllocRegistrations(alloc.ID)
	if err != nil {
		t.Fatalf("unexpected error retrieving allocation checks: %v", err)
	}
	if reg == nil {
		t.Fatalf("Unexpected nil allocation registration")
	}
	if snum := reg.NumServices(); snum != 2 {
		t.Fatalf("Unexpected number of services registered. Got %d; want 2", snum)
	}
	if cnum := reg.NumChecks(); cnum != 2 {
		t.Fatalf("Unexpected number of checks registered. Got %d; want 2", cnum)
	}

	logger.Debug("killing task")

	// Kill the task
	tr.Kill(context.Background(), &structs.TaskEvent{})

	select {
	case <-tr.WaitCh():
	case <-time.After(10 * time.Second):
		t.Fatalf("timed out waiting for Run() to exit")
	}

	// Shutdown Consul ServiceClient to ensure all pending operations complete
	if err := serviceClient.Shutdown(); err != nil {
		t.Errorf("error shutting down Consul ServiceClient: %v", err)
	}

	// Ensure Consul is clean
	services, _, err := catalog.Services(nil)
	require.Nil(err)
	require.Len(services, 1)
	require.Contains(services, "consul")
}
<|endoftext|>


<commit_before>package bytecode

import (
	"fmt"
	"strings"
)

// instruction set types
const (
	MethodDef = "Def"
	ClassDef  = "DefClass"
	Block     = "Block"
	Program   = "ProgramStart"
)

// instruction actions
const (
	GetLocal uint8 = iota
	GetConstant
	GetInstanceVariable
	SetLocal
	SetConstant
	SetInstanceVariable
	PutBoolean
	PutString
	PutFloat
	PutSelf
	PutObject
	PutNull
	NewArray
	ExpandArray
	SplatArray
	NewHash
	NewRange
	BranchUnless
	BranchIf
	Jump
	Break
	DefMethod
	DefSingletonMethod
	DefClass
	Send
	InvokeBlock
	GetBlock
	Pop
	Dup
	Leave
	InstructionCount
)

// InstructionNameTable is the table the maps instruction's op code with its readable name
var InstructionNameTable = []string{
	GetLocal:            "getlocal",
	GetConstant:         "getconstant",
	GetInstanceVariable: "getinstancevariable",
	SetLocal:            "setlocal",
	SetConstant:         "setconstant",
	SetInstanceVariable: "setinstancevariable",
	PutBoolean:          "putboolean",
	PutString:           "putstring",
	PutFloat:            "putfloat",
	PutSelf:             "putself",
	PutObject:           "putobject",
	PutNull:             "putnil",
	NewArray:            "newarray",
	ExpandArray:         "expand_array",
	SplatArray:          "splat_array",
	NewHash:             "newhash",
	NewRange:            "newrange",
	BranchUnless:        "branchunless",
	BranchIf:            "branchif",
	Jump:                "jump",
	Break:               "break",
	DefMethod:           "def_method",
	DefSingletonMethod:  "def_singleton_method",
	DefClass:            "def_class",
	Send:                "send",
	InvokeBlock:         "invokeblock",
	GetBlock:            "getblock",
	Pop:                 "pop",
	Dup:                 "dup",
	Leave:               "leave",
}

// Instruction represents compiled bytecode instruction
type Instruction struct {
	Opcode     uint8
	Params     []interface{}
	line       int
	anchor     *anchor
	sourceLine int
}

// Inspect is for inspecting the instruction's content
func (i *Instruction) Inspect() string {
	var params []string

	for _, param := range i.Params {
		params = append(params, fmt.Sprint(param))
	}
	return fmt.Sprintf("%s: %s. source line: %d", i.ActionName(), strings.Join(params, ", "), i.sourceLine)
}

// ActionName returns the human readable name of the instruction
func (i *Instruction) ActionName() string {
	return InstructionNameTable[i.Opcode]
}

// AnchorLine returns instruction anchor's line number if it has an anchor
func (i *Instruction) AnchorLine() int {
	if i.anchor != nil {
		return i.anchor.line
	}

	panic("you are calling AnchorLine on an instruction without anchors")
}

// Line returns instruction's line number
func (i *Instruction) Line() int {
	return i.line
}

// SourceLine returns instruction's source line number
// TODO: needs to change the func to simple public variable
func (i *Instruction) SourceLine() int {
	return i.sourceLine
}

type anchor struct {
	line int
}

// InstructionSet contains a set of Instructions and some metadata
type InstructionSet struct {
	name         string
	isType       string
	Instructions []*Instruction
	count        int
	argTypes     *ArgSet
}

// ArgSet stores the metadata of a method definition's parameters.
type ArgSet struct {
	names []string
	types []uint8
}

// Types are the getter method of *ArgSet's types attribute
// TODO: needs to change the func to simple public variable
func (as *ArgSet) Types() []uint8 {
	return as.types
}

// Names are the getter method of *ArgSet's names attribute
// TODO: needs to change the func to simple public variable
func (as *ArgSet) Names() []string {
	return as.names
}

func (as *ArgSet) FindIndex(name string) int {
	for i, n := range as.names {
		if n == name {
			return i
		}
	}

	return -1
}

func (as *ArgSet) setArg(index int, name string, argType uint8) {
	as.names[index] = name
	as.types[index] = argType
}

// ArgTypes returns enums that represents each argument's type
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) ArgTypes() *ArgSet {
	return is.argTypes
}

// Name returns instruction set's name
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) Name() string {
	return is.name
}

// SetType returns instruction's type
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) Type() string {
	return is.isType
}

func (is *InstructionSet) define(action uint8, sourceLine int, params ...interface{}) *Instruction {
	i := &Instruction{Opcode: action, Params: params, line: is.count, sourceLine: sourceLine + 1}
	for _, param := range params {
		a, ok := param.(*anchor)

		if ok {
			i.anchor = a
		}
	}

	is.Instructions = append(is.Instructions, i)
	is.count++
	return i
}
<commit_msg>Remove unnecessary findIndex()<commit_after>package bytecode

import (
	"fmt"
	"strings"
)

// instruction set types
const (
	MethodDef = "Def"
	ClassDef  = "DefClass"
	Block     = "Block"
	Program   = "ProgramStart"
)

// instruction actions
const (
	GetLocal uint8 = iota
	GetConstant
	GetInstanceVariable
	SetLocal
	SetConstant
	SetInstanceVariable
	PutBoolean
	PutString
	PutFloat
	PutSelf
	PutObject
	PutNull
	NewArray
	ExpandArray
	SplatArray
	NewHash
	NewRange
	BranchUnless
	BranchIf
	Jump
	Break
	DefMethod
	DefSingletonMethod
	DefClass
	Send
	InvokeBlock
	GetBlock
	Pop
	Dup
	Leave
	InstructionCount
)

// InstructionNameTable is the table the maps instruction's op code with its readable name
var InstructionNameTable = []string{
	GetLocal:            "getlocal",
	GetConstant:         "getconstant",
	GetInstanceVariable: "getinstancevariable",
	SetLocal:            "setlocal",
	SetConstant:         "setconstant",
	SetInstanceVariable: "setinstancevariable",
	PutBoolean:          "putboolean",
	PutString:           "putstring",
	PutFloat:            "putfloat",
	PutSelf:             "putself",
	PutObject:           "putobject",
	PutNull:             "putnil",
	NewArray:            "newarray",
	ExpandArray:         "expand_array",
	SplatArray:          "splat_array",
	NewHash:             "newhash",
	NewRange:            "newrange",
	BranchUnless:        "branchunless",
	BranchIf:            "branchif",
	Jump:                "jump",
	Break:               "break",
	DefMethod:           "def_method",
	DefSingletonMethod:  "def_singleton_method",
	DefClass:            "def_class",
	Send:                "send",
	InvokeBlock:         "invokeblock",
	GetBlock:            "getblock",
	Pop:                 "pop",
	Dup:                 "dup",
	Leave:               "leave",
}

// Instruction represents compiled bytecode instruction
type Instruction struct {
	Opcode     uint8
	Params     []interface{}
	line       int
	anchor     *anchor
	sourceLine int
}

// Inspect is for inspecting the instruction's content
func (i *Instruction) Inspect() string {
	var params []string

	for _, param := range i.Params {
		params = append(params, fmt.Sprint(param))
	}
	return fmt.Sprintf("%s: %s. source line: %d", i.ActionName(), strings.Join(params, ", "), i.sourceLine)
}

// ActionName returns the human readable name of the instruction
func (i *Instruction) ActionName() string {
	return InstructionNameTable[i.Opcode]
}

// AnchorLine returns instruction anchor's line number if it has an anchor
func (i *Instruction) AnchorLine() int {
	if i.anchor != nil {
		return i.anchor.line
	}

	panic("you are calling AnchorLine on an instruction without anchors")
}

// Line returns instruction's line number
func (i *Instruction) Line() int {
	return i.line
}

// SourceLine returns instruction's source line number
// TODO: needs to change the func to simple public variable
func (i *Instruction) SourceLine() int {
	return i.sourceLine
}

type anchor struct {
	line int
}

// InstructionSet contains a set of Instructions and some metadata
type InstructionSet struct {
	name         string
	isType       string
	Instructions []*Instruction
	count        int
	argTypes     *ArgSet
}

// ArgSet stores the metadata of a method definition's parameters.
type ArgSet struct {
	names []string
	types []uint8
}

// Types are the getter method of *ArgSet's types attribute
// TODO: needs to change the func to simple public variable
func (as *ArgSet) Types() []uint8 {
	return as.types
}

// Names are the getter method of *ArgSet's names attribute
// TODO: needs to change the func to simple public variable
func (as *ArgSet) Names() []string {
	return as.names
}

func (as *ArgSet) setArg(index int, name string, argType uint8) {
	as.names[index] = name
	as.types[index] = argType
}

// ArgTypes returns enums that represents each argument's type
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) ArgTypes() *ArgSet {
	return is.argTypes
}

// Name returns instruction set's name
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) Name() string {
	return is.name
}

// SetType returns instruction's type
// TODO: needs to change the func to simple public variable
func (is *InstructionSet) Type() string {
	return is.isType
}

func (is *InstructionSet) define(action uint8, sourceLine int, params ...interface{}) *Instruction {
	i := &Instruction{Opcode: action, Params: params, line: is.count, sourceLine: sourceLine + 1}
	for _, param := range params {
		a, ok := param.(*anchor)

		if ok {
			i.anchor = a
		}
	}

	is.Instructions = append(is.Instructions, i)
	is.count++
	return i
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Uber Technologies, Inc.
//
// 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 temporal

import (
	"fmt"
	"math"
	"time"

	"github.com/m3db/m3/src/query/executor/transform"
	"github.com/m3db/m3/src/query/ts"
)

const (
	// PredictLinearType predicts the value of time series t seconds from now, based on the input series, using simple linear regression.
	// PredictLinearType should only be used with gauges.
	PredictLinearType = "predict_linear"

	// DerivType calculates the per-second derivative of the time series, using simple linear regression.
	// DerivType should only be used with gauges.
	DerivType = "deriv"
)

type linearRegressionProcessor struct {
	fn      linearRegFn
	isDeriv bool
}

func (l linearRegressionProcessor) Init(op baseOp, controller *transform.Controller, opts transform.Options) Processor {
	return &linearRegressionNode{
		op:         op,
		controller: controller,
		timeSpec:   opts.TimeSpec,
		fn:         l.fn,
		isDeriv:    l.isDeriv,
	}
}

type linearRegFn func(float64, float64) float64

// NewLinearRegressionOp creates a new base temporal transform for linear regression functions
func NewLinearRegressionOp(args []interface{}, optype string) (transform.Params, error) {
	var (
		fn      linearRegFn
		isDeriv bool
	)

	switch optype {
	case PredictLinearType:
		if len(args) != 2 {
			return emptyOp, fmt.Errorf("invalid number of args for %s: %d", PredictLinearType, len(args))
		}

		duration, ok := args[1].(float64)
		if !ok {
			return emptyOp, fmt.Errorf("unable to cast to scalar argument: %v for %s", args[1], PredictLinearType)
		}

		fn = func(slope, intercept float64) float64 {
			return slope*duration + intercept
		}

	case DerivType:
		fn = func(slope, _ float64) float64 {
			return slope
		}

		isDeriv = true

	default:
		return nil, fmt.Errorf("unknown linear regression type: %s", optype)
	}

	l := linearRegressionProcessor{
		fn:      fn,
		isDeriv: isDeriv,
	}

	return newBaseOp(args, optype, l)
}

type linearRegressionNode struct {
	op         baseOp
	controller *transform.Controller
	timeSpec   transform.TimeSpec
	fn         linearRegFn
	isDeriv    bool
}

func (l linearRegressionNode) Process(dps ts.Datapoints, evaluationTime time.Time) float64 {
	if dps.Len() < 2 {
		return math.NaN()
	}

	slope, intercept := linearRegression(dps, evaluationTime, l.isDeriv)
	return l.fn(slope, intercept)
}

// linearRegression performs a least-square linear regression analysis on the
// provided datapoints. It returns the slope, and the intercept value at the
// provided time. The algorithm we use comes from https://en.wikipedia.org/wiki/Simple_linear_regression.
func linearRegression(dps ts.Datapoints, interceptTime time.Time, isDeriv bool) (float64, float64) {
	var (
		n                                   float64
		sumTimeDiff, sumVals                float64
		sumTimeDiffVals, sumTimeDiffSquared float64
		valueCount                          int
	)

	for _, dp := range dps {
		if math.IsNaN(dp.Value) {
			continue
		}

		if valueCount == 0 && isDeriv {
			// set interceptTime as timestamp of first non-NaN dp
			interceptTime = dp.Timestamp
		}

		valueCount++
		timeDiff := float64(dp.Timestamp.Sub(interceptTime).Seconds())
		n += 1.0
		sumVals += dp.Value
		sumTimeDiff += timeDiff
		sumTimeDiffVals += timeDiff * dp.Value
		sumTimeDiffSquared += timeDiff * timeDiff
	}

	// need at least 2 non-NaN values to calculate slope and intercept
	if valueCount == 1 {
		return math.NaN(), math.NaN()
	}

	covXY := sumTimeDiffVals - sumTimeDiff*sumVals/n
	varX := sumTimeDiffSquared - sumTimeDiff*sumTimeDiff/n

	slope := covXY / varX
	intercept := sumVals/n - slope*sumTimeDiff/n

	return slope, intercept
}
<commit_msg>Fix lint issue (#1085)<commit_after>// Copyright (c) 2018 Uber Technologies, Inc.
//
// 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 temporal

import (
	"fmt"
	"math"
	"time"

	"github.com/m3db/m3/src/query/executor/transform"
	"github.com/m3db/m3/src/query/ts"
)

const (
	// PredictLinearType predicts the value of time series t seconds from now, based on the input series, using simple linear regression.
	// PredictLinearType should only be used with gauges.
	PredictLinearType = "predict_linear"

	// DerivType calculates the per-second derivative of the time series, using simple linear regression.
	// DerivType should only be used with gauges.
	DerivType = "deriv"
)

type linearRegressionProcessor struct {
	fn      linearRegFn
	isDeriv bool
}

func (l linearRegressionProcessor) Init(op baseOp, controller *transform.Controller, opts transform.Options) Processor {
	return &linearRegressionNode{
		op:         op,
		controller: controller,
		timeSpec:   opts.TimeSpec,
		fn:         l.fn,
		isDeriv:    l.isDeriv,
	}
}

type linearRegFn func(float64, float64) float64

// NewLinearRegressionOp creates a new base temporal transform for linear regression functions
func NewLinearRegressionOp(args []interface{}, optype string) (transform.Params, error) {
	var (
		fn      linearRegFn
		isDeriv bool
	)

	switch optype {
	case PredictLinearType:
		if len(args) != 2 {
			return emptyOp, fmt.Errorf("invalid number of args for %s: %d", PredictLinearType, len(args))
		}

		duration, ok := args[1].(float64)
		if !ok {
			return emptyOp, fmt.Errorf("unable to cast to scalar argument: %v for %s", args[1], PredictLinearType)
		}

		fn = func(slope, intercept float64) float64 {
			return slope*duration + intercept
		}

	case DerivType:
		fn = func(slope, _ float64) float64 {
			return slope
		}

		isDeriv = true

	default:
		return nil, fmt.Errorf("unknown linear regression type: %s", optype)
	}

	l := linearRegressionProcessor{
		fn:      fn,
		isDeriv: isDeriv,
	}

	return newBaseOp(args, optype, l)
}

type linearRegressionNode struct {
	op         baseOp
	controller *transform.Controller
	timeSpec   transform.TimeSpec
	fn         linearRegFn
	isDeriv    bool
}

func (l linearRegressionNode) Process(dps ts.Datapoints, evaluationTime time.Time) float64 {
	if dps.Len() < 2 {
		return math.NaN()
	}

	slope, intercept := linearRegression(dps, evaluationTime, l.isDeriv)
	return l.fn(slope, intercept)
}

// linearRegression performs a least-square linear regression analysis on the
// provided datapoints. It returns the slope, and the intercept value at the
// provided time. The algorithm we use comes from https://en.wikipedia.org/wiki/Simple_linear_regression.
func linearRegression(dps ts.Datapoints, interceptTime time.Time, isDeriv bool) (float64, float64) {
	var (
		n                                   float64
		sumTimeDiff, sumVals                float64
		sumTimeDiffVals, sumTimeDiffSquared float64
		valueCount                          int
	)

	for _, dp := range dps {
		if math.IsNaN(dp.Value) {
			continue
		}

		if valueCount == 0 && isDeriv {
			// set interceptTime as timestamp of first non-NaN dp
			interceptTime = dp.Timestamp
		}

		valueCount++
		timeDiff := dp.Timestamp.Sub(interceptTime).Seconds()
		n += 1.0
		sumVals += dp.Value
		sumTimeDiff += timeDiff
		sumTimeDiffVals += timeDiff * dp.Value
		sumTimeDiffSquared += timeDiff * timeDiff
	}

	// need at least 2 non-NaN values to calculate slope and intercept
	if valueCount == 1 {
		return math.NaN(), math.NaN()
	}

	covXY := sumTimeDiffVals - sumTimeDiff*sumVals/n
	varX := sumTimeDiffSquared - sumTimeDiff*sumTimeDiff/n

	slope := covXY / varX
	intercept := sumVals/n - slope*sumTimeDiff/n

	return slope, intercept
}
<|endoftext|>


<commit_before>// +build btcd

package lntest

import (
	"encoding/hex"
	"fmt"
	"os"

	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/integration/rpctest"
	"github.com/btcsuite/btcd/rpcclient"
)

// logDir is the name of the temporary log directory.
const logDir = "./.backendlogs"

// perm is used to signal we want to establish a permanent connection using the
// btcd Node API.
//
// NOTE: Cannot be const, since the node API expects a reference.
var perm = "perm"

// BtcdBackendConfig is an implementation of the BackendConfig interface
// backed by a btcd node.
type BtcdBackendConfig struct {
	// rpcConfig houses the connection config to the backing btcd instance.
	rpcConfig rpcclient.ConnConfig

	// harness is the backing btcd instance.
	harness *rpctest.Harness

	// minerAddr is the p2p address of the miner to connect to.
	minerAddr string
}

// A compile time assertion to ensure BtcdBackendConfig meets the BackendConfig
// interface.
var _ BackendConfig = (*BtcdBackendConfig)(nil)

// GenArgs returns the arguments needed to be passed to LND at startup for
// using this node as a chain backend.
func (b BtcdBackendConfig) GenArgs() []string {
	var args []string
	encodedCert := hex.EncodeToString(b.rpcConfig.Certificates)
	args = append(args, "--bitcoin.node=btcd")
	args = append(args, fmt.Sprintf("--btcd.rpchost=%v", b.rpcConfig.Host))
	args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", b.rpcConfig.User))
	args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", b.rpcConfig.Pass))
	args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert))

	return args
}

// ConnectMiner is called to establish a connection to the test miner.
func (b BtcdBackendConfig) ConnectMiner() error {
	return b.harness.Node.Node(btcjson.NConnect, b.minerAddr, &perm)
}

// DisconnectMiner is called to disconnect the miner.
func (b BtcdBackendConfig) DisconnectMiner() error {
	return b.harness.Node.Node(btcjson.NRemove, b.minerAddr, &perm)
}

// Name returns the name of the backend type.
func (b BtcdBackendConfig) Name() string {
	return "btcd"
}

// NewBackend starts a new rpctest.Harness and returns a BtcdBackendConfig for
// that node. miner should be set to the P2P address of the miner to connect
// to.
func NewBackend(miner string, netParams *chaincfg.Params) (
	*BtcdBackendConfig, func(), error) {

	args := []string{
		"--rejectnonstd",
		"--txindex",
		"--trickleinterval=100ms",
		"--debuglevel=debug",
		"--logdir=" + logDir,
		"--connect=" + miner,
		"--nowinservice",
	}
	chainBackend, err := rpctest.New(netParams, nil, args)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create btcd node: %v", err)
	}

	if err := chainBackend.SetUp(false, 0); err != nil {
		return nil, nil, fmt.Errorf("unable to set up btcd backend: %v", err)
	}

	bd := &BtcdBackendConfig{
		rpcConfig: chainBackend.RPCConfig(),
		harness:   chainBackend,
		minerAddr: miner,
	}

	cleanUp := func() {
		chainBackend.TearDown()

		// After shutting down the chain backend, we'll make a copy of
		// the log file before deleting the temporary log dir.
		logFile := logDir + "/" + netParams.Name + "/btcd.log"
		err := CopyFile("./output_btcd_chainbackend.log", logFile)
		if err != nil {
			fmt.Printf("unable to copy file: %v\n", err)
		}
		if err = os.RemoveAll(logDir); err != nil {
			fmt.Printf("Cannot remove dir %s: %v\n", logDir, err)
		}
	}

	return bd, cleanUp, nil
}
<commit_msg>lntest: default to btcd as default test harness backend<commit_after>// +build !bitcoind,!neutrino

package lntest

import (
	"encoding/hex"
	"fmt"
	"os"

	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/integration/rpctest"
	"github.com/btcsuite/btcd/rpcclient"
)

// logDir is the name of the temporary log directory.
const logDir = "./.backendlogs"

// perm is used to signal we want to establish a permanent connection using the
// btcd Node API.
//
// NOTE: Cannot be const, since the node API expects a reference.
var perm = "perm"

// BtcdBackendConfig is an implementation of the BackendConfig interface
// backed by a btcd node.
type BtcdBackendConfig struct {
	// rpcConfig houses the connection config to the backing btcd instance.
	rpcConfig rpcclient.ConnConfig

	// harness is the backing btcd instance.
	harness *rpctest.Harness

	// minerAddr is the p2p address of the miner to connect to.
	minerAddr string
}

// A compile time assertion to ensure BtcdBackendConfig meets the BackendConfig
// interface.
var _ BackendConfig = (*BtcdBackendConfig)(nil)

// GenArgs returns the arguments needed to be passed to LND at startup for
// using this node as a chain backend.
func (b BtcdBackendConfig) GenArgs() []string {
	var args []string
	encodedCert := hex.EncodeToString(b.rpcConfig.Certificates)
	args = append(args, "--bitcoin.node=btcd")
	args = append(args, fmt.Sprintf("--btcd.rpchost=%v", b.rpcConfig.Host))
	args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", b.rpcConfig.User))
	args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", b.rpcConfig.Pass))
	args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert))

	return args
}

// ConnectMiner is called to establish a connection to the test miner.
func (b BtcdBackendConfig) ConnectMiner() error {
	return b.harness.Node.Node(btcjson.NConnect, b.minerAddr, &perm)
}

// DisconnectMiner is called to disconnect the miner.
func (b BtcdBackendConfig) DisconnectMiner() error {
	return b.harness.Node.Node(btcjson.NRemove, b.minerAddr, &perm)
}

// Name returns the name of the backend type.
func (b BtcdBackendConfig) Name() string {
	return "btcd"
}

// NewBackend starts a new rpctest.Harness and returns a BtcdBackendConfig for
// that node. miner should be set to the P2P address of the miner to connect
// to.
func NewBackend(miner string, netParams *chaincfg.Params) (
	*BtcdBackendConfig, func(), error) {

	args := []string{
		"--rejectnonstd",
		"--txindex",
		"--trickleinterval=100ms",
		"--debuglevel=debug",
		"--logdir=" + logDir,
		"--connect=" + miner,
		"--nowinservice",
	}
	chainBackend, err := rpctest.New(netParams, nil, args)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create btcd node: %v", err)
	}

	if err := chainBackend.SetUp(false, 0); err != nil {
		return nil, nil, fmt.Errorf("unable to set up btcd backend: %v", err)
	}

	bd := &BtcdBackendConfig{
		rpcConfig: chainBackend.RPCConfig(),
		harness:   chainBackend,
		minerAddr: miner,
	}

	cleanUp := func() {
		chainBackend.TearDown()

		// After shutting down the chain backend, we'll make a copy of
		// the log file before deleting the temporary log dir.
		logFile := logDir + "/" + netParams.Name + "/btcd.log"
		err := CopyFile("./output_btcd_chainbackend.log", logFile)
		if err != nil {
			fmt.Printf("unable to copy file: %v\n", err)
		}
		if err = os.RemoveAll(logDir); err != nil {
			fmt.Printf("Cannot remove dir %s: %v\n", logDir, err)
		}
	}

	return bd, cleanUp, nil
}
<|endoftext|>


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

package api

import (
	"encoding/json"
	"fmt"
	"github.com/bitmark-inc/bitmark-webgui/configuration"
	"github.com/bitmark-inc/bitmark-webgui/structs"
	bitmarkdConfig "github.com/bitmark-inc/bitmarkd/configuration"
	"github.com/bitmark-inc/bitmarkd/rpc"
	"github.com/bitmark-inc/logger"
	"net/http"
	"net/rpc/jsonrpc"
	"os"
	"path/filepath"
	"time"
)

type bitmarkdRequest struct {
	Option  string `json:"option"`
	Network string `json:"network"`
	// ConfigFile string `json:"config_file"`
}

// POST /api/bitmarkd
func Bitmarkd(w http.ResponseWriter, req *http.Request, webguiFilePath string, webguiConfig *configuration.Configuration, log *logger.L) {

	log.Info("POST /api/bitmarkd")
	response := &Response{
		Ok:     false,
		Result: nil,
	}

	decoder := json.NewDecoder(req.Body)
	var request bitmarkdRequest
	if err := decoder.Decode(&request); nil != err {
		log.Errorf("Error: %v", err)
		response.Result = err
		if err := writeApiResponseAndSetCookie(w, response); nil != err {
			log.Errorf("Error: %v", err)
		}
		return
	}

	log.Infof("bitmarkd option: %s", request.Option)

	apiErr := invalidValueErr
	switch request.Option {
	case `start`:
		// Check if bitmarkd is running
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStartErr
		} else {
			bitmarkService.ModeStart <- true
			// wait one second to get correct result
			time.Sleep(time.Second * 1)
			if !bitmarkService.IsRunning() {
				response.Result = bitmarkdStartErr
			} else {
				response.Ok = true
				response.Result = bitmarkdStartSuccess
			}
		}
	case `stop`:
		if !bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStopErr
		} else {
			bitmarkService.ModeStart <- false
			time.Sleep(time.Second * 1)
			if bitmarkService.IsRunning() {
				response.Result = bitmarkdStopErr
			} else {
				response.Ok = true
				response.Result = bitmarkdStopSuccess
			}

		}
	case `status`:
		response.Ok = true
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdStarted
		} else {
			response.Result = bitmarkdStopped
		}
	case `info`:
		if !bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStopErr
		} else {
			if info, err := getBitmarkdInfo(webguiConfig.BitmarkConfigFile, log); "" != err {
				response.Result = err
			} else {
				response.Ok = true
				response.Result = info
			}
		}
	case `setup`:
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStartErr
		} else {
			bitmarkConfigFile := filepath.Join(webguiConfig.DataDirectory, fmt.Sprintf("bitmarkd-%s", request.Network), "bitmarkd.conf")
			if err := bitmarkService.Setup(bitmarkConfigFile, request.Network, webguiFilePath, webguiConfig); nil != err {
				if os.IsNotExist(err) {
					response.Result = "bitmarkd config not found"
				} else {
					response.Result = err.Error()
				}
			} else {
				response.Ok = true
				response.Result = nil
			}
		}
	default:
		response.Result = apiErr
		if err := writeApiResponseAndSetCookie(w, response); nil != err {
			log.Errorf("Error: %v", err)
		}
		return
	}

	if err := writeApiResponseAndSetCookie(w, response); nil != err {
		log.Errorf("Error: %v", err)
	}
}

func getBitmarkdInfo(bitmarkConfigFile string, log *logger.L) (*rpc.InfoReply, string) {
	bitmarkConfig := &structs.BitmarkdConfiguration{}
	err := bitmarkdConfig.ParseConfigurationFile(bitmarkConfigFile, bitmarkConfig)
	if nil != err {
		log.Errorf("Failed to get bitmarkd configuration: %v", err)
		return nil, bitmarkdGetConfigErr
	}

	conn, err := bitmarkService.Connect(bitmarkConfig.ClientRPC.Listen[0])
	if nil != err {
		log.Errorf("Failed to connect to bitmarkd: %v", err)
		return nil, bitmarkdConnectErr
	}
	defer conn.Close()

	// create a client
	client := jsonrpc.NewClient(conn)
	defer client.Close()

	info, err := bitmarkService.GetInfo(client)
	if nil != err {
		log.Errorf("Failed to get bitmark info: %v", err)
		return nil, bitmarkdGetInfoErr
	}

	return info, ""
}
<commit_msg>Return error if no bitmarkd client is set in configuration file<commit_after>// Copyright (c) 2014-2016 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package api

import (
	"encoding/json"
	"fmt"
	"github.com/bitmark-inc/bitmark-webgui/configuration"
	"github.com/bitmark-inc/bitmark-webgui/structs"
	bitmarkdConfig "github.com/bitmark-inc/bitmarkd/configuration"
	"github.com/bitmark-inc/bitmarkd/rpc"
	"github.com/bitmark-inc/logger"
	"net/http"
	"net/rpc/jsonrpc"
	"os"
	"path/filepath"
	"time"
)

type bitmarkdRequest struct {
	Option  string `json:"option"`
	Network string `json:"network"`
	// ConfigFile string `json:"config_file"`
}

// POST /api/bitmarkd
func Bitmarkd(w http.ResponseWriter, req *http.Request, webguiFilePath string, webguiConfig *configuration.Configuration, log *logger.L) {

	log.Info("POST /api/bitmarkd")
	response := &Response{
		Ok:     false,
		Result: nil,
	}

	decoder := json.NewDecoder(req.Body)
	var request bitmarkdRequest
	if err := decoder.Decode(&request); nil != err {
		log.Errorf("Error: %v", err)
		response.Result = err
		if err := writeApiResponseAndSetCookie(w, response); nil != err {
			log.Errorf("Error: %v", err)
		}
		return
	}

	log.Infof("bitmarkd option: %s", request.Option)

	apiErr := invalidValueErr
	switch request.Option {
	case `start`:
		// Check if bitmarkd is running
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStartErr
		} else {
			bitmarkService.ModeStart <- true
			// wait one second to get correct result
			time.Sleep(time.Second * 1)
			if !bitmarkService.IsRunning() {
				response.Result = bitmarkdStartErr
			} else {
				response.Ok = true
				response.Result = bitmarkdStartSuccess
			}
		}
	case `stop`:
		if !bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStopErr
		} else {
			bitmarkService.ModeStart <- false
			time.Sleep(time.Second * 1)
			if bitmarkService.IsRunning() {
				response.Result = bitmarkdStopErr
			} else {
				response.Ok = true
				response.Result = bitmarkdStopSuccess
			}

		}
	case `status`:
		response.Ok = true
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdStarted
		} else {
			response.Result = bitmarkdStopped
		}
	case `info`:
		if !bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStopErr
		} else {
			if info, err := getBitmarkdInfo(webguiConfig.BitmarkConfigFile, log); "" != err {
				response.Result = err
			} else {
				response.Ok = true
				response.Result = info
			}
		}
	case `setup`:
		if bitmarkService.IsRunning() {
			response.Result = bitmarkdAlreadyStartErr
		} else {
			bitmarkConfigFile := filepath.Join(webguiConfig.DataDirectory, fmt.Sprintf("bitmarkd-%s", request.Network), "bitmarkd.conf")
			if err := bitmarkService.Setup(bitmarkConfigFile, request.Network, webguiFilePath, webguiConfig); nil != err {
				if os.IsNotExist(err) {
					response.Result = "bitmarkd config not found"
				} else {
					response.Result = err.Error()
				}
			} else {
				response.Ok = true
				response.Result = nil
			}
		}
	default:
		response.Result = apiErr
		if err := writeApiResponseAndSetCookie(w, response); nil != err {
			log.Errorf("Error: %v", err)
		}
		return
	}

	if err := writeApiResponseAndSetCookie(w, response); nil != err {
		log.Errorf("Error: %v", err)
	}
}

func getBitmarkdInfo(bitmarkConfigFile string, log *logger.L) (*rpc.InfoReply, string) {
	bitmarkConfig := &structs.BitmarkdConfiguration{}
	err := bitmarkdConfig.ParseConfigurationFile(bitmarkConfigFile, bitmarkConfig)
	if nil != err {
		log.Errorf("Failed to get bitmarkd configuration: %v", err)
		return nil, bitmarkdGetConfigErr
	}

	if len(bitmarkConfig.ClientRPC.Listen) == 0 {
		log.Errorf("No listensing port in bitmarkd configuration.")
		return nil, bitmarkdConnectErr
	}

	conn, err := bitmarkService.Connect(bitmarkConfig.ClientRPC.Listen[0])
	if nil != err {
		log.Errorf("Failed to connect to bitmarkd: %v", err)
		return nil, bitmarkdConnectErr
	}
	defer conn.Close()

	// create a client
	client := jsonrpc.NewClient(conn)
	defer client.Close()

	info, err := bitmarkService.GetInfo(client)
	if nil != err {
		log.Errorf("Failed to get bitmark info: %v", err)
		return nil, bitmarkdGetInfoErr
	}

	return info, ""
}
<|endoftext|>


<commit_before>package cbfsclient

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"
)

type FetchCallback func(oid string, r io.Reader) error

type blobInfo struct {
	Nodes map[string]time.Time
}

func (c Client) getBlobInfos(oids ...string) (map[string]blobInfo, error) {
	u := c.URLFor("/.cbfs/blob/info/")
	form := url.Values{"blob": oids}
	res, err := http.PostForm(u, form)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error fetching blob info: %v",
			res.Status)
	}

	d := json.NewDecoder(res.Body)
	rv := map[string]blobInfo{}
	err = d.Decode(&rv)
	return rv, err
}

type fetchWork struct {
	oid string
	bi  blobInfo
}

type brokenReader struct{ err error }

func (b brokenReader) Read([]byte) (int, error) {
	return 0, b.err
}

func fetchOne(oid string, si StorageNode, cb FetchCallback) error {
	res, err := http.Get(si.BlobURL(oid))
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("HTTP error: %v", res.Status)
	}
	return cb(oid, res.Body)
}

func fetchWorker(cb FetchCallback, nodes map[string]StorageNode,
	ch chan fetchWork, errch chan<- error, wg *sync.WaitGroup) {

	defer wg.Done()
	for w := range ch {
		var err error
		names := []string{}
		for n := range w.bi.Nodes {
			names = append(names, n)
		}
		for _, pos := range rand.Perm(len(names)) {
			n := names[pos]
			err = fetchOne(w.oid, nodes[n], cb)
			if err == nil {
				break
			}
		}
		if err != nil {
			select {
			case errch <- err:
			default:
			}
			cb(w.oid,
				brokenReader{fmt.Errorf("couldn't find %v", w.oid)})
		}
	}
}

// Fetch many blobs in bulk.
func (c *Client) Blobs(concurrency int,
	cb FetchCallback, oids ...string) error {

	nodes, err := c.Nodes()
	if err != nil {
		return err
	}

	infos, err := c.getBlobInfos(oids...)
	if err != nil {
		return err
	}

	workch := make(chan fetchWork)
	errch := make(chan error, 1)

	wg := &sync.WaitGroup{}
	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		go fetchWorker(cb, nodes, workch, errch, wg)
	}

	for oid, info := range infos {
		workch <- fetchWork{oid, info}
	}
	close(workch)

	go func() {
		wg.Wait()
		close(errch)
	}()

	return <-errch
}

// Grab a file.
//
// This ensures the request is coming directly from a node that
// already has the blob vs. proxying.
func (c Client) Get(path string) (io.ReadCloser, error) {
	req, err := http.NewRequest("GET", c.URLFor(path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-CBFS-LocalOnly", "true")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case 200:
		return res.Body, nil
	case 300:
		defer res.Body.Close()
		res, err = http.Get(res.Header.Get("Location"))
		if err != nil {
			return nil, err
		}
		return res.Body, nil
	default:
		defer res.Body.Close()
		return nil, fmt.Errorf("HTTP Error: %v", res.Status)
	}
}
<commit_msg>Retry loop on download.<commit_after>package cbfsclient

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"
)

type FetchCallback func(oid string, r io.Reader) error

type blobInfo struct {
	Nodes map[string]time.Time
}

func (c Client) getBlobInfos(oids ...string) (map[string]blobInfo, error) {
	u := c.URLFor("/.cbfs/blob/info/")
	form := url.Values{"blob": oids}
	res, err := http.PostForm(u, form)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error fetching blob info: %v",
			res.Status)
	}

	d := json.NewDecoder(res.Body)
	rv := map[string]blobInfo{}
	err = d.Decode(&rv)
	return rv, err
}

type fetchWork struct {
	oid string
	bi  blobInfo
}

type brokenReader struct{ err error }

func (b brokenReader) Read([]byte) (int, error) {
	return 0, b.err
}

func fetchOne(oid string, si StorageNode, cb FetchCallback) error {
	res, err := http.Get(si.BlobURL(oid))
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("HTTP error: %v", res.Status)
	}
	return cb(oid, res.Body)
}

func fetchWorker(cb FetchCallback, nodes map[string]StorageNode,
	ch chan fetchWork, errch chan<- error, wg *sync.WaitGroup) {

	defer wg.Done()
	for w := range ch {
		var err error
		names := []string{}
		for n := range w.bi.Nodes {
			names = append(names, n)
		}
		for i := 0; i < 3; i++ {
			for _, pos := range rand.Perm(len(names)) {
				n := names[pos]
				err = fetchOne(w.oid, nodes[n], cb)
				if err == nil {
					break
				}
			}
		}
		if err != nil {
			select {
			case errch <- err:
			default:
			}
			cb(w.oid,
				brokenReader{fmt.Errorf("couldn't find %v", w.oid)})
		}
	}
}

// Fetch many blobs in bulk.
func (c *Client) Blobs(concurrency int,
	cb FetchCallback, oids ...string) error {

	nodes, err := c.Nodes()
	if err != nil {
		return err
	}

	infos, err := c.getBlobInfos(oids...)
	if err != nil {
		return err
	}

	workch := make(chan fetchWork)
	errch := make(chan error, 1)

	wg := &sync.WaitGroup{}
	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		go fetchWorker(cb, nodes, workch, errch, wg)
	}

	for oid, info := range infos {
		workch <- fetchWork{oid, info}
	}
	close(workch)

	go func() {
		wg.Wait()
		close(errch)
	}()

	return <-errch
}

// Grab a file.
//
// This ensures the request is coming directly from a node that
// already has the blob vs. proxying.
func (c Client) Get(path string) (io.ReadCloser, error) {
	req, err := http.NewRequest("GET", c.URLFor(path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-CBFS-LocalOnly", "true")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case 200:
		return res.Body, nil
	case 300:
		defer res.Body.Close()
		res, err = http.Get(res.Header.Get("Location"))
		if err != nil {
			return nil, err
		}
		return res.Body, nil
	default:
		defer res.Body.Close()
		return nil, fmt.Errorf("HTTP Error: %v", res.Status)
	}
}
<|endoftext|>


<commit_before>package provisioning

import "github.com/jiasir/playback/command"

// OpenStack interface takes methods for provision OpenStack.
type OpenStack interface {
	// Prepare OpenStack basic environment.
	PrepareBasicEnvirionment()
	// Using playback-nic to setting the network for storage network.
	ConfigureStorageNetwork()
	// Deploy HAProxy and keepalived.
	LoadBalancer()
	// LBOptimize optimizing load balancer.
	LBOptimize()
	// Deploy MariaDB cluster.
	MariadbCluster()
	// Deploy RabbitMQ cluster.
	RabbtmqCluster()
	// Deploy Keystone HA.
	Keystone()
	// Format the disk for Swift storage, only support sdb1 and sdc1 currently.
	FormatDiskForSwift()
	// Deploy Swift storage.
	SwiftStorage()
	// Deploy Swift proxy HA.
	SwiftProxy()
	// Initial Swift rings.
	InitSwiftRings()
	// Distribute Swift ring configuration files.
	DistSwiftRingConf()
	// Finalize Swift installation.
	FinalizeSwift()
	// Deploy Glance HA.
	Glance()
	// Deploy Ceph admin node.
	CephAdmin()
	// Deploy the Ceph initial monitor.
	CephInitMon()
	// Deploy the Ceph clients.
	CephClient()
	// Add Ceph initial monitor(s) and gather the keys.
	GetCephKey()
	// Add Ceph OSDs.
	AddOSD()
	// Add Ceph monitors.
	AddCephMon()
	// Copy the Ceph keys to nodes.
	SyncCephKey()
	// Create the cinder ceph user and pool name.
	CephUserPool()
	// Deploy cinder-api.
	CinderAPI()
	// Deploy cinder-volume on controller node(Ceph backend).
	CinderVolume()
	// Restart volume service dependency to take effect for ceph backend.
	RestartCephDeps()
	// Deploy Nova controller.
	NovaController()
	// Deploy Horizon.
	Dashboard()
	// Deploy Nova computes.
	NovaComputes()
	// Deploy Legacy networking nova-network(FlatDHCP Only).
	NovaNetwork()
	// Deploy Orchestration components(heat).
	Heat()
	// Enable service auto start
	AutoStart()
	// Deploy Dns as a Service
	Designate()
	// Convert kvm to Docker(OPTIONAL)
	KvmToDocker()
}

// ExtraVars takes playback command line arguments.
type ExtraVars struct {
	// Ansible Playbook *.yml
	Playbook string
	// Vars: node_name
	NodeName string
	// Vars: host
	HostIP string
	// Vars: storage_ip
	StorageIP string
	// Vars: storage_mask
	StorageMask string
	// Vars: storage_network
	StorageNetwork string
	// Vars: storage_broadcast
	StorageBroadcast string
	// Command line playback-nic
	PlaybackNic PlaybackNic
	// Vars: host
	HostName string
	// Vars: router_id
	RouterID string
	// Vars: state
	State string
	// Vars: priority
	Priority string
	// Python scripts *.py
	PythonScript string
	// Vars: my_ip
	MyIP string
	// Vars: my_storage_ip
	MyStorageIP string
	// Vars: swift_storage_storage_ip
	SwiftStorageStorageIP string
	// Vars: device_name
	DeviceName string
	// Vars: device_weight
	DeviceWeight int
	// Vars: hosts
	Hosts string
	// Vars: client
	ClientName string
	// Vars: disk
	Disk string
	// Vars: partition
	Partition string
}

// PlaybackNic using playback-nic command instaed of openstack_interface.yml
type PlaybackNic struct {
	// Args: purge
	Purge bool
	// Args: public
	Public bool
	// Args: private
	Private bool
	// Args: host
	Host string
	// Args: user
	User string
	// Args: address
	Address string
	// Args: nic
	NIC string
	// Args: netmask
	Netmask string
	// Args: gateway
	Gateway string
	// Args: dns-nameservers
	DNS string
}

// ConfigureStorageNetwork takes playback-nic to set up the storage network.
// Purge the configuration and set address to 192.169.151.19 for eth1 of host 192.169.150.19 as public interface:
//	playback-nic --purge --public --host 192.169.150.19 --user ubuntu --address 192.169.151.19 --nic eth1 --netmask 255.255.255.0 --gateway 192.169.151.1 --dns-nameservers "192.169.11.11 192.169.11.12"
//Setting address to 192.168.1.12 for eth2 of host 192.169.150.19 as private interface:
//	playback-nic --private --host 192.169.150.19 --user ubuntu --address 192.168.1.12 --nic eth2 --netmask 255.255.255.0
func (vars ExtraVars) ConfigureStorageNetwork() error {
	if vars.PlaybackNic.Purge {
		if vars.PlaybackNic.Public {
			command.ExecuteWithOutput("playback-nic", "--purge", "--public", "--host", vars.PlaybackNic.Host, "--user", vars.PlaybackNic.User, "--address", vars.PlaybackNic.Address, "--nic", vars.PlaybackNic.NIC, "--netmask", vars.PlaybackNic.Netmask, "--gateway", vars.PlaybackNic.Gateway, "--dns-nameservers", vars.PlaybackNic.DNS)
		}
	}
	if vars.PlaybackNic.Private {
		command.ExecuteWithOutput("playback-nic", "--private", "--host", vars.PlaybackNic.Host, "--user", vars.PlaybackNic.Host, "--address", vars.PlaybackNic.Address, "--nic", vars.PlaybackNic.NIC, "--netmask", vars.PlaybackNic.Netmask)
	}
	return nil
}

// LoadBalancer deploy a HAProxy and Keepalived for OpenStack HA.
func (vars ExtraVars) LoadBalancer() error {
	command.ExecuteWithOutput("playback", "--ansible", "openstack_haproxy.yml", "--extra-vars", "host="+vars.HostName, "router_id="+vars.RouterID, "state="+vars.State, "priority="+vars.Priority, "-vvvv")
	return nil
}

// LBOptimize optimizing load balancer.
func (vars ExtraVars) LBOptimize() error {
	command.ExecuteWithOutput("python patch-limits.py")
	return nil
}

// PrepareBasicEnvirionment prepares OpenStack basic environment.
func (vars ExtraVars) PrepareBasicEnvirionment() error {
	return nil
}

// MariadbCluster deploy MariaDB Cluster.
func (vars ExtraVars) MariadbCluster() error {
	return nil
}

// RabbtmqCluster deploy RabbitMQ Cluster.
func (vars ExtraVars) RabbtmqCluster() error {
	return nil
}

// Keystone method deploy the Keystone components.
func (vars ExtraVars) Keystone() error {
	return nil
}

// FormatDiskForSwift formats devices for Swift Storage (sdb1 and sdc1).
func (vars ExtraVars) FormatDiskForSwift() error {
	return nil
}

// SwiftStorage deploy Swift storage.
func (vars ExtraVars) SwiftStorage() error {
	return nil
}

// SwiftProxy deploy Swift proxy HA.
func (vars ExtraVars) SwiftProxy() error {
	return nil
}

// InitSwiftRings initial Swift rings.
func (vars ExtraVars) InitSwiftRings() error {
	return nil
}

// DistSwiftRingConf destribute Swift ring configuration files.
func (vars ExtraVars) DistSwiftRingConf() error {
	return nil
}

// FinalizeSwift finalize Swift installation.
func (vars ExtraVars) FinalizeSwift() error {
	return nil
}

// Glance deploy Glance HA.
func (vars ExtraVars) Glance() error {
	return nil
}

// CephAdmin deploy the Ceph admin node.
func (vars ExtraVars) CephAdmin() error {
	return nil
}

// CephInitMon deploy the Ceph initial monitor.
func (vars ExtraVars) CephInitMon() error {
	return nil
}

// CephClient deploy the Ceph client.
func (vars ExtraVars) CephClient() error {
	return nil
}

// GetCephKey add Ceph initial monitors and gather the keys.
func (vars ExtraVars) GetCephKey() error {
	return nil
}

// AddOSD add the Ceph OSDs.
func (vars ExtraVars) AddOSD() error {
	return nil
}

// AddCephMon add the Ceph monitors.
func (vars ExtraVars) AddCephMon() error {
	return nil
}

// SyncCephKey copy the Ceph keys to nodes.
func (vars ExtraVars) SyncCephKey() error {
	return nil
}

// CephUserPool creates the cinder ceph user and pool name.
func (vars ExtraVars) CephUserPool() error {
	return nil
}

// CinderAPI deploy cinder-api.
func (vars ExtraVars) CinderAPI() error {
	return nil
}

// CinderVolume deploy cinder-volume on controller node(ceph backend).
func (vars ExtraVars) CinderVolume() error {
	return nil
}

// RestartCephDeps restart volume service dependency to take effect for ceph backend.
func (vars ExtraVars) RestartCephDeps() error {
	return nil
}

// NovaController deploy Nova controller.
func (vars ExtraVars) NovaController() error {
	return nil
}

// Dashboard deploy Horizon.
func (vars ExtraVars) Dashboard() error {
	return nil
}

// NovaComputes deploy Nova computes.
func (vars ExtraVars) NovaComputes() error {
	return nil
}

// NovaNetwork deploy legacy networking nova-network(FLATdhcp Only).
func (vars ExtraVars) NovaNetwork() error {
	return nil
}

// Heat deploy orchestration components(heat).
func (vars ExtraVars) Heat() error {
	return nil
}

// AutoStart fix the service can not auto start when sys booting.
func (vars ExtraVars) AutoStart() error {
	return nil
}

// Designate deploy DNS as a Service.
func (vars ExtraVars) Designate() error {
	return nil
}

// KvmToDocker converts kvm to docker(OPTIONAL).
func (vars ExtraVars) KvmToDocker() error {
	return nil
}
<commit_msg>Prepare OpenStack environment<commit_after>package provisioning

import "github.com/jiasir/playback/command"

// OpenStack interface takes methods for provision OpenStack.
type OpenStack interface {
	// Prepare OpenStack basic environment.
	PrepareBasicEnvirionment()
	// Using playback-nic to setting the network for storage network.
	ConfigureStorageNetwork()
	// Deploy HAProxy and keepalived.
	LoadBalancer()
	// LBOptimize optimizing load balancer.
	LBOptimize()
	// Deploy MariaDB cluster.
	MariadbCluster()
	// Deploy RabbitMQ cluster.
	RabbtmqCluster()
	// Deploy Keystone HA.
	Keystone()
	// Format the disk for Swift storage, only support sdb1 and sdc1 currently.
	FormatDiskForSwift()
	// Deploy Swift storage.
	SwiftStorage()
	// Deploy Swift proxy HA.
	SwiftProxy()
	// Initial Swift rings.
	InitSwiftRings()
	// Distribute Swift ring configuration files.
	DistSwiftRingConf()
	// Finalize Swift installation.
	FinalizeSwift()
	// Deploy Glance HA.
	Glance()
	// Deploy Ceph admin node.
	CephAdmin()
	// Deploy the Ceph initial monitor.
	CephInitMon()
	// Deploy the Ceph clients.
	CephClient()
	// Add Ceph initial monitor(s) and gather the keys.
	GetCephKey()
	// Add Ceph OSDs.
	AddOSD()
	// Add Ceph monitors.
	AddCephMon()
	// Copy the Ceph keys to nodes.
	SyncCephKey()
	// Create the cinder ceph user and pool name.
	CephUserPool()
	// Deploy cinder-api.
	CinderAPI()
	// Deploy cinder-volume on controller node(Ceph backend).
	CinderVolume()
	// Restart volume service dependency to take effect for ceph backend.
	RestartCephDeps()
	// Deploy Nova controller.
	NovaController()
	// Deploy Horizon.
	Dashboard()
	// Deploy Nova computes.
	NovaComputes()
	// Deploy Legacy networking nova-network(FlatDHCP Only).
	NovaNetwork()
	// Deploy Orchestration components(heat).
	Heat()
	// Enable service auto start
	AutoStart()
	// Deploy Dns as a Service
	Designate()
	// Convert kvm to Docker(OPTIONAL)
	KvmToDocker()
}

// ExtraVars takes playback command line arguments.
type ExtraVars struct {
	// Ansible Playbook *.yml
	Playbook string
	// Vars: node_name
	NodeName string
	// Vars: host
	HostIP string
	// Vars: storage_ip
	StorageIP string
	// Vars: storage_mask
	StorageMask string
	// Vars: storage_network
	StorageNetwork string
	// Vars: storage_broadcast
	StorageBroadcast string
	// Command line playback-nic
	PlaybackNic PlaybackNic
	// Vars: host
	HostName string
	// Vars: router_id
	RouterID string
	// Vars: state
	State string
	// Vars: priority
	Priority string
	// Python scripts *.py
	PythonScript string
	// Vars: my_ip
	MyIP string
	// Vars: my_storage_ip
	MyStorageIP string
	// Vars: swift_storage_storage_ip
	SwiftStorageStorageIP string
	// Vars: device_name
	DeviceName string
	// Vars: device_weight
	DeviceWeight int
	// Vars: hosts
	Hosts string
	// Vars: client
	ClientName string
	// Vars: disk
	Disk string
	// Vars: partition
	Partition string
}

// PlaybackNic using playback-nic command instaed of openstack_interface.yml
type PlaybackNic struct {
	// Args: purge
	Purge bool
	// Args: public
	Public bool
	// Args: private
	Private bool
	// Args: host
	Host string
	// Args: user
	User string
	// Args: address
	Address string
	// Args: nic
	NIC string
	// Args: netmask
	Netmask string
	// Args: gateway
	Gateway string
	// Args: dns-nameservers
	DNS string
}

// ConfigureStorageNetwork takes playback-nic to set up the storage network.
// Purge the configuration and set address to 192.169.151.19 for eth1 of host 192.169.150.19 as public interface:
//	playback-nic --purge --public --host 192.169.150.19 --user ubuntu --address 192.169.151.19 --nic eth1 --netmask 255.255.255.0 --gateway 192.169.151.1 --dns-nameservers "192.169.11.11 192.169.11.12"
//Setting address to 192.168.1.12 for eth2 of host 192.169.150.19 as private interface:
//	playback-nic --private --host 192.169.150.19 --user ubuntu --address 192.168.1.12 --nic eth2 --netmask 255.255.255.0
func (vars ExtraVars) ConfigureStorageNetwork() error {
	if vars.PlaybackNic.Purge {
		if vars.PlaybackNic.Public {
			command.ExecuteWithOutput("playback-nic", "--purge", "--public", "--host", vars.PlaybackNic.Host, "--user", vars.PlaybackNic.User, "--address", vars.PlaybackNic.Address, "--nic", vars.PlaybackNic.NIC, "--netmask", vars.PlaybackNic.Netmask, "--gateway", vars.PlaybackNic.Gateway, "--dns-nameservers", vars.PlaybackNic.DNS)
		}
	}
	if vars.PlaybackNic.Private {
		command.ExecuteWithOutput("playback-nic", "--private", "--host", vars.PlaybackNic.Host, "--user", vars.PlaybackNic.Host, "--address", vars.PlaybackNic.Address, "--nic", vars.PlaybackNic.NIC, "--netmask", vars.PlaybackNic.Netmask)
	}
	return nil
}

// LoadBalancer deploy a HAProxy and Keepalived for OpenStack HA.
func (vars ExtraVars) LoadBalancer() error {
	command.ExecuteWithOutput("playback", "--ansible", "openstack_haproxy.yml", "--extra-vars", "host="+vars.HostName, "router_id="+vars.RouterID, "state="+vars.State, "priority="+vars.Priority, "-vvvv")
	return nil
}

// LBOptimize optimizing load balancer.
func (vars ExtraVars) LBOptimize() error {
	command.ExecuteWithOutput("python patch-limits.py")
	return nil
}

// PrepareBasicEnvirionment prepares OpenStack basic environment.
func (vars ExtraVars) PrepareBasicEnvirionment() error {
	command.ExecuteWithOutput("playback", "--ansible", "openstack_basic_environment.yml", "-vvvv")
	return nil
}

// MariadbCluster deploy MariaDB Cluster.
func (vars ExtraVars) MariadbCluster() error {
	return nil
}

// RabbtmqCluster deploy RabbitMQ Cluster.
func (vars ExtraVars) RabbtmqCluster() error {
	return nil
}

// Keystone method deploy the Keystone components.
func (vars ExtraVars) Keystone() error {
	return nil
}

// FormatDiskForSwift formats devices for Swift Storage (sdb1 and sdc1).
func (vars ExtraVars) FormatDiskForSwift() error {
	return nil
}

// SwiftStorage deploy Swift storage.
func (vars ExtraVars) SwiftStorage() error {
	return nil
}

// SwiftProxy deploy Swift proxy HA.
func (vars ExtraVars) SwiftProxy() error {
	return nil
}

// InitSwiftRings initial Swift rings.
func (vars ExtraVars) InitSwiftRings() error {
	return nil
}

// DistSwiftRingConf destribute Swift ring configuration files.
func (vars ExtraVars) DistSwiftRingConf() error {
	return nil
}

// FinalizeSwift finalize Swift installation.
func (vars ExtraVars) FinalizeSwift() error {
	return nil
}

// Glance deploy Glance HA.
func (vars ExtraVars) Glance() error {
	return nil
}

// CephAdmin deploy the Ceph admin node.
func (vars ExtraVars) CephAdmin() error {
	return nil
}

// CephInitMon deploy the Ceph initial monitor.
func (vars ExtraVars) CephInitMon() error {
	return nil
}

// CephClient deploy the Ceph client.
func (vars ExtraVars) CephClient() error {
	return nil
}

// GetCephKey add Ceph initial monitors and gather the keys.
func (vars ExtraVars) GetCephKey() error {
	return nil
}

// AddOSD add the Ceph OSDs.
func (vars ExtraVars) AddOSD() error {
	return nil
}

// AddCephMon add the Ceph monitors.
func (vars ExtraVars) AddCephMon() error {
	return nil
}

// SyncCephKey copy the Ceph keys to nodes.
func (vars ExtraVars) SyncCephKey() error {
	return nil
}

// CephUserPool creates the cinder ceph user and pool name.
func (vars ExtraVars) CephUserPool() error {
	return nil
}

// CinderAPI deploy cinder-api.
func (vars ExtraVars) CinderAPI() error {
	return nil
}

// CinderVolume deploy cinder-volume on controller node(ceph backend).
func (vars ExtraVars) CinderVolume() error {
	return nil
}

// RestartCephDeps restart volume service dependency to take effect for ceph backend.
func (vars ExtraVars) RestartCephDeps() error {
	return nil
}

// NovaController deploy Nova controller.
func (vars ExtraVars) NovaController() error {
	return nil
}

// Dashboard deploy Horizon.
func (vars ExtraVars) Dashboard() error {
	return nil
}

// NovaComputes deploy Nova computes.
func (vars ExtraVars) NovaComputes() error {
	return nil
}

// NovaNetwork deploy legacy networking nova-network(FLATdhcp Only).
func (vars ExtraVars) NovaNetwork() error {
	return nil
}

// Heat deploy orchestration components(heat).
func (vars ExtraVars) Heat() error {
	return nil
}

// AutoStart fix the service can not auto start when sys booting.
func (vars ExtraVars) AutoStart() error {
	return nil
}

// Designate deploy DNS as a Service.
func (vars ExtraVars) Designate() error {
	return nil
}

// KvmToDocker converts kvm to docker(OPTIONAL).
func (vars ExtraVars) KvmToDocker() error {
	return nil
}
<|endoftext|>