<commit_before>package main

import (
	"fmt"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

type initDataNode struct {
	api.ServerPut `yaml:",inline"`
	Networks      []internalClusterPostNetwork `json:"networks" yaml:"networks"`
	StoragePools  []api.StoragePoolsPost       `json:"storage_pools" yaml:"storage_pools"`
	Profiles      []api.ProfilesPost           `json:"profiles" yaml:"profiles"`
	Projects      []api.ProjectsPost           `json:"projects" yaml:"projects"`
}

type initDataCluster struct {
	api.ClusterPut `yaml:",inline"`

	// The path to the cluster certificate
	// Example: /tmp/cluster.crt
	ClusterCertificatePath string `json:"cluster_certificate_path" yaml:"cluster_certificate_path"`

	// A cluster join token
	// Example: BASE64-TOKEN
	ClusterToken string `json:"cluster_token" yaml:"cluster_token"`
}

// Helper to initialize node-specific entities on a LXD instance using the
// definitions from the given initDataNode object.
//
// It's used both by the 'lxd init' command and by the PUT /1.0/cluster API.
//
// In case of error, the returned function can be used to revert the changes.
func initDataNodeApply(d lxd.InstanceServer, config initDataNode) (func(), error) {
	revert := revert.New()
	defer revert.Fail()

	// Apply server configuration.
	if config.Config != nil && len(config.Config) > 0 {
		// Get current config.
		currentServer, etag, err := d.GetServer()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve current server configuration: %w", err)
		}

		// Setup reverter.
		revert.Add(func() { _ = d.UpdateServer(currentServer.Writable(), "") })

		// Prepare the update.
		newServer := api.ServerPut{}
		err = shared.DeepCopy(currentServer.Writable(), &newServer)
		if err != nil {
			return nil, fmt.Errorf("Failed to copy server configuration: %w", err)
		}

		for k, v := range config.Config {
			newServer.Config[k] = fmt.Sprintf("%v", v)
		}

		// Apply it.
		err = d.UpdateServer(newServer, etag)
		if err != nil {
			return nil, fmt.Errorf("Failed to update server configuration: %w", err)
		}
	}

	// Apply storage configuration.
	if config.StoragePools != nil && len(config.StoragePools) > 0 {
		// Get the list of storagePools.
		storagePoolNames, err := d.GetStoragePoolNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of storage pools: %w", err)
		}

		// StoragePool creator
		createStoragePool := func(storagePool api.StoragePoolsPost) error {
			// Create the storagePool if doesn't exist.
			err := d.CreateStoragePool(storagePool)
			if err != nil {
				return fmt.Errorf("Failed to create storage pool %q: %w", storagePool.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteStoragePool(storagePool.Name) })
			return nil
		}

		// StoragePool updater.
		updateStoragePool := func(storagePool api.StoragePoolsPost) error {
			// Get the current storagePool.
			currentStoragePool, etag, err := d.GetStoragePool(storagePool.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current storage pool %q: %w", storagePool.Name, err)
			}

			// Quick check.
			if currentStoragePool.Driver != storagePool.Driver {
				return fmt.Errorf("Storage pool %q is of type %q instead of %q", currentStoragePool.Name, currentStoragePool.Driver, storagePool.Driver)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateStoragePool(currentStoragePool.Name, currentStoragePool.Writable(), "") })

			// Prepare the update.
			newStoragePool := api.StoragePoolPut{}
			err = shared.DeepCopy(currentStoragePool.Writable(), &newStoragePool)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of storage pool %q: %w", storagePool.Name, err)
			}

			// Description override.
			if storagePool.Description != "" {
				newStoragePool.Description = storagePool.Description
			}

			// Config overrides.
			for k, v := range storagePool.Config {
				newStoragePool.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UpdateStoragePool(currentStoragePool.Name, newStoragePool, etag)
			if err != nil {
				return fmt.Errorf("Failed to update storage pool %q: %w", storagePool.Name, err)
			}

			return nil
		}

		for _, storagePool := range config.StoragePools {
			// New storagePool.
			if !shared.StringInSlice(storagePool.Name, storagePoolNames) {
				err := createStoragePool(storagePool)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing storagePool.
			err := updateStoragePool(storagePool)
			if err != nil {
				return nil, err
			}
		}
	}

	// Apply network configuration function.
	applyNetwork := func(network internalClusterPostNetwork) error {
		_, _, err := d.UseProject(network.Project).GetNetwork(network.Name)
		if err != nil {
			// Create the network if doesn't exist.
			err := d.UseProject(network.Project).CreateNetwork(network.NetworksPost)
			if err != nil {
				return fmt.Errorf("Failed to create local member network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UseProject(network.Project).DeleteNetwork(network.Name) })
		} else {
			// Get the current network.
			currentNetwork, etag, err := d.UseProject(network.Project).GetNetwork(network.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Prepare the update.
			newNetwork := api.NetworkPut{}
			err = shared.DeepCopy(currentNetwork.Writable(), &newNetwork)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Description override.
			if network.Description != "" {
				newNetwork.Description = network.Description
			}

			// Config overrides.
			for k, v := range network.Config {
				newNetwork.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UseProject(network.Project).UpdateNetwork(currentNetwork.Name, newNetwork, etag)
			if err != nil {
				return fmt.Errorf("Failed to update local member network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Setup reverter.
			revert.Add(func() {
				_ = d.UseProject(network.Project).UpdateNetwork(currentNetwork.Name, currentNetwork.Writable(), "")
			})
		}

		return nil
	}

	// Apply networks in the default project before other projects config applied (so that if the projects
	// depend on a network in the default project they can have their config applied successfully).
	for i := range config.Networks {
		// Populate default project if not specified for backwards compatbility with earlier
		// preseed dump files.
		if config.Networks[i].Project == "" {
			config.Networks[i].Project = project.Default
		}

		if config.Networks[i].Project != project.Default {
			continue
		}

		err := applyNetwork(config.Networks[i])
		if err != nil {
			return nil, err
		}
	}

	// Apply project configuration.
	if config.Projects != nil && len(config.Projects) > 0 {
		// Get the list of projects.
		projectNames, err := d.GetProjectNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of projects: %w", err)
		}

		// Project creator.
		createProject := func(project api.ProjectsPost) error {
			// Create the project if doesn't exist.
			err := d.CreateProject(project)
			if err != nil {
				return fmt.Errorf("Failed to create local member project %q: %w", project.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteProject(project.Name) })
			return nil
		}

		// Project updater.
		updateProject := func(project api.ProjectsPost) error {
			// Get the current project.
			currentProject, etag, err := d.GetProject(project.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current project %q: %w", project.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateProject(currentProject.Name, currentProject.Writable(), "") })

			// Prepare the update.
			newProject := api.ProjectPut{}
			err = shared.DeepCopy(currentProject.Writable(), &newProject)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of project %q: %w", project.Name, err)
			}

			// Description override.
			if project.Description != "" {
				newProject.Description = project.Description
			}

			// Config overrides.
			for k, v := range project.Config {
				newProject.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UpdateProject(currentProject.Name, newProject, etag)
			if err != nil {
				return fmt.Errorf("Failed to update local member project %q: %w", project.Name, err)
			}

			return nil
		}

		for _, project := range config.Projects {
			// New project.
			if !shared.StringInSlice(project.Name, projectNames) {
				err := createProject(project)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing project.
			err := updateProject(project)
			if err != nil {
				return nil, err
			}
		}
	}

	// Apply networks in non-default projects after project config applied (so that their projects exist).
	for i := range config.Networks {
		if config.Networks[i].Project == project.Default {
			continue
		}

		err := applyNetwork(config.Networks[i])
		if err != nil {
			return nil, err
		}
	}

	// Apply profile configuration.
	if config.Profiles != nil && len(config.Profiles) > 0 {
		// Get the list of profiles.
		profileNames, err := d.GetProfileNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of profiles: %w", err)
		}

		// Profile creator.
		createProfile := func(profile api.ProfilesPost) error {
			// Create the profile if doesn't exist.
			err := d.CreateProfile(profile)
			if err != nil {
				return fmt.Errorf("Failed to create profile %q: %w", profile.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteProfile(profile.Name) })
			return nil
		}

		// Profile updater.
		updateProfile := func(profile api.ProfilesPost) error {
			// Get the current profile.
			currentProfile, etag, err := d.GetProfile(profile.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current profile %q: %w", profile.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateProfile(currentProfile.Name, currentProfile.Writable(), "") })

			// Prepare the update.
			newProfile := api.ProfilePut{}
			err = shared.DeepCopy(currentProfile.Writable(), &newProfile)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of profile %q: %w", profile.Name, err)
			}

			// Description override.
			if profile.Description != "" {
				newProfile.Description = profile.Description
			}

			// Config overrides.
			for k, v := range profile.Config {
				newProfile.Config[k] = fmt.Sprintf("%v", v)
			}

			// Device overrides.
			for k, v := range profile.Devices {
				// New device.
				_, ok := newProfile.Devices[k]
				if !ok {
					newProfile.Devices[k] = v
					continue
				}

				// Existing device.
				for configKey, configValue := range v {
					newProfile.Devices[k][configKey] = fmt.Sprintf("%v", configValue)
				}
			}

			// Apply it.
			err = d.UpdateProfile(currentProfile.Name, newProfile, etag)
			if err != nil {
				return fmt.Errorf("Failed to update profile %q: %w", profile.Name, err)
			}

			return nil
		}

		for _, profile := range config.Profiles {
			// New profile.
			if !shared.StringInSlice(profile.Name, profileNames) {
				err := createProfile(profile)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing profile.
			err := updateProfile(profile)
			if err != nil {
				return nil, err
			}
		}
	}

	cleanup := revert.Clone().Fail // Clone before calling revert.Success() so we can return the Fail func.
	revert.Success()
	return cleanup, nil
}

// Helper to initialize LXD clustering.
//
// Used by the 'lxd init' command.
func initDataClusterApply(d lxd.InstanceServer, config *initDataCluster) error {
	if config == nil || !config.Enabled {
		return nil
	}

	// Get the current cluster configuration
	currentCluster, etag, err := d.GetCluster()
	if err != nil {
		return fmt.Errorf("Failed to retrieve current cluster config: %w", err)
	}

	// Check if already enabled
	if !currentCluster.Enabled {
		// Configure the cluster
		op, err := d.UpdateCluster(config.ClusterPut, etag)
		if err != nil {
			return fmt.Errorf("Failed to configure cluster: %w", err)
		}

		err = op.Wait()
		if err != nil {
			return fmt.Errorf("Failed to configure cluster: %w", err)
		}
	}

	return nil
}
<commit_msg>lxd/init: Remove unnecessary duplicate network load request in initDataNodeApply<commit_after>package main

import (
	"fmt"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

type initDataNode struct {
	api.ServerPut `yaml:",inline"`
	Networks      []internalClusterPostNetwork `json:"networks" yaml:"networks"`
	StoragePools  []api.StoragePoolsPost       `json:"storage_pools" yaml:"storage_pools"`
	Profiles      []api.ProfilesPost           `json:"profiles" yaml:"profiles"`
	Projects      []api.ProjectsPost           `json:"projects" yaml:"projects"`
}

type initDataCluster struct {
	api.ClusterPut `yaml:",inline"`

	// The path to the cluster certificate
	// Example: /tmp/cluster.crt
	ClusterCertificatePath string `json:"cluster_certificate_path" yaml:"cluster_certificate_path"`

	// A cluster join token
	// Example: BASE64-TOKEN
	ClusterToken string `json:"cluster_token" yaml:"cluster_token"`
}

// Helper to initialize node-specific entities on a LXD instance using the
// definitions from the given initDataNode object.
//
// It's used both by the 'lxd init' command and by the PUT /1.0/cluster API.
//
// In case of error, the returned function can be used to revert the changes.
func initDataNodeApply(d lxd.InstanceServer, config initDataNode) (func(), error) {
	revert := revert.New()
	defer revert.Fail()

	// Apply server configuration.
	if config.Config != nil && len(config.Config) > 0 {
		// Get current config.
		currentServer, etag, err := d.GetServer()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve current server configuration: %w", err)
		}

		// Setup reverter.
		revert.Add(func() { _ = d.UpdateServer(currentServer.Writable(), "") })

		// Prepare the update.
		newServer := api.ServerPut{}
		err = shared.DeepCopy(currentServer.Writable(), &newServer)
		if err != nil {
			return nil, fmt.Errorf("Failed to copy server configuration: %w", err)
		}

		for k, v := range config.Config {
			newServer.Config[k] = fmt.Sprintf("%v", v)
		}

		// Apply it.
		err = d.UpdateServer(newServer, etag)
		if err != nil {
			return nil, fmt.Errorf("Failed to update server configuration: %w", err)
		}
	}

	// Apply storage configuration.
	if config.StoragePools != nil && len(config.StoragePools) > 0 {
		// Get the list of storagePools.
		storagePoolNames, err := d.GetStoragePoolNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of storage pools: %w", err)
		}

		// StoragePool creator
		createStoragePool := func(storagePool api.StoragePoolsPost) error {
			// Create the storagePool if doesn't exist.
			err := d.CreateStoragePool(storagePool)
			if err != nil {
				return fmt.Errorf("Failed to create storage pool %q: %w", storagePool.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteStoragePool(storagePool.Name) })
			return nil
		}

		// StoragePool updater.
		updateStoragePool := func(storagePool api.StoragePoolsPost) error {
			// Get the current storagePool.
			currentStoragePool, etag, err := d.GetStoragePool(storagePool.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current storage pool %q: %w", storagePool.Name, err)
			}

			// Quick check.
			if currentStoragePool.Driver != storagePool.Driver {
				return fmt.Errorf("Storage pool %q is of type %q instead of %q", currentStoragePool.Name, currentStoragePool.Driver, storagePool.Driver)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateStoragePool(currentStoragePool.Name, currentStoragePool.Writable(), "") })

			// Prepare the update.
			newStoragePool := api.StoragePoolPut{}
			err = shared.DeepCopy(currentStoragePool.Writable(), &newStoragePool)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of storage pool %q: %w", storagePool.Name, err)
			}

			// Description override.
			if storagePool.Description != "" {
				newStoragePool.Description = storagePool.Description
			}

			// Config overrides.
			for k, v := range storagePool.Config {
				newStoragePool.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UpdateStoragePool(currentStoragePool.Name, newStoragePool, etag)
			if err != nil {
				return fmt.Errorf("Failed to update storage pool %q: %w", storagePool.Name, err)
			}

			return nil
		}

		for _, storagePool := range config.StoragePools {
			// New storagePool.
			if !shared.StringInSlice(storagePool.Name, storagePoolNames) {
				err := createStoragePool(storagePool)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing storagePool.
			err := updateStoragePool(storagePool)
			if err != nil {
				return nil, err
			}
		}
	}

	// Apply network configuration function.
	applyNetwork := func(network internalClusterPostNetwork) error {
		currentNetwork, etag, err := d.UseProject(network.Project).GetNetwork(network.Name)
		if err != nil {
			// Create the network if doesn't exist.
			err := d.UseProject(network.Project).CreateNetwork(network.NetworksPost)
			if err != nil {
				return fmt.Errorf("Failed to create local member network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UseProject(network.Project).DeleteNetwork(network.Name) })
		} else {
			// Prepare the update.
			newNetwork := api.NetworkPut{}
			err = shared.DeepCopy(currentNetwork.Writable(), &newNetwork)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Description override.
			if network.Description != "" {
				newNetwork.Description = network.Description
			}

			// Config overrides.
			for k, v := range network.Config {
				newNetwork.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UseProject(network.Project).UpdateNetwork(currentNetwork.Name, newNetwork, etag)
			if err != nil {
				return fmt.Errorf("Failed to update local member network %q in project %q: %w", network.Name, network.Project, err)
			}

			// Setup reverter.
			revert.Add(func() {
				_ = d.UseProject(network.Project).UpdateNetwork(currentNetwork.Name, currentNetwork.Writable(), "")
			})
		}

		return nil
	}

	// Apply networks in the default project before other projects config applied (so that if the projects
	// depend on a network in the default project they can have their config applied successfully).
	for i := range config.Networks {
		// Populate default project if not specified for backwards compatbility with earlier
		// preseed dump files.
		if config.Networks[i].Project == "" {
			config.Networks[i].Project = project.Default
		}

		if config.Networks[i].Project != project.Default {
			continue
		}

		err := applyNetwork(config.Networks[i])
		if err != nil {
			return nil, err
		}
	}

	// Apply project configuration.
	if config.Projects != nil && len(config.Projects) > 0 {
		// Get the list of projects.
		projectNames, err := d.GetProjectNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of projects: %w", err)
		}

		// Project creator.
		createProject := func(project api.ProjectsPost) error {
			// Create the project if doesn't exist.
			err := d.CreateProject(project)
			if err != nil {
				return fmt.Errorf("Failed to create local member project %q: %w", project.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteProject(project.Name) })
			return nil
		}

		// Project updater.
		updateProject := func(project api.ProjectsPost) error {
			// Get the current project.
			currentProject, etag, err := d.GetProject(project.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current project %q: %w", project.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateProject(currentProject.Name, currentProject.Writable(), "") })

			// Prepare the update.
			newProject := api.ProjectPut{}
			err = shared.DeepCopy(currentProject.Writable(), &newProject)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of project %q: %w", project.Name, err)
			}

			// Description override.
			if project.Description != "" {
				newProject.Description = project.Description
			}

			// Config overrides.
			for k, v := range project.Config {
				newProject.Config[k] = fmt.Sprintf("%v", v)
			}

			// Apply it.
			err = d.UpdateProject(currentProject.Name, newProject, etag)
			if err != nil {
				return fmt.Errorf("Failed to update local member project %q: %w", project.Name, err)
			}

			return nil
		}

		for _, project := range config.Projects {
			// New project.
			if !shared.StringInSlice(project.Name, projectNames) {
				err := createProject(project)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing project.
			err := updateProject(project)
			if err != nil {
				return nil, err
			}
		}
	}

	// Apply networks in non-default projects after project config applied (so that their projects exist).
	for i := range config.Networks {
		if config.Networks[i].Project == project.Default {
			continue
		}

		err := applyNetwork(config.Networks[i])
		if err != nil {
			return nil, err
		}
	}

	// Apply profile configuration.
	if config.Profiles != nil && len(config.Profiles) > 0 {
		// Get the list of profiles.
		profileNames, err := d.GetProfileNames()
		if err != nil {
			return nil, fmt.Errorf("Failed to retrieve list of profiles: %w", err)
		}

		// Profile creator.
		createProfile := func(profile api.ProfilesPost) error {
			// Create the profile if doesn't exist.
			err := d.CreateProfile(profile)
			if err != nil {
				return fmt.Errorf("Failed to create profile %q: %w", profile.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.DeleteProfile(profile.Name) })
			return nil
		}

		// Profile updater.
		updateProfile := func(profile api.ProfilesPost) error {
			// Get the current profile.
			currentProfile, etag, err := d.GetProfile(profile.Name)
			if err != nil {
				return fmt.Errorf("Failed to retrieve current profile %q: %w", profile.Name, err)
			}

			// Setup reverter.
			revert.Add(func() { _ = d.UpdateProfile(currentProfile.Name, currentProfile.Writable(), "") })

			// Prepare the update.
			newProfile := api.ProfilePut{}
			err = shared.DeepCopy(currentProfile.Writable(), &newProfile)
			if err != nil {
				return fmt.Errorf("Failed to copy configuration of profile %q: %w", profile.Name, err)
			}

			// Description override.
			if profile.Description != "" {
				newProfile.Description = profile.Description
			}

			// Config overrides.
			for k, v := range profile.Config {
				newProfile.Config[k] = fmt.Sprintf("%v", v)
			}

			// Device overrides.
			for k, v := range profile.Devices {
				// New device.
				_, ok := newProfile.Devices[k]
				if !ok {
					newProfile.Devices[k] = v
					continue
				}

				// Existing device.
				for configKey, configValue := range v {
					newProfile.Devices[k][configKey] = fmt.Sprintf("%v", configValue)
				}
			}

			// Apply it.
			err = d.UpdateProfile(currentProfile.Name, newProfile, etag)
			if err != nil {
				return fmt.Errorf("Failed to update profile %q: %w", profile.Name, err)
			}

			return nil
		}

		for _, profile := range config.Profiles {
			// New profile.
			if !shared.StringInSlice(profile.Name, profileNames) {
				err := createProfile(profile)
				if err != nil {
					return nil, err
				}

				continue
			}

			// Existing profile.
			err := updateProfile(profile)
			if err != nil {
				return nil, err
			}
		}
	}

	cleanup := revert.Clone().Fail // Clone before calling revert.Success() so we can return the Fail func.
	revert.Success()
	return cleanup, nil
}

// Helper to initialize LXD clustering.
//
// Used by the 'lxd init' command.
func initDataClusterApply(d lxd.InstanceServer, config *initDataCluster) error {
	if config == nil || !config.Enabled {
		return nil
	}

	// Get the current cluster configuration
	currentCluster, etag, err := d.GetCluster()
	if err != nil {
		return fmt.Errorf("Failed to retrieve current cluster config: %w", err)
	}

	// Check if already enabled
	if !currentCluster.Enabled {
		// Configure the cluster
		op, err := d.UpdateCluster(config.ClusterPut, etag)
		if err != nil {
			return fmt.Errorf("Failed to configure cluster: %w", err)
		}

		err = op.Wait()
		if err != nil {
			return fmt.Errorf("Failed to configure cluster: %w", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/streadway/amqp"
)

// Bind a queue to the exchange to listen for messages
// When we publish a message, it will be sent to this queue, via the exchange
var routingKey = "words"
var exchangeName = "grandtour"
var connection *amqp.Connection
var channel *amqp.Channel
var queue amqp.Queue

func wordHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		// Use synchronous Get to get a single message from
		// the queue. In real life, prefer to Consume.
		msg, ok, err := channel.Get(queue.Name, true)
		if err != nil {
			log.Fatal(err)
		}

		// Was there something waiting?
		if ok {
			// Write the retrieved payload straight into the body
			w.Write(msg.Body)
		} else {
			// Or if nothing was retrieved, a message reflecting that
			w.Write([]byte("No message waiting"))
		}
		return
	case "PUT":
		r.ParseForm()
		// Compose a message comprising of the form message and the time
		// now
		msg := r.Form.Get("message") + " " + time.Now().Format("15:04:05.00")

		// Publish the messahe to the exchange with our routing key
		err := channel.Publish(exchangeName,
			routingKey,
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg),
			})

		if err != nil {
			log.Fatal(err)
		}
		// Set the response to accepted
		w.WriteHeader(http.StatusAccepted)
		// and rite the message that was sent back to the user
		w.Write([]byte(msg))
		return
	}

	return
}

func main() {
	// Connect to database:
	// Connection string in $COMPOSE_RABBITMQ_URL
	var err error

	// The library handles amqps connections and sets Servername correctly
	// so we can just connect here with the Dial() function
	connection, err = amqp.Dial(os.Getenv("COMPOSE_RABBITMQ_URL"))
	if err != nil {
		log.Fatal(err)
	}
	defer connection.Close()

	// We create a channel to communicate over. Note, if there is an
	// error on the channel, we would need to recreate the channel.
	channel, err = connection.Channel()
	if err != nil {
		log.Fatal(err)
	}

	// We can now create a durable exchange where we'll post our messages
	err = channel.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// And we'll declare a transient queue for our client
	queue, err = channel.QueueDeclare("", false, false, false, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Now we'll bind out transient queue to recieve messages with our
	// routing key
	err = channel.QueueBind(queue.Name, routingKey, exchangeName, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// With setup done we begin web serving
	fs := http.FileServer(http.Dir("public"))
	http.Handle("/", fs)
	http.HandleFunc("/message", wordHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Fix typo<commit_after>package main

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

	"github.com/streadway/amqp"
)

// Bind a queue to the exchange to listen for messages
// When we publish a message, it will be sent to this queue, via the exchange
var routingKey = "words"
var exchangeName = "grandtour"
var connection *amqp.Connection
var channel *amqp.Channel
var queue amqp.Queue

func wordHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		// Use synchronous Get to get a single message from
		// the queue. In real life, prefer to Consume.
		msg, ok, err := channel.Get(queue.Name, true)
		if err != nil {
			log.Fatal(err)
		}

		// Was there something waiting?
		if ok {
			// Write the retrieved payload straight into the body
			w.Write(msg.Body)
		} else {
			// Or if nothing was retrieved, a message reflecting that
			w.Write([]byte("No message waiting"))
		}
		return
	case "PUT":
		r.ParseForm()
		// Compose a message comprising of the form message and the time
		// now

		msg := r.Form.Get("message") + " " + time.Now().Format("15:04:05.00")

		// Publish the message to the exchange with our routing key
		err := channel.Publish(exchangeName,
			routingKey,
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg),
			})
		if err != nil {
			log.Fatal(err)
		}
		// Set the response to accepted
		w.WriteHeader(http.StatusAccepted)
		// and rite the message that was sent back to the user
		w.Write([]byte(msg))
		return
	}

	return
}

func main() {
	// Connect to database:
	// Connection string in $COMPOSE_RABBITMQ_URL
	var err error

	// The library handles amqps connections and sets Servername correctly
	// so we can just connect here with the Dial() function
	connection, err = amqp.Dial(os.Getenv("COMPOSE_RABBITMQ_URL"))
	if err != nil {
		log.Fatal(err)
	}
	defer connection.Close()

	// We create a channel to communicate over. Note, if there is an
	// error on the channel, we would need to recreate the channel.
	channel, err = connection.Channel()
	if err != nil {
		log.Fatal(err)
	}

	// We can now create a durable exchange where we'll post our messages
	err = channel.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// And we'll declare a transient queue for our client
	queue, err = channel.QueueDeclare("", false, false, false, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Now we'll bind out transient queue to recieve messages with our
	// routing key
	err = channel.QueueBind(queue.Name, routingKey, exchangeName, false, nil)
	if err != nil {
		log.Fatal(err)
	}

	// With setup done we begin web serving
	fs := http.FileServer(http.Dir("public"))
	http.Handle("/", fs)
	http.HandleFunc("/message", wordHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"strings"
	"strconv"
)

type Iface struct {
	IPv4    string
	CIDRv4  string
	Netmask int
	MAC     string
	Name    string
}

func IsCIDRv4(cidr string) bool {
	i, _, err := net.ParseCIDR(cidr)
	if err != nil {
		return false
	}
	if i.To4() == nil {
		return false
	}
	return true
}

func GetIPv4Netmask(cidr string) (ip string, mask int) {
	split := strings.Split(cidr, "/")

	ip = split[0]
	mask, _ = strconv.Atoi(split[1])
	return
}

func LocalIfaces() []Iface {
	var ifaces []Iface
	var iface Iface
	var addrs []net.Addr
	var err error

	interfaces, _ := net.Interfaces()

	for _, i := range interfaces {

		addrs, err = i.Addrs()
		if err != nil {
			continue
		}
		iface.Name = i.Name
		iface.MAC = i.HardwareAddr.String()
		for _, a := range addrs {
			if IsCIDRv4(a.String()) {
				iface.CIDRv4 = a.String()
				iface.IPv4, iface.Netmask =
					GetIPv4Netmask(a.String())
			}
		}
		ifaces = append(ifaces, iface)
	}
	return ifaces
}
<commit_msg>Reflect json<commit_after>package main

import (
	"net"
	"strings"
	"strconv"
)

type Iface struct {
	IPv4    string
	CIDRv4  string
	Netmask int        `json:"netmask"`
	MAC     string
	Name    string     `json:"name"`
}

func IsCIDRv4(cidr string) bool {
	i, _, err := net.ParseCIDR(cidr)
	if err != nil {
		return false
	}
	if i.To4() == nil {
		return false
	}
	return true
}

func GetIPv4Netmask(cidr string) (ip string, mask int) {
	split := strings.Split(cidr, "/")

	ip = split[0]
	mask, _ = strconv.Atoi(split[1])
	return
}

func LocalIfaces() []Iface {
	var ifaces []Iface
	var iface Iface
	var addrs []net.Addr
	var err error

	interfaces, _ := net.Interfaces()

	for _, i := range interfaces {

		addrs, err = i.Addrs()
		if err != nil {
			continue
		}
		iface.Name = i.Name
		iface.MAC = i.HardwareAddr.String()
		for _, a := range addrs {
			if IsCIDRv4(a.String()) {
				iface.CIDRv4 = a.String()
				iface.IPv4, iface.Netmask =
					GetIPv4Netmask(a.String())
			}
		}
		ifaces = append(ifaces, iface)
	}
	return ifaces
}
<|endoftext|>


<commit_before>package mains

import (
	"bufio"
	"fmt"
	"io"
	"os"

	"github.com/yuin/gopher-lua"
)

type ioLuaReader struct {
	scanner *bufio.Scanner
	closer  io.Closer
}

func ioLinesIter(L *lua.LState) int {
	ud, ok := L.Get(1).(*lua.LUserData)
	if !ok {
		L.Push(lua.LNil)
		return 1
	}
	r, ok := ud.Value.(*ioLuaReader)
	if !ok {
		L.Push(lua.LNil)
		return 1
	}
	if r.scanner.Scan() {
		L.Push(lua.LString(r.scanner.Text()))
		return 1
	}
	L.Push(lua.LNil)
	if r.closer != nil {
		r.closer.Close()
		r.closer = nil
	}
	return 1
}

func ioLines(L *lua.LState) int {
	ud := L.NewUserData()
	_, sh := getRegInt(L)
	if L.GetTop() >= 1 {
		if filename, ok := L.Get(1).(lua.LString); ok {
			if fd, err := os.Open(string(filename)); err == nil {
				ud.Value = &ioLuaReader{
					scanner: bufio.NewScanner(fd),
					closer:  fd,
				}
			} else {
				L.Push(lua.LNil)
				L.Push(lua.LString(fmt.Sprintf("%s: can not open", filename)))
				return 2
			}
		} else {
			L.Push(lua.LNil)
			L.Push(lua.LString("io.lines: not a string"))
			return 2
		}
	} else if sh != nil {
		ud.Value = &ioLuaReader{
			scanner: bufio.NewScanner(sh.In()),
			closer:  nil,
		}
	} else {
		ud.Value = &ioLuaReader{
			scanner: bufio.NewScanner(os.Stdin),
			closer:  nil,
		}
	}
	L.Push(L.NewFunction(ioLinesIter))
	L.Push(ud)
	L.Push(lua.LNil)
	return 3
}

func ioWrite(L *lua.LState) int {
	_, sh := getRegInt(L)
	out := sh.Out()
	for i, end := 1, L.GetTop(); i <= end; i++ {
		fmt.Fprint(out, L.Get(i).String())
	}
	return 0
}
<commit_msg>( mains/io.go: bufio.Scanner -> Reader for reading by rune after )<commit_after>package mains

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/yuin/gopher-lua"
)

type ioLuaReader struct {
	reader *bufio.Reader
	closer io.Closer
}

func ioLinesIter(L *lua.LState) int {
	ud, ok := L.Get(1).(*lua.LUserData)
	if !ok {
		L.Push(lua.LNil)
		return 1
	}
	r, ok := ud.Value.(*ioLuaReader)
	if !ok {
		L.Push(lua.LNil)
		return 1
	}
	if text, err := r.reader.ReadString('\n'); err == nil {
		L.Push(lua.LString(strings.TrimSuffix(text, "\n")))
	} else {
		L.Push(lua.LNil)
		if r.closer != nil {
			r.closer.Close()
			r.closer = nil
		}
	}
	return 1
}

func ioLines(L *lua.LState) int {
	ud := L.NewUserData()
	_, sh := getRegInt(L)
	if L.GetTop() >= 1 {
		if filename, ok := L.Get(1).(lua.LString); ok {
			if fd, err := os.Open(string(filename)); err == nil {
				ud.Value = &ioLuaReader{
					reader: bufio.NewReader(fd),
					closer: fd,
				}
			} else {
				L.Push(lua.LNil)
				L.Push(lua.LString(fmt.Sprintf("%s: can not open", filename)))
				return 2
			}
		} else {
			L.Push(lua.LNil)
			L.Push(lua.LString("io.lines: not a string"))
			return 2
		}
	} else if sh != nil {
		ud.Value = &ioLuaReader{
			reader: bufio.NewReader(sh.In()),
			closer: nil,
		}
	} else {
		ud.Value = &ioLuaReader{
			reader: bufio.NewReader(os.Stdin),
			closer: nil,
		}
	}
	L.Push(L.NewFunction(ioLinesIter))
	L.Push(ud)
	L.Push(lua.LNil)
	return 3
}

func ioWrite(L *lua.LState) int {
	_, sh := getRegInt(L)
	out := sh.Out()
	for i, end := 1, L.GetTop(); i <= end; i++ {
		fmt.Fprint(out, L.Get(i).String())
	}
	return 0
}
<|endoftext|>


<commit_before>package v2

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/handlers"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
)

type HoverflyLogs interface {
	GetLogs(limit int) []*logrus.Entry
}

type LogsHandler struct {
	Hoverfly HoverflyLogs
}

var DefaultLimit = 500

func (this *LogsHandler) RegisterRoutes(mux *bone.Mux, am *handlers.AuthHandler) {
	mux.Get("/api/v2/logs", negroni.New(
		negroni.HandlerFunc(am.RequireTokenAuthentication),
		negroni.HandlerFunc(this.Get),
	))

	mux.Get("/api/v2/ws/logs", http.HandlerFunc(this.GetWS))
}

func (this *LogsHandler) Get(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	var logs []*logrus.Entry

	queryParams := req.URL.Query()
	limitQuery, _ := strconv.Atoi(queryParams.Get("limit"))
	if limitQuery == 0 {
		limitQuery = DefaultLimit
	}

	logs = this.Hoverfly.GetLogs(limitQuery)

	if strings.Contains(req.Header.Get("Accept"), "text/plain") ||
		strings.Contains(req.Header.Get("Content-Type"), "text/plain") {
		handlers.WriteResponse(w, []byte(logsToPlainText(logs)))
	} else {
		bytes, _ := json.Marshal(logsToLogsView(logs))
		handlers.WriteResponse(w, bytes)
	}
}

func logsToLogsView(logs []*logrus.Entry) LogsView {
	var logInterfaces []map[string]interface{}
	for _, entry := range logs {
		data := make(map[string]interface{}, len(entry.Data)+3)

		for k, v := range entry.Data {
			data[k] = v
		}

		data["time"] = entry.Time.Format(logrus.DefaultTimestampFormat)
		data["msg"] = entry.Message
		data["level"] = entry.Level.String()

		logInterfaces = append(logInterfaces, data)
	}

	return LogsView{
		Logs: logInterfaces,
	}
}

func logsToPlainText(logs []*logrus.Entry) string {

	var buffer bytes.Buffer
	for _, entry := range logs {
		entry.Logger = logrus.New()
		log, err := entry.String()
		if err == nil {
			buffer.WriteString(log)
		}
	}

	return buffer.String()
}

func (this *LogsHandler) GetWS(w http.ResponseWriter, r *http.Request) {

	var previousLogs LogsView

	handlers.NewWebsocket(func() ([]byte, error) {
		currentLogs := logsToLogsView(this.Hoverfly.GetLogs(500))

		if !reflect.DeepEqual(currentLogs, previousLogs) {
			previousLogs = currentLogs
			return json.Marshal(currentLogs)
		}

		return nil, errors.New("No update needed")
	}, w, r)
}
<commit_msg>Fixed a bug with plain text logs from the logs API<commit_after>package v2

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/handlers"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
)

type HoverflyLogs interface {
	GetLogs(limit int) []*logrus.Entry
}

type LogsHandler struct {
	Hoverfly HoverflyLogs
}

var DefaultLimit = 500

func (this *LogsHandler) RegisterRoutes(mux *bone.Mux, am *handlers.AuthHandler) {
	mux.Get("/api/v2/logs", negroni.New(
		negroni.HandlerFunc(am.RequireTokenAuthentication),
		negroni.HandlerFunc(this.Get),
	))

	mux.Get("/api/v2/ws/logs", http.HandlerFunc(this.GetWS))
}

func (this *LogsHandler) Get(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	var logs []*logrus.Entry

	queryParams := req.URL.Query()
	limitQuery, _ := strconv.Atoi(queryParams.Get("limit"))
	if limitQuery == 0 {
		limitQuery = DefaultLimit
	}

	logs = this.Hoverfly.GetLogs(limitQuery)

	if strings.Contains(req.Header.Get("Accept"), "text/plain") ||
		strings.Contains(req.Header.Get("Content-Type"), "text/plain") {
		handlers.WriteResponse(w, []byte(logsToPlainText(logs)))
	} else {
		bytes, _ := json.Marshal(logsToLogsView(logs))
		handlers.WriteResponse(w, bytes)
	}
}

func logsToLogsView(logs []*logrus.Entry) LogsView {
	var logInterfaces []map[string]interface{}
	for _, entry := range logs {
		data := make(map[string]interface{}, len(entry.Data)+3)

		for k, v := range entry.Data {
			data[k] = v
		}

		data["time"] = entry.Time.Format(logrus.DefaultTimestampFormat)
		data["msg"] = entry.Message
		data["level"] = entry.Level.String()

		logInterfaces = append(logInterfaces, data)
	}

	return LogsView{
		Logs: logInterfaces,
	}
}

func logsToPlainText(logs []*logrus.Entry) string {

	var buffer bytes.Buffer
	for _, entry := range logs {
		entry.Logger = logrus.New()
		entry.Logger.Formatter = &logrus.TextFormatter{}

		log, err := entry.String()
		if err == nil {
			buffer.WriteString(log)
		}
	}

	return buffer.String()
}

func (this *LogsHandler) GetWS(w http.ResponseWriter, r *http.Request) {

	var previousLogs LogsView

	handlers.NewWebsocket(func() ([]byte, error) {
		currentLogs := logsToLogsView(this.Hoverfly.GetLogs(500))

		if !reflect.DeepEqual(currentLogs, previousLogs) {
			previousLogs = currentLogs
			return json.Marshal(currentLogs)
		}

		return nil, errors.New("No update needed")
	}, w, r)
}
<|endoftext|>


<commit_before>package sawyer

import (
	"github.com/lostisland/go-sawyer/mediatype"
	"io/ioutil"
	"net/http"
	"net/url"
)

// Request is a wrapped net/http Request with a pointer to the net/http Client,
// MediaType, parsed URI query, and the configured Cacher.  Requests are capable
// of returning a sawyer Response with Do() or the HTTP verb helpers (Get(),
// Head(), Post(), etc).
type Request struct {
	Client    *http.Client
	MediaType *mediatype.MediaType
	Query     url.Values
	Cacher    Cacher
	*http.Request
}

// NewRequest creates a new sawyer.Request for the given relative url path, with
// any default headers or query parameters specified on Client.  The Request URL
// is resolved to an absolute URL.
func (c *Client) NewRequest(rawurl string) (*Request, error) {
	httpreq, err := buildRequest(c, rawurl)
	if httpreq == nil {
		return nil, err
	}

	return &Request{c.HttpClient, nil, httpreq.URL.Query(), c.Cacher, httpreq}, err
}

// Do completes the HTTP request, returning a response.  The Request's Cacher is
// used to return a cached response if available.  Otherwise, the request goes
// through and fills the cache for future requests.
func (r *Request) Do(method string) *Response {
	r.URL.RawQuery = r.Query.Encode()
	r.Method = method

	cacher := r.Cacher
	useCache, clearCache := r.cacherSwitch()
	if !useCache {
		cacher = noOpCacher
	}

	cached, cachedErr := cacher.Get(r.Request)
	if cachedErr == nil {
		if cached.IsFresh() {
			return cached.Decode(r)
		} else {
			cached.SetupRequest(r.Request)
		}
	}

	httpres, err := r.Client.Do(r.Request)
	if err != nil {
		return ResponseError(err)
	}

	if cachedErr == nil && !clearCache && httpres.StatusCode == 304 {
		cacher.UpdateCache(r.Request, httpres)
		return cached.Decode(r)
	}

	mtype, err := mediaType(httpres)
	if err != nil {
		httpres.Body.Close()
		return ResponseError(err)
	}

	res := &Response{
		MediaType:  mtype,
		BodyClosed: false,
		Response:   httpres,
		Cacher:     cacher,
		isApiError: UseApiError(httpres.StatusCode),
	}

	if !res.AnyError() {
		if clearCache {
			r.Cacher.Reset(r.Request)
		} else {
			cacher.Set(r.Request, res)
		}
	}

	return res
}

// Head is a helper method for Do().
func (r *Request) Head() *Response {
	return r.Do(HeadMethod)
}

// Get is a helper method for Do().
func (r *Request) Get() *Response {
	return r.Do(GetMethod)
}

// Post is a helper method for Do().
func (r *Request) Post() *Response {
	return r.Do(PostMethod)
}

// Put is a helper method for Do().
func (r *Request) Put() *Response {
	return r.Do(PutMethod)
}

// Patch is a helper method for Do().
func (r *Request) Patch() *Response {
	return r.Do(PatchMethod)
}

// Delete is a helper method for Do().
func (r *Request) Delete() *Response {
	return r.Do(DeleteMethod)
}

// Options is a helper method for Do().
func (r *Request) Options() *Response {
	return r.Do(OptionsMethod)
}

// SetBody encodes and sets the proper headers for the request body from the
// given resource.  The resource is encoded in-memory, so be careful about
// passing a massive object.  You can set the ContentLength and Body properties
// manually.
func (r *Request) SetBody(mtype *mediatype.MediaType, resource interface{}) error {
	r.MediaType = mtype
	r.Header.Set(ctypeHeader, mtype.String())

	if resource == nil {
		return nil
	}

	buf, err := mtype.Encode(resource)
	if err != nil {
		return err
	}

	r.ContentLength = int64(buf.Len())
	r.Body = ioutil.NopCloser(buf)
	return nil
}

func (r *Request) cacherSwitch() (bool, bool) {
	switch r.Method {
	case GetMethod:
		return true, false
	case HeadMethod, OptionsMethod:
		return false, false
	default:
		return false, true
	}
}

const (
	ctypeHeader   = "Content-Type"
	HeadMethod    = "HEAD"
	GetMethod     = "GET"
	PostMethod    = "POST"
	PutMethod     = "PUT"
	PatchMethod   = "PATCH"
	DeleteMethod  = "DELETE"
	OptionsMethod = "OPTIONS"
)
<commit_msg>these constants are better than lots of bools<commit_after>package sawyer

import (
	"github.com/lostisland/go-sawyer/mediatype"
	"io/ioutil"
	"net/http"
	"net/url"
)

// Request is a wrapped net/http Request with a pointer to the net/http Client,
// MediaType, parsed URI query, and the configured Cacher.  Requests are capable
// of returning a sawyer Response with Do() or the HTTP verb helpers (Get(),
// Head(), Post(), etc).
type Request struct {
	Client    *http.Client
	MediaType *mediatype.MediaType
	Query     url.Values
	Cacher    Cacher
	*http.Request
}

// NewRequest creates a new sawyer.Request for the given relative url path, with
// any default headers or query parameters specified on Client.  The Request URL
// is resolved to an absolute URL.
func (c *Client) NewRequest(rawurl string) (*Request, error) {
	httpreq, err := buildRequest(c, rawurl)
	if httpreq == nil {
		return nil, err
	}

	return &Request{c.HttpClient, nil, httpreq.URL.Query(), c.Cacher, httpreq}, err
}

// Do completes the HTTP request, returning a response.  The Request's Cacher is
// used to return a cached response if available.  Otherwise, the request goes
// through and fills the cache for future requests.
func (r *Request) Do(method string) *Response {
	r.URL.RawQuery = r.Query.Encode()
	r.Method = method

	cacher := r.Cacher
	cacheBehavior := r.cacherBehavior()
	if cacheBehavior != useCache {
		cacher = noOpCacher
	}

	cached, cachedErr := cacher.Get(r.Request)
	if cachedErr == nil {
		if cached.IsFresh() {
			return cached.Decode(r)
		} else {
			cached.SetupRequest(r.Request)
		}
	}

	httpres, err := r.Client.Do(r.Request)
	if err != nil {
		return ResponseError(err)
	}

	if cachedErr == nil && cacheBehavior == useCache && httpres.StatusCode == 304 {
		cacher.UpdateCache(r.Request, httpres)
		return cached.Decode(r)
	}

	mtype, err := mediaType(httpres)
	if err != nil {
		httpres.Body.Close()
		return ResponseError(err)
	}

	res := &Response{
		MediaType:  mtype,
		BodyClosed: false,
		Response:   httpres,
		Cacher:     cacher,
		isApiError: UseApiError(httpres.StatusCode),
	}

	if !res.AnyError() {
		if cacheBehavior == resetCache {
			r.Cacher.Reset(r.Request)
		} else {
			cacher.Set(r.Request, res)
		}
	}

	return res
}

// Head is a helper method for Do().
func (r *Request) Head() *Response {
	return r.Do(HeadMethod)
}

// Get is a helper method for Do().
func (r *Request) Get() *Response {
	return r.Do(GetMethod)
}

// Post is a helper method for Do().
func (r *Request) Post() *Response {
	return r.Do(PostMethod)
}

// Put is a helper method for Do().
func (r *Request) Put() *Response {
	return r.Do(PutMethod)
}

// Patch is a helper method for Do().
func (r *Request) Patch() *Response {
	return r.Do(PatchMethod)
}

// Delete is a helper method for Do().
func (r *Request) Delete() *Response {
	return r.Do(DeleteMethod)
}

// Options is a helper method for Do().
func (r *Request) Options() *Response {
	return r.Do(OptionsMethod)
}

// SetBody encodes and sets the proper headers for the request body from the
// given resource.  The resource is encoded in-memory, so be careful about
// passing a massive object.  You can set the ContentLength and Body properties
// manually.
func (r *Request) SetBody(mtype *mediatype.MediaType, resource interface{}) error {
	r.MediaType = mtype
	r.Header.Set(ctypeHeader, mtype.String())

	if resource == nil {
		return nil
	}

	buf, err := mtype.Encode(resource)
	if err != nil {
		return err
	}

	r.ContentLength = int64(buf.Len())
	r.Body = ioutil.NopCloser(buf)
	return nil
}

func (r *Request) cacherBehavior() int {
	switch r.Method {
	case GetMethod:
		return useCache
	case HeadMethod, OptionsMethod:
		return noCache
	//case DeleteMethod:
	//	return clearCache
	default:
		return resetCache
	}
}

const (
	noCache    = iota
	useCache   = iota
	resetCache = iota
	clearCache = iota
)

const (
	ctypeHeader   = "Content-Type"
	HeadMethod    = "HEAD"
	GetMethod     = "GET"
	PostMethod    = "POST"
	PutMethod     = "PUT"
	PatchMethod   = "PATCH"
	DeleteMethod  = "DELETE"
	OptionsMethod = "OPTIONS"
)
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"math/rand"
	"net"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

type tcpipRequestPayload struct {
	Address string
	Port    uint32
}

func (payload tcpipRequestPayload) String() string {
	return net.JoinHostPort(payload.Address, fmt.Sprint(payload.Port))
}

func handleGlobalRequests(requests <-chan *ssh.Request, conn ssh.ConnMetadata) {
	for request := range requests {
		var requestPayload interface{}
		switch request.Type {
		case "tcpip-forward":
			fallthrough
		case "cancel-tcpip-forward":
			requestPayload = new(tcpipRequestPayload)
		default:
			log.Println("Unsupported global request type", request.Type)
			continue
		}
		requestPayloadString := ""
		if requestPayload != nil {
			if err := ssh.Unmarshal(request.Payload, requestPayload); err != nil {
				log.Println("Failed to parse request payload", err)
				continue
			}

			requestPayloadString = fmt.Sprint(requestPayload)
		}

		if request.WantReply {
			var response []byte
			if request.Type == "tcpip-forward" && requestPayload.(*tcpipRequestPayload).Port == 0 {
				response = ssh.Marshal(struct{ port uint32 }{uint32(rand.Intn(65536-1024) + 1024)})
			}
			if err := request.Reply(true, response); err != nil {
				log.Println("Failed to accept global request:", err)
				continue
			}
		}

		getLogEntry(conn).WithFields(logrus.Fields{
			"request_payload":    requestPayloadString,
			"request_type":       request.Type,
			"request_want_reply": request.WantReply,
		}).Infoln("Global request accepted")
	}
}

type ptyRequestPayload struct {
	Term                                   string
	Width, Height, PixelWidth, PixelHeight uint32
	Modes                                  string
}

func (payload ptyRequestPayload) String() string {
	return fmt.Sprintf("Term: %v, Size: %vx%v (%vx%v px), Modes: %v", payload.Term, payload.Width, payload.Height, payload.PixelWidth, payload.PixelHeight, base64.RawStdEncoding.EncodeToString([]byte(payload.Modes)))
}

type x11RequestPayload struct {
	SingleConnection         bool
	AuthProtocol, AuthCookie string
	ScreenNumber             uint32
}

func (payload x11RequestPayload) String() string {
	return fmt.Sprintf("Single connection: %v, Auth protocol: %v, Auth cookie: %v, Screen number: %v", payload.SingleConnection, payload.AuthProtocol, payload.AuthCookie, payload.ScreenNumber)
}

type envRequestPayload struct {
	Name, Value string
}

func (payload envRequestPayload) String() string {
	return fmt.Sprintf("%v=%v", payload.Name, payload.Value)
}

type execRequestPayload struct {
	Command string
}

func (payload execRequestPayload) String() string {
	return payload.Command
}

type subsystemRequestPayload struct {
	Subsystem string
}

func (payload subsystemRequestPayload) String() string {
	return payload.Subsystem
}

type windowChangeRequestPayload struct {
	Width, Height, PixelWidth, PixelHeight uint32
}

func (payload windowChangeRequestPayload) String() string {
	return fmt.Sprintf("%vx%v (%vx%v px)", payload.Width, payload.Height, payload.PixelWidth, payload.PixelHeight)
}

type flowControlRequestPayload struct {
	Enabled bool
}

func (payload flowControlRequestPayload) String() string {
	return fmt.Sprintf("Enabled: %v", payload.Enabled)
}

type signalRequestPayload struct {
	Signal string
}

func (payload signalRequestPayload) String() string {
	return payload.Signal
}

type exitStatusRequestPayload struct {
	ExitStatus uint32
}

func (payload exitStatusRequestPayload) String() string {
	return fmt.Sprint(payload.ExitStatus)
}

type exitSignalRequestPayload struct {
	Signal     string
	CoreDumped bool
	Error      string
	Language   string
}

func (payload exitSignalRequestPayload) String() string {
	return fmt.Sprintf("Signal: %v, Core dumped: %v, Error: %v, Language: %v", payload.Signal, payload.CoreDumped, payload.Error, payload.Language)
}

func handleChannelRequests(requests <-chan *ssh.Request, conn channelMetadata) {
	for request := range requests {
		var requestPayload interface{}
		switch request.Type {
		case "pty-req":
			requestPayload = new(ptyRequestPayload)
		case "x11-req":
			requestPayload = new(x11RequestPayload)
		case "env":
			requestPayload = new(envRequestPayload)
		case "shell":
		case "exec":
			requestPayload = new(execRequestPayload)
		case "subsystem":
			requestPayload = new(subsystemRequestPayload)
		case "window-change":
			requestPayload = new(windowChangeRequestPayload)
		case "xon-xoff":
			requestPayload = new(flowControlRequestPayload)
		case "signal":
			requestPayload = new(signalRequestPayload)
		case "exit-status":
			requestPayload = new(exitStatusRequestPayload)
		case "exit-signal":
			requestPayload = new(exitSignalRequestPayload)
		default:
			log.Println("Unsupported channel request type", request.Type)
			continue
		}
		requestPayloadString := ""
		if requestPayload != nil {
			if err := ssh.Unmarshal(request.Payload, requestPayload); err != nil {
				log.Println("Failed to parse request payload", err)
				continue
			}

			requestPayloadString = fmt.Sprint(requestPayload)
		}

		if request.WantReply {
			if err := request.Reply(true, nil); err != nil {
				log.Println("Failed to accept channel request:", err)
				continue
			}
		}

		conn.getLogEntry().WithFields(logrus.Fields{
			"request_payload":    requestPayloadString,
			"request_type":       request.Type,
			"request_want_reply": request.WantReply,
		}).Infoln("Channel request accepted")
	}
}
<commit_msg>Reply to (reject) unsupported requests too<commit_after>package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"math/rand"
	"net"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

type tcpipRequestPayload struct {
	Address string
	Port    uint32
}

func (payload tcpipRequestPayload) String() string {
	return net.JoinHostPort(payload.Address, fmt.Sprint(payload.Port))
}

func handleGlobalRequests(requests <-chan *ssh.Request, conn ssh.ConnMetadata) {
	for request := range requests {
		var requestPayload interface{}
		accept := true
		switch request.Type {
		case "tcpip-forward":
			fallthrough
		case "cancel-tcpip-forward":
			requestPayload = new(tcpipRequestPayload)
		default:
			log.Println("Unsupported global request type", request.Type)
			accept = false
		}
		requestPayloadString := ""
		if requestPayload != nil {
			if err := ssh.Unmarshal(request.Payload, requestPayload); err != nil {
				log.Println("Failed to parse request payload", err)
				continue
			}

			requestPayloadString = fmt.Sprint(requestPayload)
		}

		if request.WantReply {
			var response []byte
			if request.Type == "tcpip-forward" && requestPayload.(*tcpipRequestPayload).Port == 0 {
				response = ssh.Marshal(struct{ port uint32 }{uint32(rand.Intn(65536-1024) + 1024)})
			}
			if err := request.Reply(accept, response); err != nil {
				log.Println("Failed to reply to global request:", err)
				continue
			}
		}

		getLogEntry(conn).WithFields(logrus.Fields{
			"request_payload":    requestPayloadString,
			"request_type":       request.Type,
			"request_want_reply": request.WantReply,
			"accepted":           accept,
		}).Infoln("Global request received")
	}
}

type ptyRequestPayload struct {
	Term                                   string
	Width, Height, PixelWidth, PixelHeight uint32
	Modes                                  string
}

func (payload ptyRequestPayload) String() string {
	return fmt.Sprintf("Term: %v, Size: %vx%v (%vx%v px), Modes: %v", payload.Term, payload.Width, payload.Height, payload.PixelWidth, payload.PixelHeight, base64.RawStdEncoding.EncodeToString([]byte(payload.Modes)))
}

type x11RequestPayload struct {
	SingleConnection         bool
	AuthProtocol, AuthCookie string
	ScreenNumber             uint32
}

func (payload x11RequestPayload) String() string {
	return fmt.Sprintf("Single connection: %v, Auth protocol: %v, Auth cookie: %v, Screen number: %v", payload.SingleConnection, payload.AuthProtocol, payload.AuthCookie, payload.ScreenNumber)
}

type envRequestPayload struct {
	Name, Value string
}

func (payload envRequestPayload) String() string {
	return fmt.Sprintf("%v=%v", payload.Name, payload.Value)
}

type execRequestPayload struct {
	Command string
}

func (payload execRequestPayload) String() string {
	return payload.Command
}

type subsystemRequestPayload struct {
	Subsystem string
}

func (payload subsystemRequestPayload) String() string {
	return payload.Subsystem
}

type windowChangeRequestPayload struct {
	Width, Height, PixelWidth, PixelHeight uint32
}

func (payload windowChangeRequestPayload) String() string {
	return fmt.Sprintf("%vx%v (%vx%v px)", payload.Width, payload.Height, payload.PixelWidth, payload.PixelHeight)
}

type flowControlRequestPayload struct {
	Enabled bool
}

func (payload flowControlRequestPayload) String() string {
	return fmt.Sprintf("Enabled: %v", payload.Enabled)
}

type signalRequestPayload struct {
	Signal string
}

func (payload signalRequestPayload) String() string {
	return payload.Signal
}

type exitStatusRequestPayload struct {
	ExitStatus uint32
}

func (payload exitStatusRequestPayload) String() string {
	return fmt.Sprint(payload.ExitStatus)
}

type exitSignalRequestPayload struct {
	Signal     string
	CoreDumped bool
	Error      string
	Language   string
}

func (payload exitSignalRequestPayload) String() string {
	return fmt.Sprintf("Signal: %v, Core dumped: %v, Error: %v, Language: %v", payload.Signal, payload.CoreDumped, payload.Error, payload.Language)
}

func handleChannelRequests(requests <-chan *ssh.Request, conn channelMetadata) {
	for request := range requests {
		var requestPayload interface{}
		accept := true
		switch request.Type {
		case "pty-req":
			requestPayload = new(ptyRequestPayload)
		case "x11-req":
			requestPayload = new(x11RequestPayload)
		case "env":
			requestPayload = new(envRequestPayload)
		case "shell":
		case "exec":
			requestPayload = new(execRequestPayload)
		case "subsystem":
			requestPayload = new(subsystemRequestPayload)
		case "window-change":
			requestPayload = new(windowChangeRequestPayload)
		case "xon-xoff":
			requestPayload = new(flowControlRequestPayload)
		case "signal":
			requestPayload = new(signalRequestPayload)
		case "exit-status":
			requestPayload = new(exitStatusRequestPayload)
		case "exit-signal":
			requestPayload = new(exitSignalRequestPayload)
		default:
			log.Println("Unsupported channel request type", request.Type)
			accept = false
		}
		requestPayloadString := ""
		if requestPayload != nil {
			if err := ssh.Unmarshal(request.Payload, requestPayload); err != nil {
				log.Println("Failed to parse request payload", err)
				continue
			}

			requestPayloadString = fmt.Sprint(requestPayload)
		}

		if request.WantReply {
			if err := request.Reply(accept, nil); err != nil {
				log.Println("Failed to reply to channel request:", err)
				continue
			}
		}

		conn.getLogEntry().WithFields(logrus.Fields{
			"request_payload":    requestPayloadString,
			"request_type":       request.Type,
			"request_want_reply": request.WantReply,
			"accepted":           accept,
		}).Infoln("Channel request received")
	}
}
<|endoftext|>


<commit_before>package integration_test

import (
	"encoding/json"
	"time"

	. "github.com/cloudfoundry-incubator/route-emitter/routing_table/matchers"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gibson"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Integration", func() {
	listenForRoutes := func(subject string) <-chan gibson.RegistryMessage {
		routes := make(chan gibson.RegistryMessage)

		natsRunner.MessageBus.Subscribe(subject, func(msg *yagnats.Message) {
			defer GinkgoRecover()

			var message gibson.RegistryMessage
			err := json.Unmarshal(msg.Payload, &message)
			Ω(err).ShouldNot(HaveOccurred())

			routes <- message
		})

		return routes
	}

	var (
		registeredRoutes   <-chan gibson.RegistryMessage
		unregisteredRoutes <-chan gibson.RegistryMessage
	)

	BeforeEach(func() {
		registeredRoutes = listenForRoutes("router.register")
		unregisteredRoutes = listenForRoutes("router.unregister")

		natsRunner.MessageBus.Subscribe("router.greet", func(msg *yagnats.Message) {
			defer GinkgoRecover()

			greeting := gibson.RouterGreetingMessage{
				MinimumRegisterInterval: 2,
			}

			response, err := json.Marshal(greeting)
			Ω(err).ShouldNot(HaveOccurred())

			err = natsRunner.MessageBus.Publish(msg.ReplyTo, response)
			Ω(err).ShouldNot(HaveOccurred())
		})
	})

	AfterEach(func() {
		Ω(runner.Session).ShouldNot(gexec.Exit(), "Runner should not have exploded!")
	})

	Context("when the emitter is running", func() {
		BeforeEach(func() {
			runner.Start()
		})

		Context("and routes are desired", func() {
			BeforeEach(func() {
				err := bbs.DesireLRP(models.DesiredLRP{
					Domain:      "tests",
					ProcessGuid: "guid1",
					Routes:      []string{"route-1", "route-2"},
					Instances:   5,
					Stack:       "some-stack",
					MemoryMB:    1024,
					DiskMB:      512,
					Actions: []models.ExecutorAction{
						models.ExecutorAction{
							models.RunAction{
								Path: "ls",
							},
						},
					},
				})
				Ω(err).ShouldNot(HaveOccurred())
			})

			Context("and an endpoint comes up", func() {
				BeforeEach(func() {
					err := bbs.ReportActualLRPAsRunning(models.ActualLRP{
						ProcessGuid:  "guid1",
						Index:        0,
						InstanceGuid: "iguid1",

						Host: "1.2.3.4",
						Ports: []models.PortMapping{
							{ContainerPort: 8080, HostPort: 65100},
						},
					}, "executor-id")
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("emits its routes immediately", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})
		})

		Context("and an instance starts running", func() {
			BeforeEach(func() {
				err := bbs.DesireLRP(models.DesiredLRP{
					Domain:      "tests",
					ProcessGuid: "guid1",
					Routes:      []string{"route-1", "route-2"},
					Instances:   5,
					Stack:       "some-stack",
					MemoryMB:    1024,
					DiskMB:      512,
					Actions: []models.ExecutorAction{
						models.ExecutorAction{
							models.RunAction{
								Path: "ls",
							},
						},
					},
				})
				Ω(err).ShouldNot(HaveOccurred())

				err = bbs.ReportActualLRPAsStarting(models.ActualLRP{
					ProcessGuid:  "guid1",
					Index:        0,
					InstanceGuid: "iguid1",
				}, "executor-id")
				Ω(err).ShouldNot(HaveOccurred())

			})

			It("does not emit routes", func() {
				Consistently(registeredRoutes).ShouldNot(Receive())
			})
		})

		Context("and an endpoint comes up", func() {
			BeforeEach(func() {
				err := bbs.ReportActualLRPAsRunning(models.ActualLRP{
					ProcessGuid:  "guid1",
					Index:        0,
					InstanceGuid: "iguid1",

					Host: "1.2.3.4",
					Ports: []models.PortMapping{
						{ContainerPort: 8080, HostPort: 65100},
					},
				}, "executor-id")
				Ω(err).ShouldNot(HaveOccurred())
			})

			Context("and a route is desired", func() {
				BeforeEach(func() {
					time.Sleep(100 * time.Millisecond)
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-1", "route-2"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("emits its routes immediately", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})

				It("repeats the route message at the interval given by the router", func() {
					var msg1 gibson.RegistryMessage
					Eventually(registeredRoutes).Should(Receive(&msg1))
					t1 := time.Now()

					var msg2 gibson.RegistryMessage
					Eventually(registeredRoutes, 5).Should(Receive(&msg2))
					t2 := time.Now()

					Ω(msg2).Should(MatchRegistryMessage(msg1))
					Ω(t2.Sub(t1)).Should(BeNumerically("~", 2*time.Second, 500*time.Millisecond))
				})
			})
		})

		Context("and etcd goes away", func() {
			BeforeEach(func() {
				etcdRunner.Stop()
			})

			It("does not explode", func() {
				Consistently(runner.Session, 5).ShouldNot(gexec.Exit())
			})
		})
	})

	Context("when the bbs has routes to emit in /desired and /actual", func() {
		BeforeEach(func() {
			err := bbs.DesireLRP(models.DesiredLRP{
				Domain:      "tests",
				ProcessGuid: "guid1",
				Routes:      []string{"route-1", "route-2"},
				Instances:   5,
				Stack:       "some-stack",
				MemoryMB:    1024,
				DiskMB:      512,
				Actions: []models.ExecutorAction{
					models.ExecutorAction{
						models.RunAction{
							Path: "ls",
						},
					},
				},
			})
			Ω(err).ShouldNot(HaveOccurred())

			err = bbs.ReportActualLRPAsRunning(models.ActualLRP{
				ProcessGuid:  "guid1",
				Index:        0,
				InstanceGuid: "iguid1",

				Host: "1.2.3.4",
				Ports: []models.PortMapping{
					{ContainerPort: 8080, HostPort: 65100},
				},
			}, "executor-id")
			Ω(err).ShouldNot(HaveOccurred())
		})

		Context("and the emitter is started", func() {
			BeforeEach(func() {
				runner.Start()
			})

			It("immediately emits all routes", func() {
				Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
					URIs: []string{"route-1", "route-2"},
					Host: "1.2.3.4",
					Port: 65100,
				})))
			})

			Context("and a route is added", func() {
				BeforeEach(func() {
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-1", "route-2", "route-3"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("immediately emits router.register", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2", "route-3"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})

			Context("and a route is removed", func() {
				BeforeEach(func() {
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-2"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("immediately emits router.unregister", func() {
					Eventually(unregisteredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})
		})
	})
})
<commit_msg>Fix integration test to reflect runtime-schema changes<commit_after>package integration_test

import (
	"encoding/json"
	"time"

	. "github.com/cloudfoundry-incubator/route-emitter/routing_table/matchers"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gibson"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Integration", func() {
	listenForRoutes := func(subject string) <-chan gibson.RegistryMessage {
		routes := make(chan gibson.RegistryMessage)

		natsRunner.MessageBus.Subscribe(subject, func(msg *yagnats.Message) {
			defer GinkgoRecover()

			var message gibson.RegistryMessage
			err := json.Unmarshal(msg.Payload, &message)
			Ω(err).ShouldNot(HaveOccurred())

			routes <- message
		})

		return routes
	}

	var (
		registeredRoutes   <-chan gibson.RegistryMessage
		unregisteredRoutes <-chan gibson.RegistryMessage
	)

	BeforeEach(func() {
		registeredRoutes = listenForRoutes("router.register")
		unregisteredRoutes = listenForRoutes("router.unregister")

		natsRunner.MessageBus.Subscribe("router.greet", func(msg *yagnats.Message) {
			defer GinkgoRecover()

			greeting := gibson.RouterGreetingMessage{
				MinimumRegisterInterval: 2,
			}

			response, err := json.Marshal(greeting)
			Ω(err).ShouldNot(HaveOccurred())

			err = natsRunner.MessageBus.Publish(msg.ReplyTo, response)
			Ω(err).ShouldNot(HaveOccurred())
		})
	})

	AfterEach(func() {
		Ω(runner.Session).ShouldNot(gexec.Exit(), "Runner should not have exploded!")
	})

	Context("when the emitter is running", func() {
		BeforeEach(func() {
			runner.Start()
		})

		Context("and routes are desired", func() {
			BeforeEach(func() {
				err := bbs.DesireLRP(models.DesiredLRP{
					Domain:      "tests",
					ProcessGuid: "guid1",
					Routes:      []string{"route-1", "route-2"},
					Instances:   5,
					Stack:       "some-stack",
					MemoryMB:    1024,
					DiskMB:      512,
					Actions: []models.ExecutorAction{
						models.ExecutorAction{
							models.RunAction{
								Path: "ls",
							},
						},
					},
				})
				Ω(err).ShouldNot(HaveOccurred())
			})

			Context("and an endpoint comes up", func() {
				BeforeEach(func() {
					err := bbs.ReportActualLRPAsRunning(models.ActualLRP{
						ProcessGuid:  "guid1",
						Index:        0,
						InstanceGuid: "iguid1",

						Host: "1.2.3.4",
						Ports: []models.PortMapping{
							{ContainerPort: 8080, HostPort: 65100},
						},
					}, "executor-id")
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("emits its routes immediately", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})
		})

		Context("and an instance starts running", func() {
			BeforeEach(func() {
				err := bbs.DesireLRP(models.DesiredLRP{
					Domain:      "tests",
					ProcessGuid: "guid1",
					Routes:      []string{"route-1", "route-2"},
					Instances:   5,
					Stack:       "some-stack",
					MemoryMB:    1024,
					DiskMB:      512,
					Actions: []models.ExecutorAction{
						models.ExecutorAction{
							models.RunAction{
								Path: "ls",
							},
						},
					},
				})
				Ω(err).ShouldNot(HaveOccurred())

				_, err = bbs.ReportActualLRPAsStarting("guid1", "iguid1", "executor-id", 0)
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("does not emit routes", func() {
				Consistently(registeredRoutes).ShouldNot(Receive())
			})
		})

		Context("and an endpoint comes up", func() {
			BeforeEach(func() {
				err := bbs.ReportActualLRPAsRunning(models.ActualLRP{
					ProcessGuid:  "guid1",
					Index:        0,
					InstanceGuid: "iguid1",

					Host: "1.2.3.4",
					Ports: []models.PortMapping{
						{ContainerPort: 8080, HostPort: 65100},
					},
				}, "executor-id")
				Ω(err).ShouldNot(HaveOccurred())
			})

			Context("and a route is desired", func() {
				BeforeEach(func() {
					time.Sleep(100 * time.Millisecond)
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-1", "route-2"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("emits its routes immediately", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})

				It("repeats the route message at the interval given by the router", func() {
					var msg1 gibson.RegistryMessage
					Eventually(registeredRoutes).Should(Receive(&msg1))
					t1 := time.Now()

					var msg2 gibson.RegistryMessage
					Eventually(registeredRoutes, 5).Should(Receive(&msg2))
					t2 := time.Now()

					Ω(msg2).Should(MatchRegistryMessage(msg1))
					Ω(t2.Sub(t1)).Should(BeNumerically("~", 2*time.Second, 500*time.Millisecond))
				})
			})
		})

		Context("and etcd goes away", func() {
			BeforeEach(func() {
				etcdRunner.Stop()
			})

			It("does not explode", func() {
				Consistently(runner.Session, 5).ShouldNot(gexec.Exit())
			})
		})
	})

	Context("when the bbs has routes to emit in /desired and /actual", func() {
		BeforeEach(func() {
			err := bbs.DesireLRP(models.DesiredLRP{
				Domain:      "tests",
				ProcessGuid: "guid1",
				Routes:      []string{"route-1", "route-2"},
				Instances:   5,
				Stack:       "some-stack",
				MemoryMB:    1024,
				DiskMB:      512,
				Actions: []models.ExecutorAction{
					models.ExecutorAction{
						models.RunAction{
							Path: "ls",
						},
					},
				},
			})
			Ω(err).ShouldNot(HaveOccurred())

			err = bbs.ReportActualLRPAsRunning(models.ActualLRP{
				ProcessGuid:  "guid1",
				Index:        0,
				InstanceGuid: "iguid1",

				Host: "1.2.3.4",
				Ports: []models.PortMapping{
					{ContainerPort: 8080, HostPort: 65100},
				},
			}, "executor-id")
			Ω(err).ShouldNot(HaveOccurred())
		})

		Context("and the emitter is started", func() {
			BeforeEach(func() {
				runner.Start()
			})

			It("immediately emits all routes", func() {
				Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
					URIs: []string{"route-1", "route-2"},
					Host: "1.2.3.4",
					Port: 65100,
				})))
			})

			Context("and a route is added", func() {
				BeforeEach(func() {
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-1", "route-2", "route-3"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("immediately emits router.register", func() {
					Eventually(registeredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1", "route-2", "route-3"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})

			Context("and a route is removed", func() {
				BeforeEach(func() {
					err := bbs.DesireLRP(models.DesiredLRP{
						Domain:      "tests",
						ProcessGuid: "guid1",
						Routes:      []string{"route-2"},
						Instances:   5,
						Stack:       "some-stack",
						MemoryMB:    1024,
						DiskMB:      512,
						Actions: []models.ExecutorAction{
							models.ExecutorAction{
								models.RunAction{
									Path: "ls",
								},
							},
						},
					})
					Ω(err).ShouldNot(HaveOccurred())
				})

				It("immediately emits router.unregister", func() {
					Eventually(unregisteredRoutes).Should(Receive(MatchRegistryMessage(gibson.RegistryMessage{
						URIs: []string{"route-1"},
						Host: "1.2.3.4",
						Port: 65100,
					})))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"flag"
	"os"
	"path/filepath"
	"log"
	"encoding/json"
	"io/ioutil"
)

var manifest []*tar.Header

func visit(path string, f os.FileInfo, err error) error {
	log.Printf("Visited: %s\n", path)
	h, _ := tar.FileInfoHeader(f, path)
	if err != nil {
        log.Printf("Error: %s", err)
		return err;
	}
	h.Name = path
	//log.Println(string(b))
	manifest = append(manifest, h)
	return nil
}

func main() {
	flag.Parse()
	root := flag.Arg(0)
	if root == "" {
		root = "."
	}
	err := filepath.Walk(root, visit)
	if err != nil {
		log.Printf("filepath.Walk() returned %v\n", err)
	}
	m, err := json.Marshal(manifest)
	if err != nil {
        log.Printf("Error: %s", err)
		panic(err)
	}
	log.Printf("Manifest is: %s", m)
	ioutil.WriteFile("manifest.json", []byte(m), 0x755)
}
<commit_msg>Normalize to unix file paths.<commit_after>package main

import (
	"archive/tar"
	"flag"
	"os"
	"path/filepath"
	"log"
	"encoding/json"
	"io/ioutil"
)

var manifest []*tar.Header

func visit(path string, f os.FileInfo, err error) error {
	log.Printf("Visited: %s\n", path)
	h, _ := tar.FileInfoHeader(f, path)
	if err != nil {
        log.Printf("Error: %s", err)
		return err;
	}
	h.Name = filepath.ToSlash(path)
	//log.Println(string(b))
	manifest = append(manifest, h)
	return nil
}

func main() {
	flag.Parse()
	root := flag.Arg(0)
	if root == "" {
		root = "."
	}
	err := filepath.Walk(root, visit)
	if err != nil {
		log.Printf("filepath.Walk() returned %v\n", err)
	}
	m, err := json.Marshal(manifest)
	if err != nil {
        log.Printf("Error: %s", err)
		panic(err)
	}
	log.Printf("Manifest is: %s", m)
	ioutil.WriteFile("manifest.json", []byte(m), 0x755)
}
<|endoftext|>


<commit_before>package rounding

import (
	"math/big"
)

// Wrapper around QuoRem that returns the remainder
func remQuo(x, y, q, r *big.Int) *big.Int {
	q.QuoRem(x, y, r)
	return r
}

// Finite returns true if x has a finite decimal representation.
// x is finite if the Denom can be represented as (2**x) * (5**y).
func Finite(x *big.Rat) bool {
	d := new(big.Int).Set(x.Denom())
	i := new(big.Int)
	m := new(big.Int)

	for {
		switch {
		case d.Cmp(big1) == 0:
			return true
		case remQuo(d, big2, i, m).Sign() == 0:
			d.Set(i)
		case remQuo(d, big5, i, m).Sign() == 0:
			d.Set(i)
		default:
			return false
		}
	}
}

// FinitePrec returns the precision of the finite decimal representation of x.
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FinitePrec(x *big.Rat) int {
	d := x.Denom()
	n := new(big.Int).Set(x.Num())
	m := new(big.Int)

	var i int
	for m.Mod(n, d).Sign() != 0 {
		i++
		n.Mul(n, big10)
	}
	return i
}

// FiniteString returns the equivalent of x.FloatString(FinitePrec(x)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteString(x *big.Rat) string {
	return x.FloatString(FinitePrec(x))
}

// FiniteStringMin returns the equivalent of x.FloatString(max(FinitePrec(x), prec)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteStringMin(x *big.Rat, prec int) string {
	p := FinitePrec(x)
	if p < prec {
		p = prec
	}
	return x.FloatString(p)
}
<commit_msg>(*big.Rat).Denom() is not side effect free<commit_after>package rounding

import (
	"math/big"
)

// Wrapper around QuoRem that returns the remainder
func remQuo(x, y, q, r *big.Int) *big.Int {
	q.QuoRem(x, y, r)
	return r
}

// Finite returns true if x has a finite decimal representation.
// x is finite if the Denom can be represented as (2**x) * (5**y).
func Finite(x *big.Rat) bool {
	// calling x.Denom() can modify x (populates b) so be extra careful
	xx := new(big.Rat).Set(x)

	d := xx.Denom()
	i := new(big.Int)
	m := new(big.Int)

	for {
		switch {
		case d.Cmp(big1) == 0:
			return true
		case remQuo(d, big2, i, m).Sign() == 0:
			d.Set(i)
		case remQuo(d, big5, i, m).Sign() == 0:
			d.Set(i)
		default:
			return false
		}
	}
}

// FinitePrec returns the precision of the finite decimal representation of x.
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FinitePrec(x *big.Rat) int {
	// calling x.Denom() can modify x (populates b) so be extra careful
	xx := new(big.Rat).Set(x)

	d := xx.Denom()
	n := xx.Num()
	m := new(big.Int)

	var i int
	for m.Mod(n, d).Sign() != 0 {
		i++
		n.Mul(n, big10)
	}
	return i
}

// FiniteString returns the equivalent of x.FloatString(FinitePrec(x)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteString(x *big.Rat) string {
	return x.FloatString(FinitePrec(x))
}

// FiniteStringMin returns the equivalent of x.FloatString(max(FinitePrec(x), prec)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteStringMin(x *big.Rat, prec int) string {
	p := FinitePrec(x)
	if p < prec {
		p = prec
	}
	return x.FloatString(p)
}
<|endoftext|>


<commit_before>package servers

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/testhelper"
)

const tokenID = "bzbzbzbzbz"

func serviceClient() *gophercloud.ServiceClient {
	return &gophercloud.ServiceClient{
		Provider: &gophercloud.ProviderClient{TokenID: tokenID},
		Endpoint: testhelper.Endpoint(),
	}
}

func TestListServers(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "GET")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)

		w.Header().Add("Content-Type", "application/json")
		r.ParseForm()
		marker := r.Form.Get("marker")
		switch marker {
		case "":
			fmt.Fprintf(w, serverListBody)
		case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
			fmt.Fprintf(w, `{ "servers": [] }`)
		default:
			t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
		}
	})

	client := serviceClient()
	pages := 0
	err := List(client).EachPage(func(page pagination.Page) (bool, error) {
		pages++

		actual, err := ExtractServers(page)
		if err != nil {
			return false, err
		}

		if len(actual) != 2 {
			t.Fatalf("Expected 2 servers, got %d", len(actual))
		}
		equalServers(t, serverHerp, actual[0])
		equalServers(t, serverDerp, actual[1])

		return true, nil
	})

	if err != nil {
		t.Fatalf("Unexpected error from EachPage: %v", err)
	}
	if pages != 1 {
		t.Errorf("Expected 1 page, saw %d", pages)
	}
}

func TestCreateServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{
			"server": {
				"name": "derp",
				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
				"flavorRef": "1"
			}
		}`)

		w.WriteHeader(http.StatusAccepted)
		w.Header().Add("Content-Type", "application/json")
		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	result, err := Create(client, map[string]interface{}{
		"name":      "derp",
		"imageRef":  "f90f6034-2570-4974-8351-6b49732ef2eb",
		"flavorRef": "1",
	})
	if err != nil {
		t.Fatalf("Unexpected Create error: %v", err)
	}

	actual, err := ExtractServer(result)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestDeleteServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "DELETE")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)

		w.WriteHeader(http.StatusNoContent)
	})

	client := serviceClient()
	err := Delete(client, "asdfasdfasdf")
	if err != nil {
		t.Fatalf("Unexpected Delete error: %v", err)
	}
}

func TestGetServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "GET")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestHeader(t, r, "Accept", "application/json")

		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	response, err := Get(client, "1234asdf")
	if err != nil {
		t.Fatalf("Unexpected Get error: %v", err)
	}

	actual, err := ExtractServer(response)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestUpdateServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "PUT")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestHeader(t, r, "Accept", "application/json")
		testhelper.TestHeader(t, r, "Content-Type", "application/json")
		testhelper.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`)

		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	response, err := Update(client, "1234asdf", map[string]interface{}{
		"name": "new-name",
	})
	if err != nil {
		t.Fatalf("Unexpected Update error: %v", err)
	}

	actual, err := ExtractServer(response)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestChangeServerAdminPassword(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`)

		w.WriteHeader(http.StatusAccepted)
	})

	client := serviceClient()
	err := ChangeAdminPassword(client, "1234asdf", "new-password")
	if err != nil {
		t.Errorf("Unexpected ChangeAdminPassword error: %v", err)
	}
}

func TestRebootServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`)

		w.WriteHeader(http.StatusAccepted)
	})

	client := serviceClient()
	err := Reboot(client, "1234asdf", SoftReboot)
	if err != nil {
		t.Errorf("Unexpected Reboot error: %v", err)
	}
}

func TestRebuildServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}

func TestResizeServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}

func TestConfirmResize(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}

func TestRevertResize(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}
<commit_msg>Reboot action test.<commit_after>package servers

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/testhelper"
)

const tokenID = "bzbzbzbzbz"

func serviceClient() *gophercloud.ServiceClient {
	return &gophercloud.ServiceClient{
		Provider: &gophercloud.ProviderClient{TokenID: tokenID},
		Endpoint: testhelper.Endpoint(),
	}
}

func TestListServers(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "GET")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)

		w.Header().Add("Content-Type", "application/json")
		r.ParseForm()
		marker := r.Form.Get("marker")
		switch marker {
		case "":
			fmt.Fprintf(w, serverListBody)
		case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
			fmt.Fprintf(w, `{ "servers": [] }`)
		default:
			t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
		}
	})

	client := serviceClient()
	pages := 0
	err := List(client).EachPage(func(page pagination.Page) (bool, error) {
		pages++

		actual, err := ExtractServers(page)
		if err != nil {
			return false, err
		}

		if len(actual) != 2 {
			t.Fatalf("Expected 2 servers, got %d", len(actual))
		}
		equalServers(t, serverHerp, actual[0])
		equalServers(t, serverDerp, actual[1])

		return true, nil
	})

	if err != nil {
		t.Fatalf("Unexpected error from EachPage: %v", err)
	}
	if pages != 1 {
		t.Errorf("Expected 1 page, saw %d", pages)
	}
}

func TestCreateServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{
			"server": {
				"name": "derp",
				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
				"flavorRef": "1"
			}
		}`)

		w.WriteHeader(http.StatusAccepted)
		w.Header().Add("Content-Type", "application/json")
		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	result, err := Create(client, map[string]interface{}{
		"name":      "derp",
		"imageRef":  "f90f6034-2570-4974-8351-6b49732ef2eb",
		"flavorRef": "1",
	})
	if err != nil {
		t.Fatalf("Unexpected Create error: %v", err)
	}

	actual, err := ExtractServer(result)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestDeleteServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "DELETE")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)

		w.WriteHeader(http.StatusNoContent)
	})

	client := serviceClient()
	err := Delete(client, "asdfasdfasdf")
	if err != nil {
		t.Fatalf("Unexpected Delete error: %v", err)
	}
}

func TestGetServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "GET")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestHeader(t, r, "Accept", "application/json")

		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	response, err := Get(client, "1234asdf")
	if err != nil {
		t.Fatalf("Unexpected Get error: %v", err)
	}

	actual, err := ExtractServer(response)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestUpdateServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "PUT")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestHeader(t, r, "Accept", "application/json")
		testhelper.TestHeader(t, r, "Content-Type", "application/json")
		testhelper.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`)

		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	response, err := Update(client, "1234asdf", map[string]interface{}{
		"name": "new-name",
	})
	if err != nil {
		t.Fatalf("Unexpected Update error: %v", err)
	}

	actual, err := ExtractServer(response)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestChangeServerAdminPassword(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`)

		w.WriteHeader(http.StatusAccepted)
	})

	client := serviceClient()
	err := ChangeAdminPassword(client, "1234asdf", "new-password")
	if err != nil {
		t.Errorf("Unexpected ChangeAdminPassword error: %v", err)
	}
}

func TestRebootServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`)

		w.WriteHeader(http.StatusAccepted)
	})

	client := serviceClient()
	err := Reboot(client, "1234asdf", SoftReboot)
	if err != nil {
		t.Errorf("Unexpected Reboot error: %v", err)
	}
}

func TestRebuildServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()

	testhelper.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
		testhelper.TestMethod(t, r, "POST")
		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
		testhelper.TestJSONRequest(t, r, `
			{
				"rebuild": {
					"name": "new-name",
					"adminPass": "swordfish",
					"imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
					"accessIPv4": "1.2.3.4"
				}
			}
		`)

		w.WriteHeader(http.StatusAccepted)
		w.Header().Add("Content-Type", "application/json")
		fmt.Fprintf(w, singleServerBody)
	})

	client := serviceClient()
	response, err := Rebuild(client,
		"1234asdf", "new-name", "swordfish",
		"http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
		map[string]interface{}{"accessIPv4": "1.2.3.4"})
	if err != nil {
		t.Fatalf("Unexpected Rebuild error: %v", err)
	}

	actual, err := ExtractServer(response)
	if err != nil {
		t.Fatalf("Unexpected ExtractServer error: %v", err)
	}

	equalServers(t, serverDerp, *actual)
}

func TestResizeServer(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}

func TestConfirmResize(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}

func TestRevertResize(t *testing.T) {
	testhelper.SetupHTTP()
	defer testhelper.TeardownHTTP()
	t.Error("Pending")
}
<|endoftext|>