<commit_before>/*
Copyright 2018 The Knative Authors

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

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

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

package resources

import (
	"strconv"

	"github.com/knative/pkg/kmeta"
	"github.com/knative/pkg/logging"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/pkg/autoscaler"
	"github.com/knative/serving/pkg/queue"
	"github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config"
	"github.com/knative/serving/pkg/reconciler/v1alpha1/revision/resources/names"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

const varLogVolumeName = "varlog"

var (
	varLogVolume = corev1.Volume{
		Name: varLogVolumeName,
		VolumeSource: corev1.VolumeSource{
			EmptyDir: &corev1.EmptyDirVolumeSource{},
		},
	}

	varLogVolumeMount = corev1.VolumeMount{
		Name:      varLogVolumeName,
		MountPath: "/var/log",
	}

	userPorts = []corev1.ContainerPort{{
		Name:          userPortName,
		ContainerPort: int32(userPort),
	}}

	// Expose containerPort as env PORT.
	userEnv = corev1.EnvVar{
		Name:  userPortEnvName,
		Value: strconv.Itoa(userPort),
	}

	userResources = corev1.ResourceRequirements{
		Requests: corev1.ResourceList{
			corev1.ResourceCPU: userContainerCPU,
		},
	}

	// Add our own PreStop hook here, which should do two things:
	// - make the container fails the next readinessCheck to avoid
	//   having more traffic, and
	// - add a small delay so that the container stays alive a little
	//   bit longer in case stoppage of traffic is not effective
	//   immediately.
	userLifecycle = &corev1.Lifecycle{
		PreStop: &corev1.Handler{
			HTTPGet: &corev1.HTTPGetAction{
				Port: intstr.FromInt(queue.RequestQueueAdminPort),
				Path: queue.RequestQueueQuitPath,
			},
		},
	}
)

func rewriteUserProbe(p *corev1.Probe) {
	if p == nil {
		return
	}
	switch {
	case p.HTTPGet != nil:
		// For HTTP probes, we route them through the queue container
		// so that we know the queue proxy is ready/live as well.
		p.HTTPGet.Port = intstr.FromInt(queue.RequestQueuePort)
	case p.TCPSocket != nil:
		p.TCPSocket.Port = intstr.FromInt(userPort)
	}
}

func makePodSpec(rev *v1alpha1.Revision, loggingConfig *logging.Config, observabilityConfig *config.Observability, autoscalerConfig *autoscaler.Config, controllerConfig *config.Controller) *corev1.PodSpec {
	userContainer := rev.Spec.Container.DeepCopy()
	// Adding or removing an overwritten corev1.Container field here? Don't forget to
	// update the validations in pkg/webhook.validateContainer.
	userContainer.Name = userContainerName
	userContainer.Resources = userResources
	userContainer.Ports = userPorts
	userContainer.VolumeMounts = append(userContainer.VolumeMounts, varLogVolumeMount)
	userContainer.Lifecycle = userLifecycle
	userContainer.Env = append(userContainer.Env, userEnv)
	userContainer.Env = append(userContainer.Env, getKnativeEnvVar(rev)...)
	// Prefer imageDigest from revision if available
	if rev.Status.ImageDigest != "" {
		userContainer.Image = rev.Status.ImageDigest
	}

	// If the client provides probes, we should fill in the port for them.
	rewriteUserProbe(userContainer.ReadinessProbe)
	rewriteUserProbe(userContainer.LivenessProbe)

	podSpec := &corev1.PodSpec{
		Containers: []corev1.Container{
			*userContainer,
			*makeQueueContainer(rev, loggingConfig, autoscalerConfig, controllerConfig),
		},
		Volumes:            []corev1.Volume{varLogVolume},
		ServiceAccountName: rev.Spec.ServiceAccountName,
	}

	// Add Fluentd sidecar and its config map volume if var log collection is enabled.
	if observabilityConfig.EnableVarLogCollection {
		podSpec.Containers = append(podSpec.Containers, *makeFluentdContainer(rev, observabilityConfig))
		podSpec.Volumes = append(podSpec.Volumes, *makeFluentdConfigMapVolume(rev))
	}

	return podSpec
}

func MakeDeployment(rev *v1alpha1.Revision,
	loggingConfig *logging.Config, networkConfig *config.Network, observabilityConfig *config.Observability,
	autoscalerConfig *autoscaler.Config, controllerConfig *config.Controller) *appsv1.Deployment {

	podTemplateAnnotations := makeAnnotations(rev)
	// TODO(nghia): Remove the need for this
	podTemplateAnnotations[sidecarIstioInjectAnnotation] = "true"
	// TODO(mattmoor): Once we have a mechanism for decorating arbitrary deployments (and opting
	// out via annotation) we should explicitly disable that here to avoid redundant Image
	// resources.

	// Inject the IP ranges for istio sidecar configuration.
	// We will inject this value only if all of the following are true:
	// - the config map contains a non-empty value
	// - the user doesn't specify this annotation in configuration's pod template
	// - configured values are valid CIDR notation IP addresses
	// If these conditions are not met, this value will be left untouched.
	// * is a special value that is accepted as a valid.
	// * intercepts calls to all IPs: in cluster as well as outside the cluster.
	if _, ok := podTemplateAnnotations[IstioOutboundIPRangeAnnotation]; !ok {
		if len(networkConfig.IstioOutboundIPRanges) > 0 {
			podTemplateAnnotations[IstioOutboundIPRangeAnnotation] = networkConfig.IstioOutboundIPRanges
		}
	}

	one := int32(1)
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:            names.Deployment(rev),
			Namespace:       rev.Namespace,
			Labels:          makeLabels(rev),
			Annotations:     makeAnnotations(rev),
			OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)},
		},
		Spec: appsv1.DeploymentSpec{
			Replicas:                &one,
			Selector:                makeSelector(rev),
			ProgressDeadlineSeconds: &ProgressDeadlineSeconds,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels:      makeLabels(rev),
					Annotations: podTemplateAnnotations,
				},
				Spec: *makePodSpec(rev, loggingConfig, observabilityConfig, autoscalerConfig, controllerConfig),
			},
		},
	}
}
<commit_msg>Document lifecycle handlers on user-container. (#2451)<commit_after>/*
Copyright 2018 The Knative Authors

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

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

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

package resources

import (
	"strconv"

	"github.com/knative/pkg/kmeta"
	"github.com/knative/pkg/logging"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/pkg/autoscaler"
	"github.com/knative/serving/pkg/queue"
	"github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config"
	"github.com/knative/serving/pkg/reconciler/v1alpha1/revision/resources/names"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

const varLogVolumeName = "varlog"

var (
	varLogVolume = corev1.Volume{
		Name: varLogVolumeName,
		VolumeSource: corev1.VolumeSource{
			EmptyDir: &corev1.EmptyDirVolumeSource{},
		},
	}

	varLogVolumeMount = corev1.VolumeMount{
		Name:      varLogVolumeName,
		MountPath: "/var/log",
	}

	userPorts = []corev1.ContainerPort{{
		Name:          userPortName,
		ContainerPort: int32(userPort),
	}}

	// Expose containerPort as env PORT.
	userEnv = corev1.EnvVar{
		Name:  userPortEnvName,
		Value: strconv.Itoa(userPort),
	}

	userResources = corev1.ResourceRequirements{
		Requests: corev1.ResourceList{
			corev1.ResourceCPU: userContainerCPU,
		},
	}

	// This PreStop hook is actually calling an endpoint on the queue-proxy
	// because of the way PreStop hooks are called by kubelet. We use this
	// to block the user-container from exiting before the queue-proxy is ready
	// to exit so we can guarantee that there are no more requests in flight.
	userLifecycle = &corev1.Lifecycle{
		PreStop: &corev1.Handler{
			HTTPGet: &corev1.HTTPGetAction{
				Port: intstr.FromInt(queue.RequestQueueAdminPort),
				Path: queue.RequestQueueQuitPath,
			},
		},
	}
)

func rewriteUserProbe(p *corev1.Probe) {
	if p == nil {
		return
	}
	switch {
	case p.HTTPGet != nil:
		// For HTTP probes, we route them through the queue container
		// so that we know the queue proxy is ready/live as well.
		p.HTTPGet.Port = intstr.FromInt(queue.RequestQueuePort)
	case p.TCPSocket != nil:
		p.TCPSocket.Port = intstr.FromInt(userPort)
	}
}

func makePodSpec(rev *v1alpha1.Revision, loggingConfig *logging.Config, observabilityConfig *config.Observability, autoscalerConfig *autoscaler.Config, controllerConfig *config.Controller) *corev1.PodSpec {
	userContainer := rev.Spec.Container.DeepCopy()
	// Adding or removing an overwritten corev1.Container field here? Don't forget to
	// update the validations in pkg/webhook.validateContainer.
	userContainer.Name = userContainerName
	userContainer.Resources = userResources
	userContainer.Ports = userPorts
	userContainer.VolumeMounts = append(userContainer.VolumeMounts, varLogVolumeMount)
	userContainer.Lifecycle = userLifecycle
	userContainer.Env = append(userContainer.Env, userEnv)
	userContainer.Env = append(userContainer.Env, getKnativeEnvVar(rev)...)
	// Prefer imageDigest from revision if available
	if rev.Status.ImageDigest != "" {
		userContainer.Image = rev.Status.ImageDigest
	}

	// If the client provides probes, we should fill in the port for them.
	rewriteUserProbe(userContainer.ReadinessProbe)
	rewriteUserProbe(userContainer.LivenessProbe)

	podSpec := &corev1.PodSpec{
		Containers: []corev1.Container{
			*userContainer,
			*makeQueueContainer(rev, loggingConfig, autoscalerConfig, controllerConfig),
		},
		Volumes:            []corev1.Volume{varLogVolume},
		ServiceAccountName: rev.Spec.ServiceAccountName,
	}

	// Add Fluentd sidecar and its config map volume if var log collection is enabled.
	if observabilityConfig.EnableVarLogCollection {
		podSpec.Containers = append(podSpec.Containers, *makeFluentdContainer(rev, observabilityConfig))
		podSpec.Volumes = append(podSpec.Volumes, *makeFluentdConfigMapVolume(rev))
	}

	return podSpec
}

func MakeDeployment(rev *v1alpha1.Revision,
	loggingConfig *logging.Config, networkConfig *config.Network, observabilityConfig *config.Observability,
	autoscalerConfig *autoscaler.Config, controllerConfig *config.Controller) *appsv1.Deployment {

	podTemplateAnnotations := makeAnnotations(rev)
	// TODO(nghia): Remove the need for this
	podTemplateAnnotations[sidecarIstioInjectAnnotation] = "true"
	// TODO(mattmoor): Once we have a mechanism for decorating arbitrary deployments (and opting
	// out via annotation) we should explicitly disable that here to avoid redundant Image
	// resources.

	// Inject the IP ranges for istio sidecar configuration.
	// We will inject this value only if all of the following are true:
	// - the config map contains a non-empty value
	// - the user doesn't specify this annotation in configuration's pod template
	// - configured values are valid CIDR notation IP addresses
	// If these conditions are not met, this value will be left untouched.
	// * is a special value that is accepted as a valid.
	// * intercepts calls to all IPs: in cluster as well as outside the cluster.
	if _, ok := podTemplateAnnotations[IstioOutboundIPRangeAnnotation]; !ok {
		if len(networkConfig.IstioOutboundIPRanges) > 0 {
			podTemplateAnnotations[IstioOutboundIPRangeAnnotation] = networkConfig.IstioOutboundIPRanges
		}
	}

	one := int32(1)
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:            names.Deployment(rev),
			Namespace:       rev.Namespace,
			Labels:          makeLabels(rev),
			Annotations:     makeAnnotations(rev),
			OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)},
		},
		Spec: appsv1.DeploymentSpec{
			Replicas:                &one,
			Selector:                makeSelector(rev),
			ProgressDeadlineSeconds: &ProgressDeadlineSeconds,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels:      makeLabels(rev),
					Annotations: podTemplateAnnotations,
				},
				Spec: *makePodSpec(rev, loggingConfig, observabilityConfig, autoscalerConfig, controllerConfig),
			},
		},
	}
}
<|endoftext|>


<commit_before>package nom

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
)

// Port is either a physical or a virtual port of a node.
type Port struct {
	ID    PortID
	Node  UID
	Links []UID
}

// PortID is the ID of a port and is unique among the ports of a node.
type PortID string

// UID returns the unique ID of the port in the form of
// net_id$$node_id$$port_id.
func (p Port) UID() UID {
	return UIDJoin(string(p.Node), string(p.ID))
}

// ParsePortUID parses a UID of a port and returns the respective node and port
// IDs.
func ParsePortUID(id UID) (NodeID, PortID) {
	s := UIDSplit(id)
	return NodeID(s[0]), PortID(s[1])
}

// GobDecode decodes the port from b using Gob.
func (p *Port) GobDecode(b []byte) error {
	return ObjGobDecode(p, b)
}

// GobEncode encodes the port into a byte array using Gob.
func (p *Port) GobEncode() ([]byte, error) {
	return ObjGobEncode(p)
}

// JSONDecode decodes the port from a byte array using JSON.
func (p *Port) JSONDecode(b []byte) error {
	return json.Unmarshal(b, p)
}

// JSONEncode encodes the port into a byte array using JSON.
func (p *Port) JSONEncode() ([]byte, error) {
	return json.Marshal(p)
}
<commit_msg>Enhance nom.Port<commit_after>package nom

import "encoding/json"

// PortAdded is emitted when a port is added to a node (or when the node
// joins the network for the first time).
type PortAdded Port

// PortRemoved is emitted when a port is removed (or its node is disconnected
// from the controller).
type PortRemoved Port

// PortChanged is emitted when a port's state or configuration is changed.
type PortChanged Port

// Port is either a physical or a virtual port of a node.
type Port struct {
	ID      PortID      // ID is unique among the ports of this node.
	Name    string      // Human-readable name of the port.
	MACAddr MACAddr     // Hardware address of the port.
	Node    UID         // The node.
	Link    UID         // The outgoing link.
	State   PortState   // Is the state of the port.
	Config  PortConfig  // Is the configuration of the port.
	Feature PortFeature // Features of this port.
}

// PortID is the ID of a port and is unique among the ports of a node.
type PortID string

// UID returns the unique ID of the port in the form of
// net_id$$node_id$$port_id.
func (p Port) UID() UID {
	return UIDJoin(string(p.Node), string(p.ID))
}

// ParsePortUID parses a UID of a port and returns the respective node and port
// IDs.
func ParsePortUID(id UID) (NodeID, PortID) {
	s := UIDSplit(id)
	return NodeID(s[0]), PortID(s[1])
}

// GobDecode decodes the port from b using Gob.
func (p *Port) GobDecode(b []byte) error {
	return ObjGobDecode(p, b)
}

// GobEncode encodes the port into a byte array using Gob.
func (p *Port) GobEncode() ([]byte, error) {
	return ObjGobEncode(p)
}

// JSONDecode decodes the port from a byte array using JSON.
func (p *Port) JSONDecode(b []byte) error {
	return json.Unmarshal(b, p)
}

// JSONEncode encodes the port into a byte array using JSON.
func (p *Port) JSONEncode() ([]byte, error) {
	return json.Marshal(p)
}

// PortState is the current state of a port.
type PortState uint8

// Valid values for PortState.
const (
	PortStateUnknown PortState = iota // Port's state is unknown.
	PortStateDown              = iota // Port is not connected to any link.
	PortStateUp                = iota // Port is up and forwarding packets.
	PortStateBlocked           = iota // Port is blocked.
)

// PortConfig is the NOM specific configuration of the port.
type PortConfig uint8

// Valid values for PortConfig.
const (
	PortConfigDown        PortConfig = 1 << iota // Down.
	PortConfigDropPackets            = 1 << iota // Drop incoming packets.
	PortConfigNoForward              = 1 << iota // Do not forward packets.
	PortConfigNoFlood                = 1 << iota // Do not include in flood.
	PortConfigNoPacketIn             = 1 << iota // Do not send packet ins.
	PortConfigDisableStp             = 1 << iota // Disable STP.
	PortConfigDropStp                = 1 << iota // Drop STP packets.
)

// PortFeature represents port features.
type PortFeature uint16

// Valid values for PortFeature
const (
	PortFeature10MBHD  PortFeature = 1 << iota // 10MB half-duplex.
	PortFeature10MBFD              = 1 << iota // 10MB full-duplex.
	PortFeature100MBHD             = 1 << iota // 100MB half-duplex.
	PortFeature100MBFD             = 1 << iota // 100MB half-duplex.
	PortFeature1GBHD               = 1 << iota // 1GB half-duplex.
	PortFeature1GBFD               = 1 << iota // 1GB half-duplex.
	PortFeature10GBHD              = 1 << iota // 10GB  half-duplex.
	PortFeature10GBFD              = 1 << iota // 10GB half-duplex.
	PortFeatureCopper              = 1 << iota // Copper.
	PortFeatureFiber               = 1 << iota // Fiber.
	PortFeatureAutoneg             = 1 << iota // Auto negotiation.
	PortPause                      = 1 << iota // Pause.
	PortPauseAsym                  = 1 << iota // Asymmetric pause.
)
<|endoftext|>


<commit_before>package component

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/apply/action"
	"github.com/Aptomi/aptomi/pkg/slinga/event"
	"github.com/Aptomi/aptomi/pkg/slinga/lang"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"time"
)

// EndpointsActionObject is an informational data structure with Kind and Constructor for the action
var EndpointsActionObject = &object.Info{
	Kind:        "action-component-create",
	Constructor: func() object.Base { return &CreateAction{} },
}

// EndpointsAction is a action which gets called when a new component changed (created or updated) and endpoints should be updated
type EndpointsAction struct {
	// Key is the revision id and action id pair
	*action.Metadata
	ComponentKey string
}

// NewEndpointsAction creates new EndpointsAction
func NewEndpointsAction(revision object.Generation, componentKey string) *EndpointsAction {
	return &EndpointsAction{
		Metadata:     action.NewMetadata(revision, EndpointsActionObject.Kind, componentKey),
		ComponentKey: componentKey,
	}
}

// Apply applies the action
func (a *EndpointsAction) Apply(context *action.Context) error {
	err := a.processEndpoints(context)
	if err != nil {
		context.EventLog.LogError(err)
		return fmt.Errorf("Errors while getting endpoints '%s': %s", a.ComponentKey, err)
	}

	// update actual state
	return a.updateActualState(context)
}

func (a *EndpointsAction) updateActualState(context *action.Context) error {
	// preserve previous creation date before overwriting
	prevCreatedOn := context.ActualState.ComponentInstanceMap[a.ComponentKey].CreatedOn
	instance := context.DesiredState.ComponentInstanceMap[a.ComponentKey]
	instance.UpdateTimes(prevCreatedOn, time.Now())

	context.ActualState.ComponentInstanceMap[a.ComponentKey] = instance
	err := context.ActualStateUpdater.Update(instance)
	if err != nil {
		return fmt.Errorf("error while update actual state: %s", err)
	}
	return nil
}

func (a *EndpointsAction) processEndpoints(context *action.Context) error {
	instance := context.DesiredState.ComponentInstanceMap[a.ComponentKey]
	serviceObj, err := context.DesiredPolicy.GetObject(lang.ServiceObject.Kind, instance.Metadata.Key.ServiceName, instance.Metadata.Key.Namespace)
	if err != nil {
		return err
	}
	component := serviceObj.(*lang.Service).GetComponentsMap()[instance.Metadata.Key.ComponentName]

	if component == nil {
		// This is a service instance. Do nothing
		return nil
	}

	// endpoints could be calculated only for components with code
	if component.Code == nil {
		return nil
	}

	context.EventLog.WithFields(event.Fields{
		"componentKey": instance.Metadata.Key,
		"component":    component.Name,
		"code":         instance.CalculatedCodeParams,
	}).Info("Getting endpoints for component instance: " + instance.GetKey())

	clusterName, ok := instance.CalculatedCodeParams[lang.LabelCluster].(string)
	if !ok {
		return fmt.Errorf("No cluster specified in code params, component instance: %v", a.ComponentKey)
	}

	clusterObj, err := context.DesiredPolicy.GetObject(lang.ClusterObject.Kind, clusterName, object.SystemNS)
	if err != nil {
		return err
	}
	if clusterObj == nil {
		return fmt.Errorf("Can't find cluster in policy: %s", clusterName)
	}

	plugin, err := context.Plugins.GetDeployPlugin(component.Code.Type)
	if err != nil {
		return err
	}

	endpoints, err := plugin.Endpoints(clusterObj.(*lang.Cluster), instance.GetDeployName(), instance.CalculatedCodeParams, context.EventLog)
	if err != nil {
		return err
	}

	instance.Endpoints = endpoints

	return nil
}
<commit_msg>Fix component endpoint action<commit_after>package component

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/apply/action"
	"github.com/Aptomi/aptomi/pkg/slinga/event"
	"github.com/Aptomi/aptomi/pkg/slinga/lang"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
)

// EndpointsActionObject is an informational data structure with Kind and Constructor for the action
var EndpointsActionObject = &object.Info{
	Kind:        "action-component-endpoints",
	Constructor: func() object.Base { return &EndpointsAction{} },
}

// EndpointsAction is a action which gets called when a new component changed (created or updated) and endpoints should be updated
type EndpointsAction struct {
	// Key is the revision id and action id pair
	*action.Metadata
	ComponentKey string
}

// NewEndpointsAction creates new EndpointsAction
func NewEndpointsAction(revision object.Generation, componentKey string) *EndpointsAction {
	return &EndpointsAction{
		Metadata:     action.NewMetadata(revision, EndpointsActionObject.Kind, componentKey),
		ComponentKey: componentKey,
	}
}

// Apply applies the action
func (a *EndpointsAction) Apply(context *action.Context) error {
	// skip if it wasn't processed (doesn't exist in actual state)
	if context.ActualState.ComponentInstanceMap[a.ComponentKey] == nil {
		return fmt.Errorf("Can't get endpoints of component instance that doesn't present in actual state: %s", a.ComponentKey)
	}

	err := a.processEndpoints(context)
	if err != nil {
		context.EventLog.LogError(err)
		return fmt.Errorf("Errors while getting endpoints '%s': %s", a.ComponentKey, err)
	}

	// update actual state
	return a.updateActualState(context)
}

func (a *EndpointsAction) updateActualState(context *action.Context) error {
	instance := context.ActualState.ComponentInstanceMap[a.ComponentKey]
	err := context.ActualStateUpdater.Update(instance)
	if err != nil {
		return fmt.Errorf("error while update actual state: %s", err)
	}
	return nil
}

func (a *EndpointsAction) processEndpoints(context *action.Context) error {
	instance := context.ActualState.ComponentInstanceMap[a.ComponentKey]
	serviceObj, err := context.DesiredPolicy.GetObject(lang.ServiceObject.Kind, instance.Metadata.Key.ServiceName, instance.Metadata.Key.Namespace)
	if err != nil {
		return err
	}
	component := serviceObj.(*lang.Service).GetComponentsMap()[instance.Metadata.Key.ComponentName]

	if component == nil {
		// This is a service instance. Do nothing
		return nil
	}

	// endpoints could be calculated only for components with code
	if component.Code == nil {
		return nil
	}

	context.EventLog.WithFields(event.Fields{
		"componentKey": instance.Metadata.Key,
		"component":    component.Name,
		"code":         instance.CalculatedCodeParams,
	}).Info("Getting endpoints for component instance: " + instance.GetKey())

	clusterName, ok := instance.CalculatedCodeParams[lang.LabelCluster].(string)
	if !ok {
		return fmt.Errorf("No cluster specified in code params, component instance: %v", a.ComponentKey)
	}

	clusterObj, err := context.DesiredPolicy.GetObject(lang.ClusterObject.Kind, clusterName, object.SystemNS)
	if err != nil {
		return err
	}
	if clusterObj == nil {
		return fmt.Errorf("Can't find cluster in policy: %s", clusterName)
	}

	plugin, err := context.Plugins.GetDeployPlugin(component.Code.Type)
	if err != nil {
		return err
	}

	endpoints, err := plugin.Endpoints(clusterObj.(*lang.Cluster), instance.GetDeployName(), instance.CalculatedCodeParams, context.EventLog)
	if err != nil {
		return err
	}

	instance.Endpoints = endpoints

	return nil
}
<|endoftext|>


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

package gles

import (
	"testing"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapis/atom"
	"github.com/google/gapid/gapis/atom/test"
	"github.com/google/gapid/gapis/capture"
	"github.com/google/gapid/gapis/database"
	"github.com/google/gapid/gapis/memory"
)

func TestLivenessTree(t *testing.T) {
	ctx := log.Testing(t)
	ctx = database.Put(ctx, database.NewInMemory(ctx))

	//
	//          root
	//         /    \
	//     child1  child2
	//      /  \
	// childA  childB
	//
	root := StateAddress(1)
	child1 := StateAddress(2)
	child2 := StateAddress(3)
	childA := StateAddress(4)
	childB := StateAddress(5)
	tree := newLivenessTree(map[StateAddress]StateAddress{
		nullStateAddress: nullStateAddress,
		root:             nullStateAddress,
		child1:           root,
		child2:           root,
		childA:           child1,
		childB:           child1,
	})

	tree.MarkLive(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkDead(root)
	tree.MarkLive(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkLive(root)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkDead(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)

	tree.MarkDead(root)
	assert.With(ctx).That(tree.IsLive(root)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)

	tree.MarkLive(childA)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)
}

func TestDeadAtomRemoval(t *testing.T) {
	ctx := log.Testing(t)
	ctx = database.Put(ctx, database.NewInMemory(ctx))

	// Keep the given atom alive in the optimization.
	isLive := map[atom.Atom]bool{}
	live := func(a atom.Atom) atom.Atom { isLive[a] = true; return a }

	// Expect the atom to be removed by the optimization.
	isDead := map[atom.Atom]bool{}
	dead := func(a atom.Atom) atom.Atom { isDead[a] = true; return a }

	ctxHandle1 := memory.Pointer{Pool: memory.ApplicationPool, Address: 1}
	ctxHandle2 := memory.Pointer{Pool: memory.ApplicationPool, Address: 2}
	prologue := []atom.Atom{
		NewEglCreateContext(memory.Nullptr, memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1),
		atom.WithExtras(
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1, 0),
			NewStaticContextState(), NewDynamicContextState(64, 64, false)),
		NewGlCreateProgram(1),
		NewGlCreateProgram(2),
	}
	allBuffers := GLbitfield_GL_COLOR_BUFFER_BIT | GLbitfield_GL_DEPTH_BUFFER_BIT | GLbitfield_GL_STENCIL_BUFFER_BIT
	tests := map[string][]atom.Atom{
		"Draw calls up to the requested point are preserved": {
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 1, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 2, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 3, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 4, 0)),
		},
		"No request in frame kills draw calls": {
			dead(NewGlClear(allBuffers)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 1, 0)),
			dead(NewEglSwapBuffers(memory.Nullptr, memory.Nullptr, EGLBoolean(1))),
			NewGlClear(allBuffers),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Multiple requests": {
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Simple overwrite": {
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUniform4fv(1, 1, memory.Nullptr),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			dead(NewGlVertexAttribPointer(0, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr)),
			NewGlVertexAttribPointer(1, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr),
			NewGlVertexAttribPointer(0, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Overwrites should be tracked per program": {
			NewGlUseProgram(1),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUseProgram(2),
			NewGlUniform4fv(0, 1, memory.Nullptr), // Unaffected
			NewGlUseProgram(1),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlUseProgram(1),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlUseProgram(2),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Arrays should not interact with scalars": {
			NewGlUniform4fv(0, 10, memory.Nullptr),
			NewGlUniform4fv(0, 1, memory.Nullptr), // Unaffected
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Arrays should not interact with scalars (2)": {
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlUniform4fv(0, 10, memory.Nullptr), // Unaffected
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Unsupported atoms are left unmodified": {
			NewGlUseProgram(1),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUniform1f(0, 3.14), // Not handled in the optimization.
			NewGlLinkProgram(1),     // Not handled in the optimization.
			NewGlUniform4fv(0, 1, memory.Nullptr),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Multiple contexts": {
			// Draw in context 1
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlClear(allBuffers),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			// Draw in context 2
			NewEglCreateContext(memory.Nullptr, memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2),
			atom.WithExtras(
				NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2, 0),
				NewStaticContextState(), NewDynamicContextState(64, 64, false)),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlClear(allBuffers),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			// Request from both contexts
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
	}

	for name, atoms := range tests {
		inputAtoms := append(prologue, atoms...)

		capturePath, err := capture.ImportAtomList(ctx, name, atom.NewList(inputAtoms...))
		if err != nil {
			panic(err)
		}
		ctx = capture.Put(ctx, capturePath)

		dependencyGraph, err := GetDependencyGraph(ctx)
		if err != nil {
			t.Fatalf("%v", err)
		}
		transform := newDeadCodeElimination(ctx, dependencyGraph)

		expectedAtoms := []atom.Atom{}
		for i, a := range inputAtoms {
			if isLive[a] {
				transform.Request(atom.ID(i))
			}
			if !isDead[a] {
				expectedAtoms = append(expectedAtoms, a)
			}
		}

		w := &test.MockAtomWriter{}
		transform.Flush(ctx, w)

		assert.For(ctx, "Test '%v'", name).ThatSlice(w.Atoms).Equals(expectedAtoms)
	}
}
<commit_msg>Fix dead-code-elimination test. (#145)<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gles

import (
	"testing"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapis/atom"
	"github.com/google/gapid/gapis/atom/test"
	"github.com/google/gapid/gapis/capture"
	"github.com/google/gapid/gapis/database"
	"github.com/google/gapid/gapis/memory"
)

func TestLivenessTree(t *testing.T) {
	ctx := log.Testing(t)
	ctx = database.Put(ctx, database.NewInMemory(ctx))

	//
	//          root
	//         /    \
	//     child1  child2
	//      /  \
	// childA  childB
	//
	root := StateAddress(1)
	child1 := StateAddress(2)
	child2 := StateAddress(3)
	childA := StateAddress(4)
	childB := StateAddress(5)
	tree := newLivenessTree(map[StateAddress]StateAddress{
		nullStateAddress: nullStateAddress,
		root:             nullStateAddress,
		child1:           root,
		child2:           root,
		childA:           child1,
		childB:           child1,
	})

	tree.MarkLive(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkDead(root)
	tree.MarkLive(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkLive(root)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(true)

	tree.MarkDead(child1)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)

	tree.MarkDead(root)
	assert.With(ctx).That(tree.IsLive(root)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(false)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)

	tree.MarkLive(childA)
	assert.With(ctx).That(tree.IsLive(root)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child1)).Equals(true)
	assert.With(ctx).That(tree.IsLive(child2)).Equals(false)
	assert.With(ctx).That(tree.IsLive(childA)).Equals(true)
	assert.With(ctx).That(tree.IsLive(childB)).Equals(false)
}

func TestDeadAtomRemoval(t *testing.T) {
	ctx := log.Testing(t)
	ctx = database.Put(ctx, database.NewInMemory(ctx))

	// Keep the given atom alive in the optimization.
	isLive := map[atom.Atom]bool{}
	live := func(a atom.Atom) atom.Atom { isLive[a] = true; return a }

	// Expect the atom to be removed by the optimization.
	isDead := map[atom.Atom]bool{}
	dead := func(a atom.Atom) atom.Atom { isDead[a] = true; return a }

	programInfo := &ProgramInfo{
		LinkStatus: GLboolean_GL_TRUE,
		ActiveUniforms: UniformIndexːActiveUniformᵐ{
			0: {
				Name:      "uniforms",
				Type:      GLenum_GL_FLOAT_VEC4,
				Location:  0,
				ArraySize: 10,
			},
		},
	}

	ctxHandle1 := memory.Pointer{Pool: memory.ApplicationPool, Address: 1}
	ctxHandle2 := memory.Pointer{Pool: memory.ApplicationPool, Address: 2}
	prologue := []atom.Atom{
		NewEglCreateContext(memory.Nullptr, memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1),
		atom.WithExtras(
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1, 0),
			NewStaticContextState(), NewDynamicContextState(64, 64, false)),
		NewGlCreateProgram(1),
		NewGlCreateProgram(2),
		atom.WithExtras(NewGlLinkProgram(1), programInfo),
		atom.WithExtras(NewGlLinkProgram(2), programInfo),
		NewGlUseProgram(1),
	}
	allBuffers := GLbitfield_GL_COLOR_BUFFER_BIT | GLbitfield_GL_DEPTH_BUFFER_BIT | GLbitfield_GL_STENCIL_BUFFER_BIT
	tests := map[string][]atom.Atom{
		"Draw calls up to the requested point are preserved": {
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 1, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 2, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 3, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 4, 0)),
		},
		"No request in frame kills draw calls": {
			dead(NewGlClear(allBuffers)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 1, 0)),
			dead(NewEglSwapBuffers(memory.Nullptr, memory.Nullptr, EGLBoolean(1))),
			NewGlClear(allBuffers),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Multiple requests": {
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Simple overwrite": {
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUniform4fv(1, 1, memory.Nullptr),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			dead(NewGlVertexAttribPointer(0, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr)),
			NewGlVertexAttribPointer(1, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr),
			NewGlVertexAttribPointer(0, 4, GLenum_GL_FLOAT, GLboolean_GL_FALSE, 0, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Overwrites should be tracked per program": {
			NewGlUseProgram(1),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUseProgram(2),
			NewGlUniform4fv(0, 1, memory.Nullptr), // Unaffected
			NewGlUseProgram(1),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlUseProgram(1),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlUseProgram(2),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Arrays should not interact with scalars": {
			NewGlUniform4fv(0, 10, memory.Nullptr),
			NewGlUniform4fv(0, 1, memory.Nullptr), // Unaffected
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Arrays should not interact with scalars (2)": {
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlUniform4fv(0, 10, memory.Nullptr), // Unaffected
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Unsupported atoms are left unmodified": {
			NewGlUseProgram(1),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			NewGlUniform1f(0, 3.14), // Not handled in the optimization.
			NewGlLinkProgram(1),     // Not handled in the optimization.
			NewGlUniform4fv(0, 1, memory.Nullptr),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
		"Multiple contexts": {
			// Draw in context 1
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlClear(allBuffers),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			// Draw in context 2
			NewEglCreateContext(memory.Nullptr, memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2),
			atom.WithExtras(
				NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2, 0),
				NewStaticContextState(), NewDynamicContextState(64, 64, false)),
			NewGlCreateProgram(1),
			atom.WithExtras(NewGlLinkProgram(1), programInfo),
			NewGlUseProgram(1),
			dead(NewGlUniform4fv(0, 1, memory.Nullptr)),
			dead(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewGlClear(allBuffers),
			NewGlUniform4fv(0, 1, memory.Nullptr),
			NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0),
			// Request from both contexts
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle1, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
			NewEglMakeCurrent(memory.Nullptr, memory.Nullptr, memory.Nullptr, ctxHandle2, 0),
			live(NewGlDrawArrays(GLenum_GL_TRIANGLES, 0, 0)),
		},
	}

	for name, atoms := range tests {
		inputAtoms := append(prologue, atoms...)

		capturePath, err := capture.ImportAtomList(ctx, name, atom.NewList(inputAtoms...))
		if err != nil {
			panic(err)
		}
		ctx = capture.Put(ctx, capturePath)

		dependencyGraph, err := GetDependencyGraph(ctx)
		if err != nil {
			t.Fatalf("%v", err)
		}
		transform := newDeadCodeElimination(ctx, dependencyGraph)

		expectedAtoms := []atom.Atom{}
		for i, a := range inputAtoms {
			if isLive[a] {
				transform.Request(atom.ID(i))
			}
			if !isDead[a] {
				expectedAtoms = append(expectedAtoms, a)
			}
		}

		w := &test.MockAtomWriter{}
		transform.Flush(ctx, w)

		assert.For(ctx, "Test '%v'", name).ThatSlice(w.Atoms).Equals(expectedAtoms)
	}
}
<|endoftext|>


<commit_before>package goridge

import (
	"net"
	"net/rpc"
	"strings"
	"sync"
	"testing"

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

// testService sample
type testService struct{}

// Payload sample
type Payload struct {
	Name  string            `json:"name"`
	Value int               `json:"value"`
	Keys  map[string]string `json:"keys,omitempty"`
}

// Echo returns incoming message
func (s *testService) Echo(msg string, r *string) error {
	*r = msg
	return nil
}

// Echo returns error
func (s *testService) EchoR(msg string, r *string) error {
	*r = "error"
	return errors.Str("echoR error")
}

// Process performs payload conversion
func (s *testService) Process(msg Payload, r *Payload) error {
	r.Name = strings.ToUpper(msg.Name)
	r.Value = -msg.Value

	if len(msg.Keys) != 0 {
		r.Keys = make(map[string]string)
		for n, v := range msg.Keys {
			r.Keys[v] = n
		}
	}

	return nil
}

// EchoBinary work over binary data
func (s *testService) EchoBinary(msg []byte, out *[]byte) error {
	*out = append(*out, msg...)
	return nil
}
func TestClientServerJSON(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:18935")
	assert.NoError(t, err)

	go func() {
		for {
			conn, err2 := ln.Accept()
			assert.NoError(t, err2)
			rpc.ServeCodec(NewCodec(conn))
		}
	}()

	err = rpc.RegisterName("test2", new(testService))
	assert.NoError(t, err)

	conn, err := net.Dial("tcp", "127.0.0.1:18935")
	assert.NoError(t, err)

	client := rpc.NewClientWithCodec(NewClientCodec(conn))
	defer func() {
		err := client.Close()
		if err != nil {
			t.Fatal(err)
		}
	}()

	var rp = Payload{}
	assert.NoError(t, client.Call("test2.Process", Payload{
		Name:  "name",
		Value: 1000,
		Keys:  map[string]string{"key": "value"},
	}, &rp))

	assert.Equal(t, "NAME", rp.Name)
	assert.Equal(t, -1000, rp.Value)
	assert.Equal(t, "key", rp.Keys["value"])
}

func TestClientServerConcurrent(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:22385")
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			conn, err2 := ln.Accept()
			assert.NoError(t, err2)
			rpc.ServeCodec(NewCodec(conn))
		}
	}()

	err = rpc.RegisterName("test", new(testService))
	assert.NoError(t, err)

	conn, err := net.Dial("tcp", "127.0.0.1:22385")
	assert.NoError(t, err)

	client := rpc.NewClientWithCodec(NewClientCodec(conn))
	defer func() {
		err := client.Close()
		assert.NoError(t, err)
	}()

	wg := &sync.WaitGroup{}
	wg.Add(300)

	for i := 0; i < 100; i++ {
		go func() {
			defer wg.Done()
			var rp = Payload{}
			d := client.Go("test.Process", Payload{
				Name:  "name",
				Value: 1000,
				Keys:  map[string]string{"key": "value"},
			}, &rp, nil)

			<-d.Done
			assert.Equal(t, "NAME", rp.Name)
			assert.Equal(t, -1000, rp.Value)
			assert.Equal(t, "key", rp.Keys["value"])
		}()

		go func() {
			defer wg.Done()
			var rs = ""
			d := client.Go("test.Echo", "hello", &rs, nil)
			<-d.Done
			assert.Equal(t, "hello", rs)
		}()

		go func() {
			defer wg.Done()
			rs := ""
			rb := make([]byte, 0)

			a := client.Go("test.Echo", "hello", &rs, nil)
			b := client.Go("test.EchoBinary", []byte("hello world"), &rb, nil)
			c := client.Go("test.EchoR", "hi", &rs, nil)

			for i := 0; i < 3; i++ {
				select {
				case reply := <-a.Done:
					_ = reply
					assert.Equal(t, "hello", rs)
				case reply := <-b.Done:
					_ = reply
					assert.Equal(t, []byte("hello world"), rb)
				case reply := <-c.Done:
					assert.Error(t, reply.Error)
				}
			}
		}()
	}

	wg.Wait()

	wg2 := &sync.WaitGroup{}
	wg2.Add(300)

	for i := 0; i < 100; i++ {
		go func() {
			defer wg2.Done()
			var rp = Payload{}
			assert.NoError(t, client.Call("test.Process", Payload{
				Name:  "name",
				Value: 1000,
				Keys:  map[string]string{"key": "value"},
			}, &rp))

			assert.Equal(t, "NAME", rp.Name)
			assert.Equal(t, -1000, rp.Value)
			assert.Equal(t, "key", rp.Keys["value"])
		}()

		go func() {
			defer wg2.Done()
			var rs = ""
			assert.NoError(t, client.Call("test.Echo", "hello", &rs))
			assert.Equal(t, "hello", rs)
		}()

		go func() {
			defer wg2.Done()
			rs := ""
			rb := make([]byte, 0, len("hello world"))
			assert.NoError(t, client.Call("test.Echo", "hello", &rs))
			assert.Equal(t, "hello", rs)

			assert.NoError(t, client.Call("test.EchoBinary", []byte("hello world"), &rb))
			assert.Equal(t, []byte("hello world"), rb)

			assert.Error(t, client.Call("test.EchoR", "hi", &rs))
		}()
	}

	wg2.Wait()
}
<commit_msg>add random input to the codec/client test<commit_after>package goridge

import (
	"crypto/rand"
	"net"
	"net/rpc"
	"strings"
	"sync"
	"testing"

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

// testService sample
type testService struct{}

// Payload sample
type Payload struct {
	Name  string            `json:"name"`
	Value int               `json:"value"`
	Keys  map[string]string `json:"keys,omitempty"`
}

// Echo returns incoming message
func (s *testService) Echo(msg string, r *string) error {
	*r = msg
	return nil
}

// Echo returns error
func (s *testService) EchoR(msg string, r *string) error {
	*r = "error"
	return errors.Str("echoR error")
}

// Process performs payload conversion
func (s *testService) Process(msg Payload, r *Payload) error {
	r.Name = strings.ToUpper(msg.Name)
	r.Value = -msg.Value

	if len(msg.Keys) != 0 {
		r.Keys = make(map[string]string)
		for n, v := range msg.Keys {
			r.Keys[v] = n
		}
	}

	return nil
}

// EchoBinary work over binary data
func (s *testService) EchoBinary(msg []byte, out *[]byte) error {
	*out = append(*out, msg...)
	return nil
}
func TestClientServerJSON(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:18935")
	assert.NoError(t, err)

	go func() {
		for {
			conn, err2 := ln.Accept()
			assert.NoError(t, err2)
			rpc.ServeCodec(NewCodec(conn))
		}
	}()

	err = rpc.RegisterName("test2", new(testService))
	assert.NoError(t, err)

	conn, err := net.Dial("tcp", "127.0.0.1:18935")
	assert.NoError(t, err)

	client := rpc.NewClientWithCodec(NewClientCodec(conn))
	defer func() {
		err := client.Close()
		if err != nil {
			t.Fatal(err)
		}
	}()

	var rp = Payload{}
	assert.NoError(t, client.Call("test2.Process", Payload{
		Name:  "name",
		Value: 1000,
		Keys:  map[string]string{"key": "value"},
	}, &rp))

	assert.Equal(t, "NAME", rp.Name)
	assert.Equal(t, -1000, rp.Value)
	assert.Equal(t, "key", rp.Keys["value"])
}

func TestClientServerConcurrent(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:22385")
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			conn, err2 := ln.Accept()
			assert.NoError(t, err2)
			rpc.ServeCodec(NewCodec(conn))
		}
	}()

	err = rpc.RegisterName("test", new(testService))
	assert.NoError(t, err)

	conn, err := net.Dial("tcp", "127.0.0.1:22385")
	assert.NoError(t, err)

	client := rpc.NewClientWithCodec(NewClientCodec(conn))
	defer func() {
		err := client.Close()
		assert.NoError(t, err)
	}()

	wg := &sync.WaitGroup{}
	wg.Add(300)

	// this test uses random inputs
	for i := 0; i < 100; i++ {
		go func() {
			defer wg.Done()
			var rp = Payload{}
			b := make([]byte, 15)
			_, err := rand.Read(b)
			assert.NoError(t, err)

			<-client.Go("test.Process", Payload{
				Name:  string(b),
				Value: 1000,
				Keys:  map[string]string{"key": string(b)},
			}, &rp, nil).Done

			assert.Equal(t, strings.ToUpper(string(b)), rp.Name)
			assert.Equal(t, -1000, rp.Value)
			assert.Equal(t, "key", rp.Keys[string(b)])
		}()

		go func() {
			var rs = ""
			b := make([]byte, 15)
			_, err := rand.Read(b)
			assert.NoError(t, err)
			<-client.Go("test.Echo", string(b), &rs, nil).Done
			assert.Equal(t, string(b), rs)
			wg.Done()
		}()

		go func() {
			rs := ""
			rb := make([]byte, 0)

			r := make([]byte, 15)
			_, err := rand.Read(r)
			assert.NoError(t, err)
			a := client.Go("test.Echo", string(r), &rs, nil)
			b := client.Go("test.EchoBinary", []byte("hello world"), &rb, nil)
			c := client.Go("test.EchoR", "hi", &rs, nil)

			<-a.Done
			assert.Equal(t, string(r), rs)
			<-b.Done
			assert.Equal(t, []byte("hello world"), rb)
			resC := <-c.Done
			assert.Error(t, resC.Error)
			wg.Done()
		}()
	}

	wg.Wait()

	wg2 := &sync.WaitGroup{}
	wg2.Add(300)

	for i := 0; i < 100; i++ {
		go func() {
			defer wg2.Done()
			var rp = Payload{}
			b := make([]byte, 15)
			_, err := rand.Read(b)
			assert.NoError(t, err)

			assert.NoError(t, client.Call("test.Process", Payload{
				Name:  string(b),
				Value: 1000,
				Keys:  map[string]string{"key": string(b)},
			}, &rp))

			assert.Equal(t, strings.ToUpper(string(b)), rp.Name)
			assert.Equal(t, -1000, rp.Value)
			assert.Equal(t, "key", rp.Keys[string(b)])
		}()

		go func() {
			defer wg2.Done()
			var rs = ""
			r := make([]byte, 15)
			_, err := rand.Read(r)
			assert.NoError(t, err)

			assert.NoError(t, client.Call("test.Echo", string(r), &rs))
			assert.Equal(t, string(r), rs)
		}()

		go func() {
			defer wg2.Done()
			rs := ""
			rb := make([]byte, 0, len("hello world"))

			r := make([]byte, 15)
			_, err := rand.Read(r)
			assert.NoError(t, err)

			assert.NoError(t, client.Call("test.Echo", string(r), &rs))
			assert.Equal(t, string(r), rs)

			assert.NoError(t, client.Call("test.EchoBinary", r, &rb))
			assert.Equal(t, r, rb)

			assert.Error(t, client.Call("test.EchoR", "hi", &rs))
		}()
	}

	wg2.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"compress/bzip2"
	"compress/gzip"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/chop-dbhi/bitindex"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

func openFile(name string) (*os.File, io.Reader, error) {
	f, err := os.Open(name)

	if err != nil {
		return nil, nil, err
	}

	var r io.Reader

	// Detect compression.
	switch filepath.Ext(name) {
	case ".gzip", ".gz":
		r, err = gzip.NewReader(f)

		if err != nil {
			return nil, nil, err
		}

	case ".bzip2", ".bz2":
		r = bzip2.NewReader(f)

	default:
		r = f
	}

	return f, r, nil
}

var buildCmd = &cobra.Command{
	Use: "build [<path>]",

	Short: "Build an index.",

	Run: func(cmd *cobra.Command, args []string) {
		var (
			r   io.Reader
			err error
		)

		switch len(args) {
		case 0:
			r = os.Stdin

		case 1:
			var f *os.File

			if f, r, err = openFile(args[0]); err != nil {
				cmd.Printf("Cannot open file: %s\n", err)
				os.Exit(1)
			}

			defer f.Close()

		default:
			cmd.Println("Stdin or a single file must be passed.")
			os.Exit(1)
		}

		var ixer bitindex.Indexer

		switch viper.GetString("build.format") {
		case "csv":
			ix := bitindex.NewCSVIndexer(r)
			ix.Header = viper.GetBool("build.csv-header")

			kc := viper.GetInt("build.csv-key")
			dc := viper.GetInt("build.csv-domain")

			ix.Parse = func(row []string) (uint32, uint32, error) {
				ki, err := strconv.Atoi(row[kc])

				if err != nil {
					return 0, 0, err
				}

				di, err := strconv.Atoi(row[dc])

				if err != nil {
					return 0, 0, err
				}

				return uint32(ki), uint32(di), nil
			}

			ixer = ix

		default:
			cmd.Println("--format flag is required")
			os.Exit(1)
		}

		t0 := time.Now()
		idx, err := ixer.Index()
		dur := time.Now().Sub(t0)

		if err != nil {
			cmd.Printf("Error building index: %s\n", err)
			os.Exit(1)
		}

		cmd.Println("Build time:", dur)
		cmd.Println("Statistics:")
		cmd.Println("* Domain size:", idx.Domain.Size())
		cmd.Println("* Table size:", idx.Table.Size())
		cmd.Println("* Sparsity:", idx.Sparsity()*100)

		output := viper.GetString("build.output")

		var w io.Writer

		// Build output file.
		if output == "" {
			w = os.Stdout
		} else {
			o, err := os.Create(output)

			if err != nil {
				cmd.Println("Error opening file to write:", err)
				os.Exit(1)
			}

			w = o
		}

		if err := bitindex.DumpIndex(w, idx); err != nil {
			cmd.Println("Error dumping index:", err)
			os.Exit(1)
		}

		// Coerce to file, sync and close.
		if f, ok := w.(*os.File); ok {
			if err = f.Sync(); err != nil {
				cmd.Printf("Error syncing file: %s\n", err)
			}

			if err = f.Close(); err != nil {
				cmd.Printf("Error closing file: %s\n", err)
			}
		}
	},
}

func init() {
	flags := buildCmd.Flags()

	// General.
	flags.String("format", "", "Format of the input stream: csv")
	flags.String("output", "", "Specify an output file to write the stream to.")

	// format is required.
	buildCmd.MarkFlagRequired("format")

	viper.BindPFlag("build.format", flags.Lookup("format"))
	viper.BindPFlag("build.output", flags.Lookup("output"))

	// CSV indexer.
	flags.Bool("csv-header", false, "CSV file has a header")
	flags.Int("csv-key", 0, "Index of the column containing set keys.")
	flags.Int("csv-domain", 1, "Index of the column containing domain members.")

	viper.BindPFlag("build.csv-header", flags.Lookup("csv-header"))
	viper.BindPFlag("build.csv-key", flags.Lookup("csv-key"))
	viper.BindPFlag("build.csv-domain", flags.Lookup("csv-domain"))
}
<commit_msg>Add write time to output of build command<commit_after>package main

import (
	"compress/bzip2"
	"compress/gzip"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/chop-dbhi/bitindex"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

func openFile(name string) (*os.File, io.Reader, error) {
	f, err := os.Open(name)

	if err != nil {
		return nil, nil, err
	}

	var r io.Reader

	// Detect compression.
	switch filepath.Ext(name) {
	case ".gzip", ".gz":
		r, err = gzip.NewReader(f)

		if err != nil {
			return nil, nil, err
		}

	case ".bzip2", ".bz2":
		r = bzip2.NewReader(f)

	default:
		r = f
	}

	return f, r, nil
}

var buildCmd = &cobra.Command{
	Use: "build [<path>]",

	Short: "Build an index.",

	Run: func(cmd *cobra.Command, args []string) {
		var (
			r   io.Reader
			err error
		)

		switch len(args) {
		case 0:
			r = os.Stdin

		case 1:
			var f *os.File

			if f, r, err = openFile(args[0]); err != nil {
				cmd.Printf("Cannot open file: %s\n", err)
				os.Exit(1)
			}

			defer f.Close()

		default:
			cmd.Println("Stdin or a single file must be passed.")
			os.Exit(1)
		}

		var ixer bitindex.Indexer

		switch viper.GetString("build.format") {
		case "csv":
			ix := bitindex.NewCSVIndexer(r)
			ix.Header = viper.GetBool("build.csv-header")

			kc := viper.GetInt("build.csv-key")
			dc := viper.GetInt("build.csv-domain")

			ix.Parse = func(row []string) (uint32, uint32, error) {
				ki, err := strconv.Atoi(row[kc])

				if err != nil {
					return 0, 0, err
				}

				di, err := strconv.Atoi(row[dc])

				if err != nil {
					return 0, 0, err
				}

				return uint32(ki), uint32(di), nil
			}

			ixer = ix

		default:
			cmd.Println("--format flag is required")
			os.Exit(1)
		}

		t0 := time.Now()
		idx, err := ixer.Index()
		bt := time.Now().Sub(t0)

		if err != nil {
			cmd.Printf("Error building index: %s\n", err)
			os.Exit(1)
		}

		output := viper.GetString("build.output")

		var w io.Writer

		// Build output file.
		if output == "" {
			w = os.Stdout
		} else {
			o, err := os.Create(output)

			if err != nil {
				cmd.Println("Error opening file to write:", err)
				os.Exit(1)
			}

			w = o
		}

		t0 = time.Now()

		if err := bitindex.DumpIndex(w, idx); err != nil {
			cmd.Println("Error dumping index:", err)
			os.Exit(1)
		}

		wt := time.Now().Sub(t0)

		// Coerce to file, sync and close.
		if f, ok := w.(*os.File); ok {
			if err = f.Sync(); err != nil {
				cmd.Printf("Error syncing file: %s\n", err)
			}

			if err = f.Close(); err != nil {
				cmd.Printf("Error closing file: %s\n", err)
			}
		}

		cmd.Println("Build time:", bt)
		cmd.Println("Write time:", wt)
		cmd.Println("Domain size:", idx.Domain.Size())
		cmd.Println("Table size:", idx.Table.Size())
		cmd.Println("Sparsity:", idx.Sparsity()*100)
	},
}

func init() {
	flags := buildCmd.Flags()

	// General.
	flags.String("format", "", "Format of the input stream: csv")
	flags.String("output", "", "Specify an output file to write the stream to.")

	// format is required.
	buildCmd.MarkFlagRequired("format")

	viper.BindPFlag("build.format", flags.Lookup("format"))
	viper.BindPFlag("build.output", flags.Lookup("output"))

	// CSV indexer.
	flags.Bool("csv-header", false, "CSV file has a header")
	flags.Int("csv-key", 0, "Index of the column containing set keys.")
	flags.Int("csv-domain", 1, "Index of the column containing domain members.")

	viper.BindPFlag("build.csv-header", flags.Lookup("csv-header"))
	viper.BindPFlag("build.csv-key", flags.Lookup("csv-key"))
	viper.BindPFlag("build.csv-domain", flags.Lookup("csv-domain"))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/relab/gorums/byzq"
)

type register struct {
	sync.RWMutex
	state map[string]byzq.Value
}

func main() {
	port := flag.Int("port", 8080, "port to listen on")
	f := flag.Int("f", 0, "fault tolerance, supported values f=1,2,3 (this is ignored if addrs is provided)")
	key := flag.String("key", "", "public/private key file this server")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *f > 0 {
		// we are running only local since we have asked for 3f+1 servers
		done := make(chan bool)
		n := 3**f + 1
		for i := 0; i < n; i++ {
			go serve(*port+i, *key)
		}
		// wait indefinitely
		<-done
	}
	// run only one server
	serve(*port, *key)
}

func serve(port int, keyFile string) {
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()
	if keyFile == "" {
		log.Fatalln("required server keys not provided")
	}
	creds, err := credentials.NewServerTLSFromFile(keyFile+".pem", keyFile+".key")
	if err != nil {
		log.Fatalf("failed to load credentials %v", err)
	}
	opts := []grpc.ServerOption{grpc.Creds(creds)}
	grpcServer := grpc.NewServer(opts...)
	smap := make(map[string]byzq.Value)
	byzq.RegisterRegisterServer(grpcServer, &register{state: smap})
	log.Printf("Server %s running", l.Addr())
	log.Fatal(grpcServer.Serve(l))
}

func (r *register) Read(ctx context.Context, k *byzq.Key) (*byzq.Value, error) {
	r.RLock()
	value := r.state[k.Key]
	r.RUnlock()
	return &value, nil
}

func (r *register) Write(ctx context.Context, v *byzq.Value) (*byzq.WriteResponse, error) {
	wr := &byzq.WriteResponse{Timestamp: v.C.Timestamp}
	r.Lock()
	val, found := r.state[v.C.Key]
	if !found || v.C.Timestamp > val.C.Timestamp {
		r.state[v.C.Key] = *v
	}
	r.Unlock()
	return wr, nil
}
<commit_msg>minor usage update for the f argument<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/relab/gorums/byzq"
)

type register struct {
	sync.RWMutex
	state map[string]byzq.Value
}

func main() {
	port := flag.Int("port", 8080, "port to listen on")
	f := flag.Int("f", 0, "fault tolerance")
	key := flag.String("key", "", "public/private key file this server")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *f > 0 {
		// we are running only local since we have asked for 3f+1 servers
		done := make(chan bool)
		n := 3**f + 1
		for i := 0; i < n; i++ {
			go serve(*port+i, *key)
		}
		// wait indefinitely
		<-done
	}
	// run only one server
	serve(*port, *key)
}

func serve(port int, keyFile string) {
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()
	if keyFile == "" {
		log.Fatalln("required server keys not provided")
	}
	creds, err := credentials.NewServerTLSFromFile(keyFile+".pem", keyFile+".key")
	if err != nil {
		log.Fatalf("failed to load credentials %v", err)
	}
	opts := []grpc.ServerOption{grpc.Creds(creds)}
	grpcServer := grpc.NewServer(opts...)
	smap := make(map[string]byzq.Value)
	byzq.RegisterRegisterServer(grpcServer, &register{state: smap})
	log.Printf("Server %s running", l.Addr())
	log.Fatal(grpcServer.Serve(l))
}

func (r *register) Read(ctx context.Context, k *byzq.Key) (*byzq.Value, error) {
	r.RLock()
	value := r.state[k.Key]
	r.RUnlock()
	return &value, nil
}

func (r *register) Write(ctx context.Context, v *byzq.Value) (*byzq.WriteResponse, error) {
	wr := &byzq.WriteResponse{Timestamp: v.C.Timestamp}
	r.Lock()
	val, found := r.state[v.C.Key]
	if !found || v.C.Timestamp > val.C.Timestamp {
		r.state[v.C.Key] = *v
	}
	r.Unlock()
	return wr, nil
}
<|endoftext|>


<commit_before><commit_msg>bugfix, add commit id to diff<commit_after><|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/rcrowley/go-metrics/librato"

	log "github.com/heroku/log-iss/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/heroku/authenticater"
	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/heroku/rollrus"
)

type ShutdownCh chan struct{}

var Config IssConfig

func awaitShutdownSignals(chs ...ShutdownCh) {
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
	for sig := range sigCh {
		log.WithFields(log.Fields{"at": "shutdown-signal", "signal": sig}).Info()
		for _, ch := range chs {
			ch <- struct{}{}
		}
	}
}

func main() {
	rollrus.SetupLogging(os.Getenv("ROLLBAR_TOKEN"), os.Getenv("ENVIRONMENT"))

	config, err := NewIssConfig()
	if err != nil {
		log.Fatalln(err)
	}

	Config = config

	log.AddHook(&DefaultFieldsHook{log.Fields{"app": "log-iss", "source": Config.Deploy}})

	auth, err := authenticater.NewBasicAuthFromString(Config.Tokens)
	if err != nil {
		log.Fatalln(err)
	}

	forwarderSet := NewForwarderSet(Config)

	shutdownCh := make(ShutdownCh)

	httpServer := NewHttpServer(Config, auth, Fix, forwarderSet)

	go awaitShutdownSignals(httpServer.ShutdownCh, shutdownCh)

	go forwarderSet.Run()

	go func() {
		if err := httpServer.Run(); err != nil {
			log.Fatalln("Unable to start HTTP server:", err)
		}
	}()

	if Config.LibratoOwner != "" && Config.LibratoToken != "" {
		log.WithField("source", Config.LibratoSource).Info("starting librato metrics reporting")
		go librato.Librato(
			config.MetricsRegistry,
			20*time.Second,
			Config.LibratoOwner,
			Config.LibratoToken,
			Config.LibratoSource,
			[]float64{0.50, 0.95, 0.99},
			time.Millisecond,
		)
	}

	log.WithField("at", "start").Info()
	<-shutdownCh
	log.WithField("at", "drain").Info()
	httpServer.Wait()
	log.WithField("at", "exit").Info()
}
<commit_msg>remove the global Config<commit_after>package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/rcrowley/go-metrics/librato"

	log "github.com/heroku/log-iss/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/heroku/authenticater"
	"github.com/heroku/log-iss/Godeps/_workspace/src/github.com/heroku/rollrus"
)

type ShutdownCh chan struct{}

func awaitShutdownSignals(chs ...ShutdownCh) {
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
	for sig := range sigCh {
		log.WithFields(log.Fields{"at": "shutdown-signal", "signal": sig}).Info()
		for _, ch := range chs {
			ch <- struct{}{}
		}
	}
}

func main() {
	rollrus.SetupLogging(os.Getenv("ROLLBAR_TOKEN"), os.Getenv("ENVIRONMENT"))

	config, err := NewIssConfig()
	if err != nil {
		log.Fatalln(err)
	}

	log.AddHook(&DefaultFieldsHook{log.Fields{"app": "log-iss", "source": config.Deploy}})

	auth, err := authenticater.NewBasicAuthFromString(config.Tokens)
	if err != nil {
		log.Fatalln(err)
	}

	forwarderSet := NewForwarderSet(config)

	shutdownCh := make(ShutdownCh)

	httpServer := NewHttpServer(config, auth, Fix, forwarderSet)

	go awaitShutdownSignals(httpServer.ShutdownCh, shutdownCh)

	go forwarderSet.Run()

	go func() {
		if err := httpServer.Run(); err != nil {
			log.Fatalln("Unable to start HTTP server:", err)
		}
	}()

	if config.LibratoOwner != "" && config.LibratoToken != "" {
		log.WithField("source", config.LibratoSource).Info("starting librato metrics reporting")
		go librato.Librato(
			config.MetricsRegistry,
			20*time.Second,
			config.LibratoOwner,
			config.LibratoToken,
			config.LibratoSource,
			[]float64{0.50, 0.95, 0.99},
			time.Millisecond,
		)
	}

	log.WithField("at", "start").Info()
	<-shutdownCh
	log.WithField("at", "drain").Info()
	httpServer.Wait()
	log.WithField("at", "exit").Info()
}
<|endoftext|>


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

package main

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

// Unit tests for internal guru functions

func TestIssue17515(t *testing.T) {
	// Tests handling of symlinks in function guessImportPath
	// If we have Go code inside $HOME/go/src and create a symlink $HOME/src to it
	// there are 4 possible cases that need to be tested:
	// (1) absolute & absolute: GOPATH=$HOME/go/src file=$HOME/go/src/test/test.go
	// (2) absolute & symlink:  GOPATH=$HOME/go/src file=$HOME/src/test/test.go
	// (3) symlink & symlink:   GOPATH=$HOME/src file=$HOME/src/test/test.go
	// (4) symlink & absolute:  GOPATH=$HOME/src file= $HOME/go/src/test/test.go

	// Create a temporary home directory under /tmp
	home, err := ioutil.TempDir(os.TempDir(), "home")
	if err != nil {
		t.Errorf("Unable to create a temporary directory in %s", os.TempDir())
	}

	// create filepath /tmp/home/go/src/test/test.go
	if err = os.MkdirAll(home+"/go/src/test", 0755); err != nil {
		t.Fatal(err)
	}

	// symlink between /tmp/home/go/src and /tmp/home/src
	if err = os.Symlink(home+"/go/src", home+"/src"); err != nil {
		t.Fatal(err)
	}

	// Defer tear down (removing files, symlinks)
	defer os.RemoveAll(home)

	var buildContext = build.Default

	// Success test cases
	for _, test := range []struct {
		gopath, filename, wantSrcdir string
	}{
		{home + "/go", home + "/go/src/test/test.go", home + "/go/src"},
		{home + "/go", home + "/src/test/test.go", home + "/go/src"},
		{home, home + "/src/test/test.go", home + "/src"},
		{home, home + "/go/src/test/test.go", home + "/src"},
	} {
		buildContext.GOPATH = test.gopath
		srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
		if srcdir != test.wantSrcdir || importPath != "test" || err != nil {
			t.Errorf("guessImportPath(%v, %v) = %v, %v, %v; want %v, %v, %v",
				test.filename, test.gopath, srcdir, importPath, err, test.wantSrcdir, "test", "nil")
		}
	}
	// Function to format expected error message
	errFormat := func(fpath string) string {
		return fmt.Sprintf("can't evaluate symlinks of %s", fpath)
	}

	// Failure test cases
	for _, test := range []struct {
		gopath, filename, wantErr string
	}{
		{home + "/go", home + "/go/src/fake/test.go", errFormat(home + "/go/src/fake")},
		{home + "/go", home + "/src/fake/test.go", errFormat(home + "/src/fake")},
		{home, home + "/src/fake/test.go", errFormat(home + "/src/fake")},
		{home, home + "/go/src/fake/test.go", errFormat(home + "/go/src/fake")},
	} {
		buildContext.GOPATH = test.gopath
		srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
		if !strings.HasPrefix(fmt.Sprint(err), test.wantErr) {
			t.Errorf("guessImportPath(%v, %v) = %v, %v, %v; want %v, %v, %v",
				test.filename, test.gopath, srcdir, importPath, err, "", "", test.wantErr)
		}
	}
}
<commit_msg>cmd/guru: avoid testing symlinks on OSes that do not support them<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"runtime"
	"strings"
	"testing"
)

// Unit tests for internal guru functions

func TestIssue17515(t *testing.T) {
	// Tests handling of symlinks in function guessImportPath
	// If we have Go code inside $HOME/go/src and create a symlink $HOME/src to it
	// there are 4 possible cases that need to be tested:
	// (1) absolute & absolute: GOPATH=$HOME/go/src file=$HOME/go/src/test/test.go
	// (2) absolute & symlink:  GOPATH=$HOME/go/src file=$HOME/src/test/test.go
	// (3) symlink & symlink:   GOPATH=$HOME/src file=$HOME/src/test/test.go
	// (4) symlink & absolute:  GOPATH=$HOME/src file= $HOME/go/src/test/test.go

	// Create a temporary home directory under /tmp
	home, err := ioutil.TempDir(os.TempDir(), "home")
	if err != nil {
		t.Errorf("Unable to create a temporary directory in %s", os.TempDir())
	}

	defer os.RemoveAll(home)

	// create filepath /tmp/home/go/src/test/test.go
	if err = os.MkdirAll(home+"/go/src/test", 0755); err != nil {
		t.Fatal(err)
	}

	var buildContext = build.Default

	// Success test cases
	type SuccessTest struct {
		gopath, filename, wantSrcdir string
	}

	successTests := []SuccessTest{
		{home + "/go", home + "/go/src/test/test.go", home + "/go/src"},
	}

	// Add symlink cases if not on Windows, Plan 9
	if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
		// symlink between /tmp/home/go/src and /tmp/home/src
		if err := os.Symlink(home+"/go/src", home+"/src"); err != nil {
			t.Fatal(err)
		}

		successTests = append(successTests, []SuccessTest{
			{home + "/go", home + "/src/test/test.go", home + "/go/src"},
			{home, home + "/go/src/test/test.go", home + "/src"},
			{home, home + "/src/test/test.go", home + "/src"},
		}...)
	}

	for _, test := range successTests {
		buildContext.GOPATH = test.gopath
		srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
		if srcdir != test.wantSrcdir || importPath != "test" || err != nil {
			t.Errorf("guessImportPath(%v, %v) = %v, %v, %v; want %v, %v, %v",
				test.filename, test.gopath, srcdir, importPath, err, test.wantSrcdir, "test", "nil")
		}
	}
	// Function to format expected error message
	errFormat := func(fpath string) string {
		return fmt.Sprintf("can't evaluate symlinks of %s", fpath)
	}

	// Failure test cases
	type FailTest struct {
		gopath, filename, wantErr string
	}

	failTests := []FailTest{
		{home + "/go", home + "/go/src/fake/test.go", errFormat(home + "/go/src/fake")},
	}

	if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
		failTests = append(failTests, []FailTest{
			{home + "/go", home + "/src/fake/test.go", errFormat(home + "/src/fake")},
			{home, home + "/src/fake/test.go", errFormat(home + "/src/fake")},
			{home, home + "/go/src/fake/test.go", errFormat(home + "/go/src/fake")},
		}...)
	}

	for _, test := range failTests {
		buildContext.GOPATH = test.gopath
		srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
		if !strings.HasPrefix(fmt.Sprint(err), test.wantErr) {
			t.Errorf("guessImportPath(%v, %v) = %v, %v, %v; want %v, %v, %v",
				test.filename, test.gopath, srcdir, importPath, err, "", "", test.wantErr)
		}
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	stdtesting "testing"

	"launchpad.net/gnuflag"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/osenv"
	_ "launchpad.net/juju-core/provider/dummy"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

func TestPackage(t *stdtesting.T) {
	testing.MgoTestPackage(t)
}

type MainSuite struct {
	testing.FakeHomeSuite
}

var _ = gc.Suite(&MainSuite{})

var (
	flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing")
)

// Reentrancy point for testing (something as close as possible to) the juju
// tool itself.
func TestRunMain(t *stdtesting.T) {
	if *flagRunMain {
		Main(flag.Args())
	}
}

func badrun(c *gc.C, exit int, args ...string) string {
	localArgs := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "juju"}, args...)
	ps := exec.Command(os.Args[0], localArgs...)
	ps.Env = append(os.Environ(), osenv.JujuHomeEnvKey+"="+osenv.JujuHome())
	output, err := ps.CombinedOutput()
	c.Logf("command output: %q", output)
	if exit != 0 {
		c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit))
	}
	return string(output)
}

func helpText(command cmd.Command, name string) string {
	buff := &bytes.Buffer{}
	info := command.Info()
	info.Name = name
	f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError)
	command.SetFlags(f)
	buff.Write(info.Help(f))
	return buff.String()
}

func deployHelpText() string {
	return helpText(&DeployCommand{}, "juju deploy")
}

func syncToolsHelpText() string {
	return helpText(&SyncToolsCommand{}, "juju sync-tools")
}

func (s *MainSuite) TestRunMain(c *gc.C) {
	defer testing.MakeSampleHome(c).Restore()
	// The test array structure needs to be inline here as some of the
	// expected values below use deployHelpText().  This constructs the deploy
	// command and runs gets the help for it.  When the deploy command is
	// setting the flags (which is needed for the help text) it is accessing
	// osenv.JujuHome(), which panics if SetJujuHome has not been called.
	// The FakeHome from testing does this.
	for i, t := range []struct {
		summary string
		args    []string
		code    int
		out     string
	}{{
		summary: "no params shows help",
		args:    []string{},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help is the same as juju",
		args:    []string{"help"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju --help works too",
		args:    []string{"--help"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help basics is the same as juju",
		args:    []string{"help", "basics"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help foo doesn't exist",
		args:    []string{"help", "foo"},
		code:    1,
		out:     "ERROR unknown command or topic for foo\n",
	}, {
		summary: "juju help deploy shows the default help without global options",
		args:    []string{"help", "deploy"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "juju --help deploy shows the same help as 'help deploy'",
		args:    []string{"--help", "deploy"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "juju deploy --help shows the same help as 'help deploy'",
		args:    []string{"deploy", "--help"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "unknown command",
		args:    []string{"discombobulate"},
		code:    1,
		out:     "ERROR unrecognized command: juju discombobulate\n",
	}, {
		summary: "unknown option before command",
		args:    []string{"--cheese", "bootstrap"},
		code:    2,
		out:     "error: flag provided but not defined: --cheese\n",
	}, {
		summary: "unknown option after command",
		args:    []string{"bootstrap", "--cheese"},
		code:    2,
		out:     "error: flag provided but not defined: --cheese\n",
	}, {
		summary: "known option, but specified before command",
		args:    []string{"--environment", "blah", "bootstrap"},
		code:    2,
		out:     "error: flag provided but not defined: --environment\n",
	}, {
		summary: "juju sync-tools registered properly",
		args:    []string{"sync-tools", "--help"},
		code:    0,
		out:     syncToolsHelpText(),
	}, {
		summary: "check version command registered properly",
		args:    []string{"version"},
		code:    0,
		out:     version.Current.String() + "\n",
	},
	} {
		c.Logf("test %d: %s", i, t.summary)
		out := badrun(c, t.code, t.args...)
		c.Assert(out, gc.Equals, t.out)
	}
}

var brokenConfig = `
'
`

// breakJuju writes a dummy environment with incomplete configuration.
// environMethod is called.
func breakJuju(c *gc.C, environMethod string) (msg string) {
	path := osenv.JujuHomePath("environments.yaml")
	err := ioutil.WriteFile(path, []byte(brokenConfig), 0666)
	c.Assert(err, gc.IsNil)
	return `cannot parse "[^"]*": YAML error.*`
}

func (s *MainSuite) TestActualRunJujuArgsBeforeCommand(c *gc.C) {
	c.Skip("breaks test isolation: lp:1233601")
	defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore()
	// Check global args work when specified before command
	msg := breakJuju(c, "Bootstrap")
	logpath := filepath.Join(c.MkDir(), "log")
	out := badrun(c, 1, "--log-file", logpath, "bootstrap")
	fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg)
	c.Assert(out, gc.Matches, fullmsg)
	content, err := ioutil.ReadFile(logpath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, fullmsg)
}

func (s *MainSuite) TestActualRunJujuArgsAfterCommand(c *gc.C) {
	c.Skip("breaks test isolation: lp:1233601")
	defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore()
	// Check global args work when specified after command
	msg := breakJuju(c, "Bootstrap")
	logpath := filepath.Join(c.MkDir(), "log")
	out := badrun(c, 1, "bootstrap", "--log-file", logpath)
	fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg)
	c.Assert(out, gc.Matches, fullmsg)
	content, err := ioutil.ReadFile(logpath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, fullmsg)
}

var commandNames = []string{
	"add-machine",
	"add-relation",
	"add-unit",
	"api-endpoints",
	"authorised-keys",
	"bootstrap",
	"debug-hooks",
	"debug-log",
	"deploy",
	"destroy-environment",
	"destroy-machine",
	"destroy-relation",
	"destroy-service",
	"destroy-unit",
	"env", // alias for switch
	"expose",
	"generate-config", // alias for init
	"get",
	"get-constraints",
	"get-env", // alias for get-environment
	"get-environment",
	"help",
	"help-tool",
	"init",
	"publish",
	"remove-machine",  // alias for destroy-machine
	"remove-relation", // alias for destroy-relation
	"remove-service",  // alias for destroy-service
	"remove-unit",     // alias for destroy-unit
	"resolved",
	"run",
	"scp",
	"set",
	"set-constraints",
	"set-env", // alias for set-environment
	"set-environment",
	"ssh",
	"stat", // alias for status
	"status",
	"switch",
	"sync-tools",
	"terminate-machine", // alias for destroy-machine
	"unexpose",
	"unset",
	"upgrade-charm",
	"upgrade-juju",
	"version",
}

func (s *MainSuite) TestHelpCommands(c *gc.C) {
	// Check that we have correctly registered all the commands
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "commands")
	lines := strings.Split(out, "\n")
	var names []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		names = append(names, f[0])
	}
	// The names should be output in alphabetical order, so don't sort.
	c.Assert(names, gc.DeepEquals, commandNames)
}

var topicNames = []string{
	"azure",
	"basics",
	"commands",
	"constraints",
	"ec2",
	"global-options",
	"glossary",
	"hpcloud",
	"local",
	"logging",
	"openstack",
	"plugins",
	"topics",
}

func (s *MainSuite) TestHelpTopics(c *gc.C) {
	// Check that we have correctly registered all the topics
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "topics")
	lines := strings.Split(out, "\n")
	var names []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		names = append(names, f[0])
	}
	// The names should be output in alphabetical order, so don't sort.
	c.Assert(names, gc.DeepEquals, topicNames)
}

var globalFlags = []string{
	"--debug .*",
	"--description .*",
	"-h, --help .*",
	"--log-file .*",
	"--logging-config .*",
	"--show-log .*",
	"-v, --verbose .*",
}

func (s *MainSuite) TestHelpGlobalOptions(c *gc.C) {
	// Check that we have correctly registered all the topics
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "global-options")
	c.Assert(out, gc.Matches, `Global Options

These options may be used with any command, and may appear in front of any
command\.(.|\n)*`)
	lines := strings.Split(out, "\n")
	var flags []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 || line[0] != '-' {
			continue
		}
		flags = append(flags, line)
	}
	c.Assert(len(flags), gc.Equals, len(globalFlags))
	for i, line := range flags {
		c.Assert(line, gc.Matches, globalFlags[i])
	}
}
<commit_msg>update help topic names<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	stdtesting "testing"

	"launchpad.net/gnuflag"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/osenv"
	_ "launchpad.net/juju-core/provider/dummy"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

func TestPackage(t *stdtesting.T) {
	testing.MgoTestPackage(t)
}

type MainSuite struct {
	testing.FakeHomeSuite
}

var _ = gc.Suite(&MainSuite{})

var (
	flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing")
)

// Reentrancy point for testing (something as close as possible to) the juju
// tool itself.
func TestRunMain(t *stdtesting.T) {
	if *flagRunMain {
		Main(flag.Args())
	}
}

func badrun(c *gc.C, exit int, args ...string) string {
	localArgs := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "juju"}, args...)
	ps := exec.Command(os.Args[0], localArgs...)
	ps.Env = append(os.Environ(), osenv.JujuHomeEnvKey+"="+osenv.JujuHome())
	output, err := ps.CombinedOutput()
	c.Logf("command output: %q", output)
	if exit != 0 {
		c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit))
	}
	return string(output)
}

func helpText(command cmd.Command, name string) string {
	buff := &bytes.Buffer{}
	info := command.Info()
	info.Name = name
	f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError)
	command.SetFlags(f)
	buff.Write(info.Help(f))
	return buff.String()
}

func deployHelpText() string {
	return helpText(&DeployCommand{}, "juju deploy")
}

func syncToolsHelpText() string {
	return helpText(&SyncToolsCommand{}, "juju sync-tools")
}

func (s *MainSuite) TestRunMain(c *gc.C) {
	defer testing.MakeSampleHome(c).Restore()
	// The test array structure needs to be inline here as some of the
	// expected values below use deployHelpText().  This constructs the deploy
	// command and runs gets the help for it.  When the deploy command is
	// setting the flags (which is needed for the help text) it is accessing
	// osenv.JujuHome(), which panics if SetJujuHome has not been called.
	// The FakeHome from testing does this.
	for i, t := range []struct {
		summary string
		args    []string
		code    int
		out     string
	}{{
		summary: "no params shows help",
		args:    []string{},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help is the same as juju",
		args:    []string{"help"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju --help works too",
		args:    []string{"--help"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help basics is the same as juju",
		args:    []string{"help", "basics"},
		code:    0,
		out:     strings.TrimLeft(helpBasics, "\n"),
	}, {
		summary: "juju help foo doesn't exist",
		args:    []string{"help", "foo"},
		code:    1,
		out:     "ERROR unknown command or topic for foo\n",
	}, {
		summary: "juju help deploy shows the default help without global options",
		args:    []string{"help", "deploy"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "juju --help deploy shows the same help as 'help deploy'",
		args:    []string{"--help", "deploy"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "juju deploy --help shows the same help as 'help deploy'",
		args:    []string{"deploy", "--help"},
		code:    0,
		out:     deployHelpText(),
	}, {
		summary: "unknown command",
		args:    []string{"discombobulate"},
		code:    1,
		out:     "ERROR unrecognized command: juju discombobulate\n",
	}, {
		summary: "unknown option before command",
		args:    []string{"--cheese", "bootstrap"},
		code:    2,
		out:     "error: flag provided but not defined: --cheese\n",
	}, {
		summary: "unknown option after command",
		args:    []string{"bootstrap", "--cheese"},
		code:    2,
		out:     "error: flag provided but not defined: --cheese\n",
	}, {
		summary: "known option, but specified before command",
		args:    []string{"--environment", "blah", "bootstrap"},
		code:    2,
		out:     "error: flag provided but not defined: --environment\n",
	}, {
		summary: "juju sync-tools registered properly",
		args:    []string{"sync-tools", "--help"},
		code:    0,
		out:     syncToolsHelpText(),
	}, {
		summary: "check version command registered properly",
		args:    []string{"version"},
		code:    0,
		out:     version.Current.String() + "\n",
	},
	} {
		c.Logf("test %d: %s", i, t.summary)
		out := badrun(c, t.code, t.args...)
		c.Assert(out, gc.Equals, t.out)
	}
}

var brokenConfig = `
'
`

// breakJuju writes a dummy environment with incomplete configuration.
// environMethod is called.
func breakJuju(c *gc.C, environMethod string) (msg string) {
	path := osenv.JujuHomePath("environments.yaml")
	err := ioutil.WriteFile(path, []byte(brokenConfig), 0666)
	c.Assert(err, gc.IsNil)
	return `cannot parse "[^"]*": YAML error.*`
}

func (s *MainSuite) TestActualRunJujuArgsBeforeCommand(c *gc.C) {
	c.Skip("breaks test isolation: lp:1233601")
	defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore()
	// Check global args work when specified before command
	msg := breakJuju(c, "Bootstrap")
	logpath := filepath.Join(c.MkDir(), "log")
	out := badrun(c, 1, "--log-file", logpath, "bootstrap")
	fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg)
	c.Assert(out, gc.Matches, fullmsg)
	content, err := ioutil.ReadFile(logpath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, fullmsg)
}

func (s *MainSuite) TestActualRunJujuArgsAfterCommand(c *gc.C) {
	c.Skip("breaks test isolation: lp:1233601")
	defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore()
	// Check global args work when specified after command
	msg := breakJuju(c, "Bootstrap")
	logpath := filepath.Join(c.MkDir(), "log")
	out := badrun(c, 1, "bootstrap", "--log-file", logpath)
	fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg)
	c.Assert(out, gc.Matches, fullmsg)
	content, err := ioutil.ReadFile(logpath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, fullmsg)
}

var commandNames = []string{
	"add-machine",
	"add-relation",
	"add-unit",
	"api-endpoints",
	"authorised-keys",
	"bootstrap",
	"debug-hooks",
	"debug-log",
	"deploy",
	"destroy-environment",
	"destroy-machine",
	"destroy-relation",
	"destroy-service",
	"destroy-unit",
	"env", // alias for switch
	"expose",
	"generate-config", // alias for init
	"get",
	"get-constraints",
	"get-env", // alias for get-environment
	"get-environment",
	"help",
	"help-tool",
	"init",
	"publish",
	"remove-machine",  // alias for destroy-machine
	"remove-relation", // alias for destroy-relation
	"remove-service",  // alias for destroy-service
	"remove-unit",     // alias for destroy-unit
	"resolved",
	"run",
	"scp",
	"set",
	"set-constraints",
	"set-env", // alias for set-environment
	"set-environment",
	"ssh",
	"stat", // alias for status
	"status",
	"switch",
	"sync-tools",
	"terminate-machine", // alias for destroy-machine
	"unexpose",
	"unset",
	"upgrade-charm",
	"upgrade-juju",
	"version",
}

func (s *MainSuite) TestHelpCommands(c *gc.C) {
	// Check that we have correctly registered all the commands
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "commands")
	lines := strings.Split(out, "\n")
	var names []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		names = append(names, f[0])
	}
	// The names should be output in alphabetical order, so don't sort.
	c.Assert(names, gc.DeepEquals, commandNames)
}

var topicNames = []string{
	"azure-provider",
	"basics",
	"commands",
	"constraints",
	"ec2-provider",
	"global-options",
	"glossary",
	"hpcloud-provider",
	"local-provider",
	"logging",
	"openstack-provider",
	"plugins",
	"topics",
}

func (s *MainSuite) TestHelpTopics(c *gc.C) {
	// Check that we have correctly registered all the topics
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "topics")
	lines := strings.Split(out, "\n")
	var names []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		names = append(names, f[0])
	}
	// The names should be output in alphabetical order, so don't sort.
	c.Assert(names, gc.DeepEquals, topicNames)
}

var globalFlags = []string{
	"--debug .*",
	"--description .*",
	"-h, --help .*",
	"--log-file .*",
	"--logging-config .*",
	"--show-log .*",
	"-v, --verbose .*",
}

func (s *MainSuite) TestHelpGlobalOptions(c *gc.C) {
	// Check that we have correctly registered all the topics
	// by checking the help output.
	defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir()))
	out := badrun(c, 0, "help", "global-options")
	c.Assert(out, gc.Matches, `Global Options

These options may be used with any command, and may appear in front of any
command\.(.|\n)*`)
	lines := strings.Split(out, "\n")
	var flags []string
	for _, line := range lines {
		f := strings.Fields(line)
		if len(f) == 0 || line[0] != '-' {
			continue
		}
		flags = append(flags, line)
	}
	c.Assert(len(flags), gc.Equals, len(globalFlags))
	for i, line := range flags {
		c.Assert(line, gc.Matches, globalFlags[i])
	}
}
<|endoftext|>