<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

    https://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 route

import (
	"context"
	"fmt"
	"reflect"

	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/sets"

	"knative.dev/pkg/apis/duck"
	"knative.dev/pkg/logging"
	netv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
	"knative.dev/serving/pkg/apis/serving"
	"knative.dev/serving/pkg/apis/serving/v1alpha1"
	"knative.dev/serving/pkg/reconciler/route/config"
	"knative.dev/serving/pkg/reconciler/route/resources"
	resourcenames "knative.dev/serving/pkg/reconciler/route/resources/names"
	"knative.dev/serving/pkg/reconciler/route/traffic"
)

func (c *Reconciler) getClusterIngressForRoute(route *v1alpha1.Route) (*netv1alpha1.ClusterIngress, error) {
	// First, look up the fixed name.
	ciName := resourcenames.ClusterIngress(route)
	ci, err := c.clusterIngressLister.Get(ciName)
	if err == nil {
		return ci, nil
	}

	// If that isn't found, then fallback on the legacy selector-based approach.
	selector := routeOwnerLabelSelector(route)
	ingresses, err := c.clusterIngressLister.List(selector)
	if err != nil {
		return nil, err
	}
	if len(ingresses) == 0 {
		return nil, apierrs.NewNotFound(
			v1alpha1.Resource("clusteringress"), resourcenames.ClusterIngress(route))
	}

	if len(ingresses) > 1 {
		// Return error as we expect only one ingress instance for a route.
		return nil, fmt.Errorf("more than one ClusterIngress are found for route %s/%s: %v", route.Namespace, route.Name, ingresses)
	}

	return ingresses[0], nil
}

func routeOwnerLabelSelector(route *v1alpha1.Route) labels.Selector {
	return labels.Set(map[string]string{
		serving.RouteLabelKey:          route.Name,
		serving.RouteNamespaceLabelKey: route.Namespace,
	}).AsSelector()
}

func (c *Reconciler) deleteClusterIngressesForRoute(route *v1alpha1.Route) error {
	selector := routeOwnerLabelSelector(route).String()

	// We always use DeleteCollection because even with a fixed name, we apply the labels.
	return c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().DeleteCollection(
		nil, metav1.ListOptions{LabelSelector: selector},
	)
}

func (c *Reconciler) reconcileClusterIngress(
	ctx context.Context, r *v1alpha1.Route, desired *netv1alpha1.ClusterIngress) (*netv1alpha1.ClusterIngress, error) {
	logger := logging.FromContext(ctx)
	clusterIngress, err := c.getClusterIngressForRoute(r)
	if apierrs.IsNotFound(err) {
		clusterIngress, err = c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().Create(desired)
		if err != nil {
			logger.Errorw("Failed to create ClusterIngress", zap.Error(err))
			c.Recorder.Eventf(r, corev1.EventTypeWarning, "CreationFailed",
				"Failed to create ClusterIngress for route %s/%s: %v", r.Namespace, r.Name, err)
			return nil, err
		}
		c.Recorder.Eventf(r, corev1.EventTypeNormal, "Created",
			"Created ClusterIngress %q", clusterIngress.Name)
		return clusterIngress, nil
	} else if err != nil {
		return nil, err
	} else {
		// It is notable that one reason for differences here may be defaulting.
		// When that is the case, the Update will end up being a nop because the
		// webhook will bring them into alignment and no new reconciliation will occur.
		if !equality.Semantic.DeepEqual(clusterIngress.Spec, desired.Spec) {
			// Don't modify the informers copy
			origin := clusterIngress.DeepCopy()
			origin.Spec = desired.Spec

			updated, err := c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().Update(origin)
			if err != nil {
				logger.Errorw("Failed to update ClusterIngress", zap.Error(err))
				return nil, err
			}
			return updated, nil
		}
	}

	return clusterIngress, err
}

func (c *Reconciler) deleteServices(namespace string, serviceNames sets.String) error {
	for _, serviceName := range serviceNames.List() {
		if err := c.KubeClientSet.CoreV1().Services(namespace).Delete(serviceName, nil); err != nil {
			c.Logger.Errorw("Failed to delete service", zap.Error(err))
			return err
		}
	}

	return nil
}

func (c *Reconciler) reconcilePlaceholderServices(ctx context.Context, route *v1alpha1.Route, targets map[string]traffic.RevisionTargets, currentServiceNames sets.String) ([]*corev1.Service, error) {
	logger := logging.FromContext(ctx)
	ns := route.Namespace

	names := sets.NewString()
	for name := range targets {
		names.Insert(name)
	}

	var services []*corev1.Service
	for _, name := range names.List() {
		desiredService, err := resources.MakeK8sPlaceholderService(ctx, route, name)
		if err != nil {
			logger.Warnw("Failed to construct placeholder k8s service", zap.Error(err))
			return nil, err
		}

		service, err := c.serviceLister.Services(ns).Get(desiredService.Name)
		if apierrs.IsNotFound(err) {
			// Doesn't exist, create it.
			service, err = c.KubeClientSet.CoreV1().Services(ns).Create(desiredService)
			if err != nil {
				logger.Errorw("Failed to create placeholder service", zap.Error(err))
				c.Recorder.Eventf(route, corev1.EventTypeWarning, "CreationFailed",
					"Failed to create placeholder service %q: %v", desiredService.Name, err)
				return nil, err
			}
			logger.Infof("Created service %s", desiredService.Name)
			c.Recorder.Eventf(route, corev1.EventTypeNormal, "Created", "Created placeholder service %q", desiredService.Name)
		} else if err != nil {
			return nil, err
		} else if !metav1.IsControlledBy(service, route) {
			// Surface an error in the route's status, and return an error.
			route.Status.MarkServiceNotOwned(desiredService.Name)
			return nil, fmt.Errorf("route: %q does not own Service: %q", route.Name, desiredService.Name)
		}

		services = append(services, service)
		delete(currentServiceNames, desiredService.Name)
	}

	// Delete any current services that was no longer desired.
	if err := c.deleteServices(ns, currentServiceNames); err != nil {
		return nil, err
	}

	// TODO(mattmoor): This is where we'd look at the state of the Service and
	// reflect any necessary state into the Route.
	return services, nil
}

func (c *Reconciler) updatePlaceholderServices(ctx context.Context, route *v1alpha1.Route, services []*corev1.Service, ingress *netv1alpha1.ClusterIngress) error {
	logger := logging.FromContext(ctx)
	ns := route.Namespace

	eg, _ := errgroup.WithContext(ctx)
	for _, service := range services {
		service := service
		eg.Go(func() error {
			desiredService, err := resources.MakeK8sService(ctx, route, service.Name, ingress, resources.IsClusterLocalService(service))
			if err != nil {
				// Loadbalancer not ready, no need to update.
				logger.Warnf("Failed to update k8s service: %v", err)
				return nil
			}

			// Make sure that the service has the proper specification.
			if !equality.Semantic.DeepEqual(service.Spec, desiredService.Spec) {
				// Don't modify the informers copy
				existing := service.DeepCopy()
				existing.Spec = desiredService.Spec
				_, err = c.KubeClientSet.CoreV1().Services(ns).Update(existing)
				if err != nil {
					return err
				}
			}
			return nil
		})
	}

	// TODO(mattmoor): This is where we'd look at the state of the Service and
	// reflect any necessary state into the Route.
	return eg.Wait()
}

// Update the Status of the route.  Caller is responsible for checking
// for semantic differences before calling.
func (c *Reconciler) updateStatus(desired *v1alpha1.Route) (*v1alpha1.Route, error) {
	route, err := c.routeLister.Routes(desired.Namespace).Get(desired.Name)
	if err != nil {
		return nil, err
	}
	// If there's nothing to update, just return.
	if reflect.DeepEqual(route.Status, desired.Status) {
		return route, nil
	}
	// Don't modify the informers copy
	existing := route.DeepCopy()
	existing.Status = desired.Status
	return c.ServingClientSet.ServingV1alpha1().Routes(desired.Namespace).UpdateStatus(existing)
}

// Update the lastPinned annotation on revisions we target so they don't get GC'd.
func (c *Reconciler) reconcileTargetRevisions(ctx context.Context, t *traffic.Config, route *v1alpha1.Route) error {
	gcConfig := config.FromContext(ctx).GC
	lpDebounce := gcConfig.StaleRevisionLastpinnedDebounce

	eg, _ := errgroup.WithContext(ctx)
	for _, target := range t.Targets {
		for _, rt := range target {
			tt := rt.TrafficTarget
			eg.Go(func() error {
				rev, err := c.revisionLister.Revisions(route.Namespace).Get(tt.RevisionName)
				if apierrs.IsNotFound(err) {
					c.Logger.Infof("Unable to update lastPinned for missing revision %q", tt.RevisionName)
					return nil
				} else if err != nil {
					return err
				}

				newRev := rev.DeepCopy()
				lastPin, err := newRev.GetLastPinned()
				if err != nil {
					// Missing is an expected error case for a not yet pinned revision.
					if err.(v1alpha1.LastPinnedParseError).Type != v1alpha1.AnnotationParseErrorTypeMissing {
						return err
					}
				} else {
					// Enforce a delay before performing an update on lastPinned to avoid excess churn.
					if lastPin.Add(lpDebounce).After(c.clock.Now()) {
						return nil
					}
				}

				if newRev.Annotations == nil {
					newRev.Annotations = make(map[string]string)
				}

				newRev.ObjectMeta.Annotations[serving.RevisionLastPinnedAnnotationKey] = v1alpha1.RevisionLastPinnedString(c.clock.Now())
				patch, err := duck.CreateMergePatch(rev, newRev)
				if err != nil {
					return err
				}

				if _, err := c.ServingClientSet.ServingV1alpha1().Revisions(route.Namespace).Patch(rev.Name, types.MergePatchType, patch); err != nil {
					c.Logger.Errorf("Unable to set revision annotation: %v", err)
					return err
				}
				return nil
			})
		}
	}
	return eg.Wait()
}

func (c *Reconciler) reconcileCertificate(ctx context.Context, r *v1alpha1.Route, desiredCert *netv1alpha1.Certificate) (*netv1alpha1.Certificate, error) {
	cert, err := c.certificateLister.Certificates(desiredCert.Namespace).Get(desiredCert.Name)
	if apierrs.IsNotFound(err) {
		cert, err = c.ServingClientSet.NetworkingV1alpha1().Certificates(desiredCert.Namespace).Create(desiredCert)
		if err != nil {
			c.Logger.Error("Failed to create Certificate", zap.Error(err))
			c.Recorder.Eventf(r, corev1.EventTypeWarning, "CreationFailed",
				"Failed to create Certificate for route %s/%s: %v", r.Namespace, r.Name, err)
			return nil, err
		}
		c.Recorder.Eventf(r, corev1.EventTypeNormal, "Created",
			"Created Certificate %q/%q", cert.Namespace, cert.Name)
		return cert, nil
	} else if err != nil {
		return nil, err
	} else if !metav1.IsControlledBy(cert, r) {
		// Surface an error in the route's status, and return an error.
		r.Status.MarkCertificateNotOwned(cert.Name)
		return nil, fmt.Errorf("route: %s does not own certificate: %s", r.Name, cert.Name)
	} else {
		if !equality.Semantic.DeepEqual(cert.Spec, desiredCert.Spec) {
			// Don't modify the informers copy
			existing := cert.DeepCopy()
			existing.Spec = desiredCert.Spec
			cert, err := c.ServingClientSet.NetworkingV1alpha1().Certificates(existing.Namespace).Update(existing)
			if err != nil {
				c.Recorder.Eventf(r, corev1.EventTypeWarning, "UpdateFailed",
					"Failed to update Certificate %s/%s: %v", existing.Namespace, existing.Name, err)
				return nil, err
			}
			c.Recorder.Eventf(existing, corev1.EventTypeNormal, "Updated",
				"Updated Spec for Certificate %s/%s", existing.Namespace, existing.Name)
			return cert, nil
		}
	}
	return cert, nil
}
<commit_msg>Use revision.SetLastPinned() in route reconciler (#4789)<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

    https://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 route

import (
	"context"
	"fmt"
	"reflect"

	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/sets"

	"knative.dev/pkg/apis/duck"
	"knative.dev/pkg/logging"
	netv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
	"knative.dev/serving/pkg/apis/serving"
	"knative.dev/serving/pkg/apis/serving/v1alpha1"
	"knative.dev/serving/pkg/reconciler/route/config"
	"knative.dev/serving/pkg/reconciler/route/resources"
	resourcenames "knative.dev/serving/pkg/reconciler/route/resources/names"
	"knative.dev/serving/pkg/reconciler/route/traffic"
)

func (c *Reconciler) getClusterIngressForRoute(route *v1alpha1.Route) (*netv1alpha1.ClusterIngress, error) {
	// First, look up the fixed name.
	ciName := resourcenames.ClusterIngress(route)
	ci, err := c.clusterIngressLister.Get(ciName)
	if err == nil {
		return ci, nil
	}

	// If that isn't found, then fallback on the legacy selector-based approach.
	selector := routeOwnerLabelSelector(route)
	ingresses, err := c.clusterIngressLister.List(selector)
	if err != nil {
		return nil, err
	}
	if len(ingresses) == 0 {
		return nil, apierrs.NewNotFound(
			v1alpha1.Resource("clusteringress"), resourcenames.ClusterIngress(route))
	}

	if len(ingresses) > 1 {
		// Return error as we expect only one ingress instance for a route.
		return nil, fmt.Errorf("more than one ClusterIngress are found for route %s/%s: %v", route.Namespace, route.Name, ingresses)
	}

	return ingresses[0], nil
}

func routeOwnerLabelSelector(route *v1alpha1.Route) labels.Selector {
	return labels.Set(map[string]string{
		serving.RouteLabelKey:          route.Name,
		serving.RouteNamespaceLabelKey: route.Namespace,
	}).AsSelector()
}

func (c *Reconciler) deleteClusterIngressesForRoute(route *v1alpha1.Route) error {
	selector := routeOwnerLabelSelector(route).String()

	// We always use DeleteCollection because even with a fixed name, we apply the labels.
	return c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().DeleteCollection(
		nil, metav1.ListOptions{LabelSelector: selector},
	)
}

func (c *Reconciler) reconcileClusterIngress(
	ctx context.Context, r *v1alpha1.Route, desired *netv1alpha1.ClusterIngress) (*netv1alpha1.ClusterIngress, error) {
	logger := logging.FromContext(ctx)
	clusterIngress, err := c.getClusterIngressForRoute(r)
	if apierrs.IsNotFound(err) {
		clusterIngress, err = c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().Create(desired)
		if err != nil {
			logger.Errorw("Failed to create ClusterIngress", zap.Error(err))
			c.Recorder.Eventf(r, corev1.EventTypeWarning, "CreationFailed",
				"Failed to create ClusterIngress for route %s/%s: %v", r.Namespace, r.Name, err)
			return nil, err
		}
		c.Recorder.Eventf(r, corev1.EventTypeNormal, "Created",
			"Created ClusterIngress %q", clusterIngress.Name)
		return clusterIngress, nil
	} else if err != nil {
		return nil, err
	} else {
		// It is notable that one reason for differences here may be defaulting.
		// When that is the case, the Update will end up being a nop because the
		// webhook will bring them into alignment and no new reconciliation will occur.
		if !equality.Semantic.DeepEqual(clusterIngress.Spec, desired.Spec) {
			// Don't modify the informers copy
			origin := clusterIngress.DeepCopy()
			origin.Spec = desired.Spec

			updated, err := c.ServingClientSet.NetworkingV1alpha1().ClusterIngresses().Update(origin)
			if err != nil {
				logger.Errorw("Failed to update ClusterIngress", zap.Error(err))
				return nil, err
			}
			return updated, nil
		}
	}

	return clusterIngress, err
}

func (c *Reconciler) deleteServices(namespace string, serviceNames sets.String) error {
	for _, serviceName := range serviceNames.List() {
		if err := c.KubeClientSet.CoreV1().Services(namespace).Delete(serviceName, nil); err != nil {
			c.Logger.Errorw("Failed to delete service", zap.Error(err))
			return err
		}
	}

	return nil
}

func (c *Reconciler) reconcilePlaceholderServices(ctx context.Context, route *v1alpha1.Route, targets map[string]traffic.RevisionTargets, currentServiceNames sets.String) ([]*corev1.Service, error) {
	logger := logging.FromContext(ctx)
	ns := route.Namespace

	names := sets.NewString()
	for name := range targets {
		names.Insert(name)
	}

	var services []*corev1.Service
	for _, name := range names.List() {
		desiredService, err := resources.MakeK8sPlaceholderService(ctx, route, name)
		if err != nil {
			logger.Warnw("Failed to construct placeholder k8s service", zap.Error(err))
			return nil, err
		}

		service, err := c.serviceLister.Services(ns).Get(desiredService.Name)
		if apierrs.IsNotFound(err) {
			// Doesn't exist, create it.
			service, err = c.KubeClientSet.CoreV1().Services(ns).Create(desiredService)
			if err != nil {
				logger.Errorw("Failed to create placeholder service", zap.Error(err))
				c.Recorder.Eventf(route, corev1.EventTypeWarning, "CreationFailed",
					"Failed to create placeholder service %q: %v", desiredService.Name, err)
				return nil, err
			}
			logger.Infof("Created service %s", desiredService.Name)
			c.Recorder.Eventf(route, corev1.EventTypeNormal, "Created", "Created placeholder service %q", desiredService.Name)
		} else if err != nil {
			return nil, err
		} else if !metav1.IsControlledBy(service, route) {
			// Surface an error in the route's status, and return an error.
			route.Status.MarkServiceNotOwned(desiredService.Name)
			return nil, fmt.Errorf("route: %q does not own Service: %q", route.Name, desiredService.Name)
		}

		services = append(services, service)
		delete(currentServiceNames, desiredService.Name)
	}

	// Delete any current services that was no longer desired.
	if err := c.deleteServices(ns, currentServiceNames); err != nil {
		return nil, err
	}

	// TODO(mattmoor): This is where we'd look at the state of the Service and
	// reflect any necessary state into the Route.
	return services, nil
}

func (c *Reconciler) updatePlaceholderServices(ctx context.Context, route *v1alpha1.Route, services []*corev1.Service, ingress *netv1alpha1.ClusterIngress) error {
	logger := logging.FromContext(ctx)
	ns := route.Namespace

	eg, _ := errgroup.WithContext(ctx)
	for _, service := range services {
		service := service
		eg.Go(func() error {
			desiredService, err := resources.MakeK8sService(ctx, route, service.Name, ingress, resources.IsClusterLocalService(service))
			if err != nil {
				// Loadbalancer not ready, no need to update.
				logger.Warnf("Failed to update k8s service: %v", err)
				return nil
			}

			// Make sure that the service has the proper specification.
			if !equality.Semantic.DeepEqual(service.Spec, desiredService.Spec) {
				// Don't modify the informers copy
				existing := service.DeepCopy()
				existing.Spec = desiredService.Spec
				_, err = c.KubeClientSet.CoreV1().Services(ns).Update(existing)
				if err != nil {
					return err
				}
			}
			return nil
		})
	}

	// TODO(mattmoor): This is where we'd look at the state of the Service and
	// reflect any necessary state into the Route.
	return eg.Wait()
}

// Update the Status of the route.  Caller is responsible for checking
// for semantic differences before calling.
func (c *Reconciler) updateStatus(desired *v1alpha1.Route) (*v1alpha1.Route, error) {
	route, err := c.routeLister.Routes(desired.Namespace).Get(desired.Name)
	if err != nil {
		return nil, err
	}
	// If there's nothing to update, just return.
	if reflect.DeepEqual(route.Status, desired.Status) {
		return route, nil
	}
	// Don't modify the informers copy
	existing := route.DeepCopy()
	existing.Status = desired.Status
	return c.ServingClientSet.ServingV1alpha1().Routes(desired.Namespace).UpdateStatus(existing)
}

// Update the lastPinned annotation on revisions we target so they don't get GC'd.
func (c *Reconciler) reconcileTargetRevisions(ctx context.Context, t *traffic.Config, route *v1alpha1.Route) error {
	gcConfig := config.FromContext(ctx).GC
	lpDebounce := gcConfig.StaleRevisionLastpinnedDebounce

	eg, _ := errgroup.WithContext(ctx)
	for _, target := range t.Targets {
		for _, rt := range target {
			tt := rt.TrafficTarget
			eg.Go(func() error {
				rev, err := c.revisionLister.Revisions(route.Namespace).Get(tt.RevisionName)
				if apierrs.IsNotFound(err) {
					c.Logger.Infof("Unable to update lastPinned for missing revision %q", tt.RevisionName)
					return nil
				} else if err != nil {
					return err
				}

				newRev := rev.DeepCopy()

				lastPin, err := newRev.GetLastPinned()
				if err != nil {
					// Missing is an expected error case for a not yet pinned revision.
					if err.(v1alpha1.LastPinnedParseError).Type != v1alpha1.AnnotationParseErrorTypeMissing {
						return err
					}
				} else {
					// Enforce a delay before performing an update on lastPinned to avoid excess churn.
					if lastPin.Add(lpDebounce).After(c.clock.Now()) {
						return nil
					}
				}

				newRev.SetLastPinned(c.clock.Now())

				patch, err := duck.CreateMergePatch(rev, newRev)
				if err != nil {
					return err
				}

				if _, err := c.ServingClientSet.ServingV1alpha1().Revisions(route.Namespace).Patch(rev.Name, types.MergePatchType, patch); err != nil {
					c.Logger.Errorf("Unable to set revision annotation: %v", err)
					return err
				}
				return nil
			})
		}
	}
	return eg.Wait()
}

func (c *Reconciler) reconcileCertificate(ctx context.Context, r *v1alpha1.Route, desiredCert *netv1alpha1.Certificate) (*netv1alpha1.Certificate, error) {
	cert, err := c.certificateLister.Certificates(desiredCert.Namespace).Get(desiredCert.Name)
	if apierrs.IsNotFound(err) {
		cert, err = c.ServingClientSet.NetworkingV1alpha1().Certificates(desiredCert.Namespace).Create(desiredCert)
		if err != nil {
			c.Logger.Error("Failed to create Certificate", zap.Error(err))
			c.Recorder.Eventf(r, corev1.EventTypeWarning, "CreationFailed",
				"Failed to create Certificate for route %s/%s: %v", r.Namespace, r.Name, err)
			return nil, err
		}
		c.Recorder.Eventf(r, corev1.EventTypeNormal, "Created",
			"Created Certificate %q/%q", cert.Namespace, cert.Name)
		return cert, nil
	} else if err != nil {
		return nil, err
	} else if !metav1.IsControlledBy(cert, r) {
		// Surface an error in the route's status, and return an error.
		r.Status.MarkCertificateNotOwned(cert.Name)
		return nil, fmt.Errorf("route: %s does not own certificate: %s", r.Name, cert.Name)
	} else {
		if !equality.Semantic.DeepEqual(cert.Spec, desiredCert.Spec) {
			// Don't modify the informers copy
			existing := cert.DeepCopy()
			existing.Spec = desiredCert.Spec
			cert, err := c.ServingClientSet.NetworkingV1alpha1().Certificates(existing.Namespace).Update(existing)
			if err != nil {
				c.Recorder.Eventf(r, corev1.EventTypeWarning, "UpdateFailed",
					"Failed to update Certificate %s/%s: %v", existing.Namespace, existing.Name, err)
				return nil, err
			}
			c.Recorder.Eventf(existing, corev1.EventTypeNormal, "Updated",
				"Updated Spec for Certificate %s/%s", existing.Namespace, existing.Name)
			return cert, nil
		}
	}
	return cert, nil
}
<|endoftext|>


<commit_before>package billing

// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// 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.
//
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

import (
	"context"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/tracing"
	"net/http"
)

// DiscoverTenantsClient is the billing client provides access to billing resources for Azure subscriptions.
type DiscoverTenantsClient struct {
	BaseClient
}

// NewDiscoverTenantsClient creates an instance of the DiscoverTenantsClient client.
func NewDiscoverTenantsClient(subscriptionID string) DiscoverTenantsClient {
	return NewDiscoverTenantsClientWithBaseURI(DefaultBaseURI, subscriptionID)
}

// NewDiscoverTenantsClientWithBaseURI creates an instance of the DiscoverTenantsClient client.
func NewDiscoverTenantsClientWithBaseURI(baseURI string, subscriptionID string) DiscoverTenantsClient {
	return DiscoverTenantsClient{NewWithBaseURI(baseURI, subscriptionID)}
}

// Get gets a Tenant Properties.
// Parameters:
// billingProfileID - billing Profile Id.
func (client DiscoverTenantsClient) Get(ctx context.Context, billingProfileID string) (result DiscoverTenant, err error) {
	if tracing.IsEnabled() {
		ctx = tracing.StartSpan(ctx, fqdn+"/DiscoverTenantsClient.Get")
		defer func() {
			sc := -1
			if result.Response.Response != nil {
				sc = result.Response.Response.StatusCode
			}
			tracing.EndSpan(ctx, sc, err)
		}()
	}
	req, err := client.GetPreparer(ctx, billingProfileID)
	if err != nil {
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", nil, "Failure preparing request")
		return
	}

	resp, err := client.GetSender(req)
	if err != nil {
		result.Response = autorest.Response{Response: resp}
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", resp, "Failure sending request")
		return
	}

	result, err = client.GetResponder(resp)
	if err != nil {
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", resp, "Failure responding to request")
	}

	return
}

// GetPreparer prepares the Get request.
func (client DiscoverTenantsClient) GetPreparer(ctx context.Context, billingProfileID string) (*http.Request, error) {
	pathParameters := map[string]interface{}{
		"billingProfileId": autorest.Encode("path", billingProfileID),
	}

	const APIVersion = "2018-03-01-preview"
	queryParameters := map[string]interface{}{
		"api-version": APIVersion,
	}

	preparer := autorest.CreatePreparer(
		autorest.AsGet(),
		autorest.WithBaseURL(client.BaseURI),
		autorest.WithPathParameters("/providers/Microsoft.Billing/discoverTenants", pathParameters),
		autorest.WithQueryParameters(queryParameters))
	return preparer.Prepare((&http.Request{}).WithContext(ctx))
}

// GetSender sends the Get request. The method will close the
// http.Response Body if it receives an error.
func (client DiscoverTenantsClient) GetSender(req *http.Request) (*http.Response, error) {
	return autorest.SendWithSender(client, req,
		autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...))
}

// GetResponder handles the response to the Get request. The method always
// closes the http.Response Body.
func (client DiscoverTenantsClient) GetResponder(resp *http.Response) (result DiscoverTenant, err error) {
	err = autorest.Respond(
		resp,
		client.ByInspecting(),
		azure.WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByUnmarshallingJSON(&result),
		autorest.ByClosing())
	result.Response = autorest.Response{Response: resp}
	return
}
<commit_msg>Generated from eab3b0f6d4d94e909316970d13c435393d424358<commit_after>package billing

// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// 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.
//
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

import (
	"context"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/tracing"
	"net/http"
)

// DiscoverTenantsClient is the billing client provides access to billing resources for Azure subscriptions.
type DiscoverTenantsClient struct {
	BaseClient
}

// NewDiscoverTenantsClient creates an instance of the DiscoverTenantsClient client.
func NewDiscoverTenantsClient(subscriptionID string) DiscoverTenantsClient {
	return NewDiscoverTenantsClientWithBaseURI(DefaultBaseURI, subscriptionID)
}

// NewDiscoverTenantsClientWithBaseURI creates an instance of the DiscoverTenantsClient client.
func NewDiscoverTenantsClientWithBaseURI(baseURI string, subscriptionID string) DiscoverTenantsClient {
	return DiscoverTenantsClient{NewWithBaseURI(baseURI, subscriptionID)}
}

// Get gets a Tenant Properties.
// Parameters:
// billingProfileID - billing Profile Id.
func (client DiscoverTenantsClient) Get(ctx context.Context, billingProfileID string) (result DiscoverTenant, err error) {
	if tracing.IsEnabled() {
		ctx = tracing.StartSpan(ctx, fqdn+"/DiscoverTenantsClient.Get")
		defer func() {
			sc := -1
			if result.Response.Response != nil {
				sc = result.Response.Response.StatusCode
			}
			tracing.EndSpan(ctx, sc, err)
		}()
	}
	req, err := client.GetPreparer(ctx, billingProfileID)
	if err != nil {
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", nil, "Failure preparing request")
		return
	}

	resp, err := client.GetSender(req)
	if err != nil {
		result.Response = autorest.Response{Response: resp}
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", resp, "Failure sending request")
		return
	}

	result, err = client.GetResponder(resp)
	if err != nil {
		err = autorest.NewErrorWithError(err, "billing.DiscoverTenantsClient", "Get", resp, "Failure responding to request")
	}

	return
}

// GetPreparer prepares the Get request.
func (client DiscoverTenantsClient) GetPreparer(ctx context.Context, billingProfileID string) (*http.Request, error) {
	pathParameters := map[string]interface{}{
		"billingProfileId": autorest.Encode("path", billingProfileID),
	}

	const APIVersion = "2018-03-01-preview"
	queryParameters := map[string]interface{}{
		"api-version": APIVersion,
	}

	preparer := autorest.CreatePreparer(
		autorest.AsGet(),
		autorest.WithBaseURL(client.BaseURI),
		autorest.WithPathParameters("/providers/Microsoft.Billing/billingProfiles/{billingProfileId}/discoverTenants", pathParameters),
		autorest.WithQueryParameters(queryParameters))
	return preparer.Prepare((&http.Request{}).WithContext(ctx))
}

// GetSender sends the Get request. The method will close the
// http.Response Body if it receives an error.
func (client DiscoverTenantsClient) GetSender(req *http.Request) (*http.Response, error) {
	return autorest.SendWithSender(client, req,
		autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...))
}

// GetResponder handles the response to the Get request. The method always
// closes the http.Response Body.
func (client DiscoverTenantsClient) GetResponder(resp *http.Response) (result DiscoverTenant, err error) {
	err = autorest.Respond(
		resp,
		client.ByInspecting(),
		azure.WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByUnmarshallingJSON(&result),
		autorest.ByClosing())
	result.Response = autorest.Response{Response: resp}
	return
}
<|endoftext|>


<commit_before>package qemu

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath("qemu-system-x86_64")
	if err != nil {
		return nil
	}

	return &QemuDriver{
		executable: cmd,
	}
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		qmpSockName: homeDir + QmpSockName,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case int:
			proc, err = os.FindProcess(p.(int))
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	_ = <-qc.waitQmp
	close(qc.waitQmp)
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, cmd *hypervisor.PauseCommand) {
	cause := "doesn't support pause for qemu right now"
	glog.Warning(cause)
	ctx.Hub <- &hypervisor.PauseResult{Cause: cause, Reply: cmd}
}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.BlockDescriptor) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(qc, name, sourceType, filename, format, id)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.BlockDescriptor, callback hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(qc, id, callback)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo) {
	newNetworkAddSession(qc, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent) {
	newNetworkDelSession(qc, n.DeviceName, callback)
}

func (qc *QemuContext) AddCpu(ctx *hypervisor.VmContext, id int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 1)
	commands[0] = &QmpCommand{
		Execute: "cpu-add",
		Arguments: map[string]interface{}{
			"id": id,
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		callback: callback,
	}
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		callback: callback,
	}
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuContext) arguments(ctx *hypervisor.VmContext) []string {
	if ctx.Boot == nil {
		ctx.Boot = &hypervisor.BootConfig{
			CPU:    1,
			Memory: 128,
			Kernel: hypervisor.DefaultKernel,
			Initrd: hypervisor.DefaultInitrd,
		}
	}
	boot := ctx.Boot

	var machineClass, memParams, cpuParams string
	if ctx.Boot.HotAddCpuMem {
		machineClass = "pc-i440fx-2.1"
		memParams = fmt.Sprintf("size=%d,slots=1,maxmem=%s", ctx.Boot.Memory, hypervisor.DefaultMaxMem) // TODO set maxmem to the total memory of the system
		cpuParams = fmt.Sprintf("cpus=%d,maxcpus=%d", ctx.Boot.CPU, hypervisor.DefaultMaxCpus)          // TODO set it to the cpus of the system
	} else {
		machineClass = "pc-i440fx-2.0"
		memParams = strconv.Itoa(ctx.Boot.Memory)
		cpuParams = strconv.Itoa(ctx.Boot.CPU)
	}

	params := []string{
		"-machine", machineClass + ",accel=kvm,usb=off", "-global", "kvm-pit.lost_tick_policy=discard", "-cpu", "host"}
	if _, err := os.Stat("/dev/kvm"); os.IsNotExist(err) {
		glog.V(1).Info("kvm not exist change to no kvm mode")
		params = []string{"-machine", machineClass + ",usb=off", "-cpu", "core2duo"}
	}

	if boot.Bios != "" && boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Bios),
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else if boot.Bios != "" {
		params = append(params,
			"-bios", boot.Bios,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	} else if boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else {
		params = append(params,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	}

	return append(params,
		"-realtime", "mlock=off", "-no-user-config", "-nodefaults", "-no-hpet",
		"-rtc", "base=utc,driftfix=slew", "-no-reboot", "-display", "none", "-boot", "strict=on",
		"-m", memParams, "-smp", cpuParams,
		"-qmp", fmt.Sprintf("unix:%s,server,nowait", qc.qmpSockName), "-serial", fmt.Sprintf("unix:%s,server,nowait", ctx.ConsoleSockName),
		"-device", "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x2", "-device", "virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3",
		"-chardev", fmt.Sprintf("socket,id=charch0,path=%s,server,nowait", ctx.HyperSockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charch0,id=channel0,name=sh.hyper.channel.0",
		"-chardev", fmt.Sprintf("socket,id=charch1,path=%s,server,nowait", ctx.TtySockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charch1,id=channel1,name=sh.hyper.channel.1",
		"-fsdev", fmt.Sprintf("local,id=virtio9p,path=%s,security_model=none", ctx.ShareDir),
		"-device", fmt.Sprintf("virtio-9p-pci,fsdev=virtio9p,mount_tag=%s", hypervisor.ShareDirTag),
	)
}
<commit_msg>enable pause for qemu driver<commit_after>package qemu

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath("qemu-system-x86_64")
	if err != nil {
		return nil
	}

	return &QemuDriver{
		executable: cmd,
	}
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		qmpSockName: homeDir + QmpSockName,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case int:
			proc, err = os.FindProcess(p.(int))
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	_ = <-qc.waitQmp
	close(qc.waitQmp)
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, cmd *hypervisor.PauseCommand) {
	commands := make([]*QmpCommand, 1)

	if cmd.Pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	// TODO: handle qmp error
	qc.qmp <- &QmpSession{
		commands: commands,
		callback: &hypervisor.PauseResult{Reply: cmd},
	}

}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.BlockDescriptor) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(qc, name, sourceType, filename, format, id)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.BlockDescriptor, callback hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(qc, id, callback)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo) {
	newNetworkAddSession(qc, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent) {
	newNetworkDelSession(qc, n.DeviceName, callback)
}

func (qc *QemuContext) AddCpu(ctx *hypervisor.VmContext, id int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 1)
	commands[0] = &QmpCommand{
		Execute: "cpu-add",
		Arguments: map[string]interface{}{
			"id": id,
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		callback: callback,
	}
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		callback: callback,
	}
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuContext) arguments(ctx *hypervisor.VmContext) []string {
	if ctx.Boot == nil {
		ctx.Boot = &hypervisor.BootConfig{
			CPU:    1,
			Memory: 128,
			Kernel: hypervisor.DefaultKernel,
			Initrd: hypervisor.DefaultInitrd,
		}
	}
	boot := ctx.Boot

	var machineClass, memParams, cpuParams string
	if ctx.Boot.HotAddCpuMem {
		machineClass = "pc-i440fx-2.1"
		memParams = fmt.Sprintf("size=%d,slots=1,maxmem=%s", ctx.Boot.Memory, hypervisor.DefaultMaxMem) // TODO set maxmem to the total memory of the system
		cpuParams = fmt.Sprintf("cpus=%d,maxcpus=%d", ctx.Boot.CPU, hypervisor.DefaultMaxCpus)          // TODO set it to the cpus of the system
	} else {
		machineClass = "pc-i440fx-2.0"
		memParams = strconv.Itoa(ctx.Boot.Memory)
		cpuParams = strconv.Itoa(ctx.Boot.CPU)
	}

	params := []string{
		"-machine", machineClass + ",accel=kvm,usb=off", "-global", "kvm-pit.lost_tick_policy=discard", "-cpu", "host"}
	if _, err := os.Stat("/dev/kvm"); os.IsNotExist(err) {
		glog.V(1).Info("kvm not exist change to no kvm mode")
		params = []string{"-machine", machineClass + ",usb=off", "-cpu", "core2duo"}
	}

	if boot.Bios != "" && boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Bios),
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else if boot.Bios != "" {
		params = append(params,
			"-bios", boot.Bios,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	} else if boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else {
		params = append(params,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	}

	return append(params,
		"-realtime", "mlock=off", "-no-user-config", "-nodefaults", "-no-hpet",
		"-rtc", "base=utc,driftfix=slew", "-no-reboot", "-display", "none", "-boot", "strict=on",
		"-m", memParams, "-smp", cpuParams,
		"-qmp", fmt.Sprintf("unix:%s,server,nowait", qc.qmpSockName), "-serial", fmt.Sprintf("unix:%s,server,nowait", ctx.ConsoleSockName),
		"-device", "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x2", "-device", "virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3",
		"-chardev", fmt.Sprintf("socket,id=charch0,path=%s,server,nowait", ctx.HyperSockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charch0,id=channel0,name=sh.hyper.channel.0",
		"-chardev", fmt.Sprintf("socket,id=charch1,path=%s,server,nowait", ctx.TtySockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charch1,id=channel1,name=sh.hyper.channel.1",
		"-fsdev", fmt.Sprintf("local,id=virtio9p,path=%s,security_model=none", ctx.ShareDir),
		"-device", fmt.Sprintf("virtio-9p-pci,fsdev=virtio9p,mount_tag=%s", hypervisor.ShareDirTag),
	)
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru 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 cloudstack

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/tsuru/tsuru/iaas"
)

func init() {
	iaas.RegisterIaasProvider("cloudstack", NewCloudstackIaaS())
}

type CloudstackIaaS struct {
	base iaas.UserDataIaaS
}

func NewCloudstackIaaS() *CloudstackIaaS {
	return &CloudstackIaaS{base: iaas.UserDataIaaS{NamedIaaS: iaas.NamedIaaS{BaseIaaSName: "cloudstack"}}}
}

func (i *CloudstackIaaS) Clone(name string) iaas.IaaS {
	clone := *i
	clone.base.IaaSName = name
	return &clone
}

func (i *CloudstackIaaS) Describe() string {
	return `Cloudstack IaaS required params:
  networkids=<networkids>                   Your network uuid
  templateid=<templateid>                   Your template uuid
  serviceofferingid=<serviceofferingid>     Your service offering uuid
  zoneid=<zoneid>                           Your zone uuid

Further params will also be sent to cloudstack's deployVirtualMachine command.
`
}

func validateParams(params map[string]string) error {
	mandatory := []string{"networkids", "templateid", "serviceofferingid", "zoneid"}
	for _, p := range mandatory {
		_, isPresent := params[p]
		if !isPresent {
			return fmt.Errorf("param %q is mandatory", p)
		}
	}
	return nil
}

func (i *CloudstackIaaS) do(cmd string, params map[string]string, result interface{}) error {
	url, err := i.buildUrl(cmd, params)
	if err != nil {
		return err
	}
	client := http.DefaultClient
	client.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Unexpected response code for %s command %d: %s", cmd, resp.StatusCode, string(body))
	}
	if result != nil {
		err = json.Unmarshal(body, result)
		if err != nil {
			return fmt.Errorf("Unexpected result data for %s command: %s - Body: %s", cmd, err.Error(), string(body))
		}
	}
	return nil
}

func (i *CloudstackIaaS) DeleteMachine(machine *iaas.Machine) error {
	var volumesRsp ListVolumesResponse
	err := i.do("listVolumes", ApiParams{
		"virtualmachineid": machine.Id,
		"projectid":        machine.CreationParams["projectid"],
	}, &volumesRsp)
	if err != nil {
		return err
	}
	var destroyData DestroyVirtualMachineResponse
	err = i.do("destroyVirtualMachine", ApiParams{
		"id": machine.Id,
	}, &destroyData)
	if err != nil {
		return err
	}
	_, err = i.waitForAsyncJob(destroyData.DestroyVirtualMachineResponse.JobID)
	if err != nil {
		return err
	}
	for _, vol := range volumesRsp.ListVolumesResponse.Volume {
		if vol.Type != DISK_TYPE_DATADISK {
			continue
		}
		var detachRsp DetachVolumeResponse
		err = i.do("detachVolume", ApiParams{"id": vol.ID}, &detachRsp)
		if err != nil {
			return err
		}
		_, err = i.waitForAsyncJob(detachRsp.DetachVolumeResponse.JobID)
		if err != nil {
			return err
		}
		err = i.do("deleteVolume", ApiParams{"id": vol.ID}, nil)
		if err != nil {
			return err
		}
	}
	return nil
}

func (i *CloudstackIaaS) CreateMachine(params map[string]string) (*iaas.Machine, error) {
	err := validateParams(params)
	if err != nil {
		return nil, err
	}
	userData, err := i.base.ReadUserData()
	if err != nil {
		return nil, err
	}
	paramsCopy := make(map[string]string)
	for k, v := range params {
		paramsCopy[k] = v
	}
	paramsCopy["userdata"] = userData
	var vmStatus DeployVirtualMachineResponse
	err = i.do("deployVirtualMachine", paramsCopy, &vmStatus)
	if err != nil {
		return nil, err
	}
	IpAddress, err := i.waitVMIsCreated(vmStatus.DeployVirtualMachineResponse.JobID, vmStatus.DeployVirtualMachineResponse.ID, params["projectid"])
	if err != nil {
		return nil, err
	}
	m := &iaas.Machine{
		Id:      vmStatus.DeployVirtualMachineResponse.ID,
		Address: IpAddress,
		Status:  "running",
	}
	return m, nil
}

func (i *CloudstackIaaS) buildUrl(command string, params map[string]string) (string, error) {
	apiKey, err := i.base.GetConfigString("api-key")
	if err != nil {
		return "", err
	}
	secretKey, err := i.base.GetConfigString("secret-key")
	if err != nil {
		return "", err
	}
	params["command"] = command
	params["response"] = "json"
	params["apiKey"] = apiKey
	var sorted_keys []string
	for k := range params {
		sorted_keys = append(sorted_keys, k)
	}
	sort.Strings(sorted_keys)
	var string_params []string
	for _, key := range sorted_keys {
		queryStringParam := fmt.Sprintf("%s=%s", key, url.QueryEscape(params[key]))
		string_params = append(string_params, queryStringParam)
	}
	queryString := strings.Join(string_params, "&")
	digest := hmac.New(sha1.New, []byte(secretKey))
	digest.Write([]byte(strings.ToLower(queryString)))
	signature := base64.StdEncoding.EncodeToString(digest.Sum(nil))
	cloudstackUrl, err := i.base.GetConfigString("url")
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s?%s&signature=%s", cloudstackUrl, queryString, url.QueryEscape(signature)), nil
}

func (i *CloudstackIaaS) waitForAsyncJob(jobId string) (QueryAsyncJobResultResponse, error) {
	count := 0
	maxTry := 300
	var jobResponse QueryAsyncJobResultResponse
	for count < maxTry {
		err := i.do("queryAsyncJobResult", ApiParams{"jobid": jobId}, &jobResponse)
		if err != nil {
			return jobResponse, err
		}
		if jobResponse.QueryAsyncJobResultResponse.JobStatus != JOB_STATUS_IN_PROGRESS {
			if jobResponse.QueryAsyncJobResultResponse.JobStatus == JOB_STATUS_FAILED {
				return jobResponse, fmt.Errorf("Job failed to complete: %#v", jobResponse.QueryAsyncJobResultResponse.JobResult)
			}
			return jobResponse, nil
		}
		count = count + 1
		time.Sleep(time.Second)
	}
	return jobResponse, fmt.Errorf("Maximum number of retries waiting for job %q", jobId)
}

func (i *CloudstackIaaS) waitVMIsCreated(jobId, machineId, projectId string) (string, error) {
	_, err := i.waitForAsyncJob(jobId)
	if err != nil {
		return "", err
	}
	var machineInfo ListVirtualMachinesResponse
	err = i.do("listVirtualMachines", ApiParams{
		"id":        machineId,
		"projectid": projectId,
	}, &machineInfo)
	if err != nil {
		return "", err
	}
	return machineInfo.ListVirtualMachinesResponse.VirtualMachine[0].Nic[0].IpAddress, nil
}
<commit_msg>iaas/cloudstack: turn off insecure skip verify<commit_after>// Copyright 2014 tsuru 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 cloudstack

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/tsuru/tsuru/iaas"
)

func init() {
	iaas.RegisterIaasProvider("cloudstack", NewCloudstackIaaS())
}

type CloudstackIaaS struct {
	base iaas.UserDataIaaS
}

func NewCloudstackIaaS() *CloudstackIaaS {
	return &CloudstackIaaS{base: iaas.UserDataIaaS{NamedIaaS: iaas.NamedIaaS{BaseIaaSName: "cloudstack"}}}
}

func (i *CloudstackIaaS) Clone(name string) iaas.IaaS {
	clone := *i
	clone.base.IaaSName = name
	return &clone
}

func (i *CloudstackIaaS) Describe() string {
	return `Cloudstack IaaS required params:
  networkids=<networkids>                   Your network uuid
  templateid=<templateid>                   Your template uuid
  serviceofferingid=<serviceofferingid>     Your service offering uuid
  zoneid=<zoneid>                           Your zone uuid

Further params will also be sent to cloudstack's deployVirtualMachine command.
`
}

func validateParams(params map[string]string) error {
	mandatory := []string{"networkids", "templateid", "serviceofferingid", "zoneid"}
	for _, p := range mandatory {
		_, isPresent := params[p]
		if !isPresent {
			return fmt.Errorf("param %q is mandatory", p)
		}
	}
	return nil
}

func (i *CloudstackIaaS) do(cmd string, params map[string]string, result interface{}) error {
	url, err := i.buildUrl(cmd, params)
	if err != nil {
		return err
	}
	client := http.DefaultClient
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Unexpected response code for %s command %d: %s", cmd, resp.StatusCode, string(body))
	}
	if result != nil {
		err = json.Unmarshal(body, result)
		if err != nil {
			return fmt.Errorf("Unexpected result data for %s command: %s - Body: %s", cmd, err.Error(), string(body))
		}
	}
	return nil
}

func (i *CloudstackIaaS) DeleteMachine(machine *iaas.Machine) error {
	var volumesRsp ListVolumesResponse
	err := i.do("listVolumes", ApiParams{
		"virtualmachineid": machine.Id,
		"projectid":        machine.CreationParams["projectid"],
	}, &volumesRsp)
	if err != nil {
		return err
	}
	var destroyData DestroyVirtualMachineResponse
	err = i.do("destroyVirtualMachine", ApiParams{
		"id": machine.Id,
	}, &destroyData)
	if err != nil {
		return err
	}
	_, err = i.waitForAsyncJob(destroyData.DestroyVirtualMachineResponse.JobID)
	if err != nil {
		return err
	}
	for _, vol := range volumesRsp.ListVolumesResponse.Volume {
		if vol.Type != DISK_TYPE_DATADISK {
			continue
		}
		var detachRsp DetachVolumeResponse
		err = i.do("detachVolume", ApiParams{"id": vol.ID}, &detachRsp)
		if err != nil {
			return err
		}
		_, err = i.waitForAsyncJob(detachRsp.DetachVolumeResponse.JobID)
		if err != nil {
			return err
		}
		err = i.do("deleteVolume", ApiParams{"id": vol.ID}, nil)
		if err != nil {
			return err
		}
	}
	return nil
}

func (i *CloudstackIaaS) CreateMachine(params map[string]string) (*iaas.Machine, error) {
	err := validateParams(params)
	if err != nil {
		return nil, err
	}
	userData, err := i.base.ReadUserData()
	if err != nil {
		return nil, err
	}
	paramsCopy := make(map[string]string)
	for k, v := range params {
		paramsCopy[k] = v
	}
	paramsCopy["userdata"] = userData
	var vmStatus DeployVirtualMachineResponse
	err = i.do("deployVirtualMachine", paramsCopy, &vmStatus)
	if err != nil {
		return nil, err
	}
	IpAddress, err := i.waitVMIsCreated(vmStatus.DeployVirtualMachineResponse.JobID, vmStatus.DeployVirtualMachineResponse.ID, params["projectid"])
	if err != nil {
		return nil, err
	}
	m := &iaas.Machine{
		Id:      vmStatus.DeployVirtualMachineResponse.ID,
		Address: IpAddress,
		Status:  "running",
	}
	return m, nil
}

func (i *CloudstackIaaS) buildUrl(command string, params map[string]string) (string, error) {
	apiKey, err := i.base.GetConfigString("api-key")
	if err != nil {
		return "", err
	}
	secretKey, err := i.base.GetConfigString("secret-key")
	if err != nil {
		return "", err
	}
	params["command"] = command
	params["response"] = "json"
	params["apiKey"] = apiKey
	var sorted_keys []string
	for k := range params {
		sorted_keys = append(sorted_keys, k)
	}
	sort.Strings(sorted_keys)
	var string_params []string
	for _, key := range sorted_keys {
		queryStringParam := fmt.Sprintf("%s=%s", key, url.QueryEscape(params[key]))
		string_params = append(string_params, queryStringParam)
	}
	queryString := strings.Join(string_params, "&")
	digest := hmac.New(sha1.New, []byte(secretKey))
	digest.Write([]byte(strings.ToLower(queryString)))
	signature := base64.StdEncoding.EncodeToString(digest.Sum(nil))
	cloudstackUrl, err := i.base.GetConfigString("url")
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s?%s&signature=%s", cloudstackUrl, queryString, url.QueryEscape(signature)), nil
}

func (i *CloudstackIaaS) waitForAsyncJob(jobId string) (QueryAsyncJobResultResponse, error) {
	count := 0
	maxTry := 300
	var jobResponse QueryAsyncJobResultResponse
	for count < maxTry {
		err := i.do("queryAsyncJobResult", ApiParams{"jobid": jobId}, &jobResponse)
		if err != nil {
			return jobResponse, err
		}
		if jobResponse.QueryAsyncJobResultResponse.JobStatus != JOB_STATUS_IN_PROGRESS {
			if jobResponse.QueryAsyncJobResultResponse.JobStatus == JOB_STATUS_FAILED {
				return jobResponse, fmt.Errorf("Job failed to complete: %#v", jobResponse.QueryAsyncJobResultResponse.JobResult)
			}
			return jobResponse, nil
		}
		count = count + 1
		time.Sleep(time.Second)
	}
	return jobResponse, fmt.Errorf("Maximum number of retries waiting for job %q", jobId)
}

func (i *CloudstackIaaS) waitVMIsCreated(jobId, machineId, projectId string) (string, error) {
	_, err := i.waitForAsyncJob(jobId)
	if err != nil {
		return "", err
	}
	var machineInfo ListVirtualMachinesResponse
	err = i.do("listVirtualMachines", ApiParams{
		"id":        machineId,
		"projectid": projectId,
	}, &machineInfo)
	if err != nil {
		return "", err
	}
	return machineInfo.ListVirtualMachinesResponse.VirtualMachine[0].Nic[0].IpAddress, nil
}
<|endoftext|>


<commit_before>// Package mediate provides retryable, failure
// tolerant and rate limited HTTP Transport / RoundTripper interfaces
// for all net.Http client users.
package mediate

import (
	"bytes"
	"io/ioutil"
	"net/http"
)

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct. Bodies
// should be deep copied here due to closing.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	return r2
}

// cloneResponse makes a new shallow clone of an http.Response
func cloneResponse(r *http.Response) *http.Response {
	// shallow copy of the struct
	r2 := new(http.Response)
	*r2 = *r
	return r2
}

type canceler interface {
	CancelRequest(*http.Request)
}

// FixedRetry transport - on any failure, the request will be retried
// at most count times.
type fixedRetries struct {
	transport      http.RoundTripper
	retriesAllowed int
}

// FixedRetries will issue the same request up to count times, if
// an explicit error (socket error, transport error) is returned
// from the underlying RoundTripper. This implementation performs
// no backoff and does not look at the http.Response status codes.
func FixedRetries(count int, transport http.RoundTripper) http.RoundTripper {
	if transport == nil {
		transport = http.DefaultTransport
	}
	return &fixedRetries{transport: transport, retriesAllowed: count}
}

func (t *fixedRetries) CancelRequest(req *http.Request) {
	tr, ok := t.transport.(canceler)
	if ok {
		tr.CancelRequest(req)
	}
}

func (t *fixedRetries) RoundTrip(req *http.Request) (*http.Response, error) {
	var lastError error
	for retry := 0; retry < t.retriesAllowed; retry++ {
		nreq := cloneRequest(req)
		var resp *http.Response
		resp, lastError = t.transport.RoundTrip(nreq)
		if lastError == nil {
			return resp, nil
		}
	}
	return nil, lastError
}

/////////////////////////

type reliableBody struct {
	transport http.RoundTripper
}

// ReliableBody builds a RoundTripper which will consume all
// of the response Body into a new memory buffer, and returns
// the response with this alternate Body.
//
// This is less memory efficient compared to streaming the response
// from the socket directly, but allows API to work with complete
// operations making retries and other actions trivial.
func ReliableBody(transport http.RoundTripper) http.RoundTripper {
	return &reliableBody{transport}
}

func (t *reliableBody) CancelRequest(req *http.Request) {
	tr, ok := t.transport.(canceler)
	if ok {
		tr.CancelRequest(req)
	}
}

func (t *reliableBody) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := t.transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	buf := bytes.NewReader(body)
	resp.Body = ioutil.NopCloser(buf)
	return resp, nil
}
<commit_msg>A basic rate limiter for API calls<commit_after>// Package mediate provides retryable, failure
// tolerant and rate limited HTTP Transport / RoundTripper interfaces
// for all net.Http client users.
package mediate

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"time"
)

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct. Bodies
// should be deep copied here due to closing.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	return r2
}

// cloneResponse makes a new shallow clone of an http.Response
func cloneResponse(r *http.Response) *http.Response {
	// shallow copy of the struct
	r2 := new(http.Response)
	*r2 = *r
	return r2
}

type canceler interface {
	CancelRequest(*http.Request)
}

// FixedRetry transport - on any failure, the request will be retried
// at most count times.
type fixedRetries struct {
	transport      http.RoundTripper
	retriesAllowed int
}

// FixedRetries will issue the same request up to count times, if
// an explicit error (socket error, transport error) is returned
// from the underlying RoundTripper. This implementation performs
// no backoff and does not look at the http.Response status codes.
func FixedRetries(count int, transport http.RoundTripper) http.RoundTripper {
	if transport == nil {
		transport = http.DefaultTransport
	}
	return &fixedRetries{transport: transport, retriesAllowed: count}
}

func (t *fixedRetries) CancelRequest(req *http.Request) {
	tr, ok := t.transport.(canceler)
	if ok {
		tr.CancelRequest(req)
	}
}

func (t *fixedRetries) RoundTrip(req *http.Request) (*http.Response, error) {
	var lastError error
	for retry := 0; retry < t.retriesAllowed; retry++ {
		nreq := cloneRequest(req)
		var resp *http.Response
		resp, lastError = t.transport.RoundTrip(nreq)
		if lastError == nil {
			return resp, nil
		}
	}
	return nil, lastError
}

/////////////////////////

type reliableBody struct {
	transport http.RoundTripper
}

// ReliableBody builds a RoundTripper which will consume all
// of the response Body into a new memory buffer, and returns
// the response with this alternate Body.
//
// This is less memory efficient compared to streaming the response
// from the socket directly, but allows API to work with complete
// operations making retries and other actions trivial.
func ReliableBody(transport http.RoundTripper) http.RoundTripper {
	return &reliableBody{transport}
}

func (t *reliableBody) CancelRequest(req *http.Request) {
	tr, ok := t.transport.(canceler)
	if ok {
		tr.CancelRequest(req)
	}
}

func (t *reliableBody) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := t.transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	buf := bytes.NewReader(body)
	resp.Body = ioutil.NopCloser(buf)
	return resp, nil
}

/////////////////////////

type rateLimit struct {
	requests  int
	quantum   time.Duration
	transport http.RoundTripper
	limiter   chan int
}

func RateLimit(requests int, every time.Duration, transport http.RoundTripper) http.RoundTripper {
	q := every / 10
	rl := &rateLimit{requests: requests / 10,
		quantum: q, transport: transport,
		limiter: make(chan int)}

	go rl.ticker()
	return rl
}

func (r *rateLimit) ticker() {
	tick := time.NewTicker(r.quantum)
	limit := r.requests
	for {
		limit--
		select {
		case r.limiter <- 0:
			// Allow a request
		case _ = <-tick.C:
			// Expired? reset the counter
			limit = r.requests
		}
		// Out of tokens? Wait until the timer expires
		if limit <= 0 {
			_ = <-tick.C
		}
	}
}

func (r *rateLimit) RoundTrip(req *http.Request) (*http.Response, error) {
	_ = <-r.limiter
	return r.RoundTrip(req)
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to the Message struct

package discordgo

import (
	"io"
	"regexp"
	"strconv"
	"strings"
)

// MessageType is the type of Message
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault MessageType = iota
	MessageTypeRecipientAdd
	MessageTypeRecipientRemove
	MessageTypeCall
	MessageTypeChannelNameChange
	MessageTypeChannelIconChange
	MessageTypeChannelPinnedMessage
	MessageTypeGuildMemberJoin
)

// A Message stores all data related to a specific Discord message.
type Message struct {
	// The ID of the message.
	ID int64 `json:"id,string"`

	// The ID of the channel in which the message was sent.
	ChannelID int64 `json:"channel_id,string"`

	// The ID of the guild in which the message was sent.
	GuildID int64 `json:"guild_id,string,omitempty"`

	// The content of the message.
	Content string `json:"content"`

	// The time at which the messsage was sent.
	// CAUTION: this field may be removed in a
	// future API version; it is safer to calculate
	// the creation time via the ID.
	Timestamp Timestamp `json:"timestamp"`

	// The time at which the last edit of the message
	// occurred, if it has been edited.
	EditedTimestamp Timestamp `json:"edited_timestamp"`

	// The roles mentioned in the message.
	MentionRoles IDSlice `json:"mention_roles,string"`

	// Whether the message is text-to-speech.
	Tts bool `json:"tts"`

	// Whether the message mentions everyone.
	MentionEveryone bool `json:"mention_everyone"`

	// The author of the message. This is not guaranteed to be a
	// valid user (webhook-sent messages do not possess a full author).
	Author *User `json:"author"`

	// A list of attachments present in the message.
	Attachments []*MessageAttachment `json:"attachments"`

	// A list of embeds present in the message. Multiple
	// embeds can currently only be sent by webhooks.
	Embeds []*MessageEmbed `json:"embeds"`

	// A list of users mentioned in the message.
	Mentions []*User `json:"mentions"`

	// A list of reactions to the message.
	Reactions []*MessageReactions `json:"reactions"`

	// The type of the message.
	Type MessageType `json:"type"`

	WebhookID int64 `json:"webhook_id,string"`
}

func (m *Message) GetGuildID() int64 {
	return m.GuildID
}

func (m *Message) GetChannelID() int64 {
	return m.ChannelID
}

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content string        `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`
	Tts     bool          `json:"tts"`
	Files   []*File       `json:"-"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content *string       `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`

	ID      int64
	Channel int64
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID int64, messageID int64) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embed = embed
	return m
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	ProxyURL string `json:"proxy_url"`
	Filename string `json:"filename"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	Size     int    `json:"size"`
}

// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        string                 `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+StrID(user.ID)+">", "@"+user.Username,
			"<@!"+StrID(user.ID)+">", "@"+user.Username,
		).Replace(content)
	}
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+StrID(user.ID)+">", "@"+user.Username,
			"<@!"+StrID(user.ID)+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<@&"+StrID(role.ID)+">", "@"+role.Name, -1)
	}

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		id, err := strconv.ParseInt(mention[2:len(mention)-1], 10, 64)
		if err != nil {
			return mention
		}

		channel, err := s.State.Channel(id)
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}
<commit_msg>add member to message struct<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to the Message struct

package discordgo

import (
	"io"
	"regexp"
	"strconv"
	"strings"
)

// MessageType is the type of Message
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault MessageType = iota
	MessageTypeRecipientAdd
	MessageTypeRecipientRemove
	MessageTypeCall
	MessageTypeChannelNameChange
	MessageTypeChannelIconChange
	MessageTypeChannelPinnedMessage
	MessageTypeGuildMemberJoin
)

// A Message stores all data related to a specific Discord message.
type Message struct {
	// The ID of the message.
	ID int64 `json:"id,string"`

	// The ID of the channel in which the message was sent.
	ChannelID int64 `json:"channel_id,string"`

	// The ID of the guild in which the message was sent.
	GuildID int64 `json:"guild_id,string,omitempty"`

	// The content of the message.
	Content string `json:"content"`

	// The time at which the messsage was sent.
	// CAUTION: this field may be removed in a
	// future API version; it is safer to calculate
	// the creation time via the ID.
	Timestamp Timestamp `json:"timestamp"`

	// The time at which the last edit of the message
	// occurred, if it has been edited.
	EditedTimestamp Timestamp `json:"edited_timestamp"`

	// The roles mentioned in the message.
	MentionRoles IDSlice `json:"mention_roles,string"`

	// Whether the message is text-to-speech.
	Tts bool `json:"tts"`

	// Whether the message mentions everyone.
	MentionEveryone bool `json:"mention_everyone"`

	// The author of the message. This is not guaranteed to be a
	// valid user (webhook-sent messages do not possess a full author).
	Author *User `json:"author"`

	// A list of attachments present in the message.
	Attachments []*MessageAttachment `json:"attachments"`

	// A list of embeds present in the message. Multiple
	// embeds can currently only be sent by webhooks.
	Embeds []*MessageEmbed `json:"embeds"`

	// A list of users mentioned in the message.
	Mentions []*User `json:"mentions"`

	// A list of reactions to the message.
	Reactions []*MessageReactions `json:"reactions"`

	// The type of the message.
	Type MessageType `json:"type"`

	WebhookID int64 `json:"webhook_id,string"`

	Member *Member `json:"member"`
}

func (m *Message) GetGuildID() int64 {
	return m.GuildID
}

func (m *Message) GetChannelID() int64 {
	return m.ChannelID
}

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content string        `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`
	Tts     bool          `json:"tts"`
	Files   []*File       `json:"-"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content *string       `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`

	ID      int64
	Channel int64
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID int64, messageID int64) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embed = embed
	return m
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	ProxyURL string `json:"proxy_url"`
	Filename string `json:"filename"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	Size     int    `json:"size"`
}

// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        string                 `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+StrID(user.ID)+">", "@"+user.Username,
			"<@!"+StrID(user.ID)+">", "@"+user.Username,
		).Replace(content)
	}
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+StrID(user.ID)+">", "@"+user.Username,
			"<@!"+StrID(user.ID)+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<@&"+StrID(role.ID)+">", "@"+role.Name, -1)
	}

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		id, err := strconv.ParseInt(mention[2:len(mention)-1], 10, 64)
		if err != nil {
			return mention
		}

		channel, err := s.State.Channel(id)
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}
<|endoftext|>


<commit_before>package femebe

import (
	"encoding/binary"
	"errors"
	"io"
)

var ErrTooLarge = errors.New("Message buffering size limit exceeded")

const MSG_TYPE_FIRST = '\000'

type Message struct {
	// Constant-width header
	msgType byte
	sz      uint32

	buffered Reader
	union    io.Reader

	// The rest of the message yet to be read.
	future io.Reader
}

func (m *Message) MsgType() byte {
	return m.msgType
}

func (m *Message) Payload() io.Reader {
	return m.union
}

func (m *Message) Size() uint32 {
	return m.sz
}

func (m *Message) IsBuffered() bool {
	return m.future == nil
}

func (m *Message) Force() ([]byte, error) {
	if m.IsBuffered() {
		return m.buffered.Bytes(), nil
	}

	payloadSz := m.Size() - 4
	curBuf := m.buffered.Bytes()
	var buf []byte

	if uint32(cap(curBuf)) < payloadSz {
		buf = make([]byte, len(curBuf), payloadSz)
		copy(buf, curBuf)
	} else {
		buf = curBuf
	}

	payload := buf[:payloadSz]
	_, err := io.ReadFull(m.future, payload)

	m.buffered.InitReader(payload)
	m.future = nil

	return m.buffered.Bytes(), err
}

func (m *Message) WriteTo(w io.Writer) (_ int64, err error) {
	var totalN int64

	if mt := m.MsgType(); mt != MSG_TYPE_FIRST {
		n, err := w.Write([]byte{mt})
		totalN += int64(n)
		if err != nil {
			return totalN, err
		}
	}

	// Write message size integer to the stream
	var bufBack [4]byte
	buf := bufBack[:]
	binary.BigEndian.PutUint32(buf, m.Size())
	nMsgSz, err := w.Write(buf)
	totalN += int64(nMsgSz)
	if err != nil {
		return totalN, err
	}

	// Write the actual payload
	var nPayload int64

	if m.future == nil {
		// Fast path for fully buffered messages
		var nPayloadSm int
		nPayloadSm, err = w.Write(m.buffered.Bytes())
		nPayload = int64(nPayloadSm)
	} else {
		// Slow generic path
		nPayload, err = io.Copy(w, m.Payload())
	}

	totalN += nPayload
	return totalN, err
}

func (m *Message) baseInitMessage(msgType byte, size uint32) {
	m.msgType = msgType
	m.sz = size
}

func (m *Message) InitFromBytes(msgType byte, payload []byte) {
	m.baseInitMessage(msgType, uint32(len(payload))+4)
	m.future = nil
	m.buffered.InitReader(payload)
	m.union = &m.buffered
}

func (m *Message) InitPromise(msgType byte, size uint32,
	buffered []byte, r io.Reader) {
	m.baseInitMessage(msgType, size)
	m.buffered.InitReader(buffered)

	remaining := int64(size - 4 - uint32(len(buffered)))
	m.future = io.LimitReader(r, remaining)

	m.union = io.MultiReader(&m.buffered, m.future)
}
<commit_msg>changed a bufer to be allocated globally<commit_after>package femebe

import (
	"encoding/binary"
	"errors"
	"io"
)

var ErrTooLarge = errors.New("Message buffering size limit exceeded")

const MSG_TYPE_FIRST = '\000'

type Message struct {
	// Constant-width header
	msgType byte
	sz      uint32

	buffered Reader
	union    io.Reader

	// The rest of the message yet to be read.
	future io.Reader
}

func (m *Message) MsgType() byte {
	return m.msgType
}

func (m *Message) Payload() io.Reader {
	return m.union
}

func (m *Message) Size() uint32 {
	return m.sz
}

func (m *Message) IsBuffered() bool {
	return m.future == nil
}

func (m *Message) Force() ([]byte, error) {
	if m.IsBuffered() {
		return m.buffered.Bytes(), nil
	}

	payloadSz := m.Size() - 4
	curBuf := m.buffered.Bytes()
	var buf []byte

	if uint32(cap(curBuf)) < payloadSz {
		buf = make([]byte, len(curBuf), payloadSz)
		copy(buf, curBuf)
	} else {
		buf = curBuf
	}

	payload := buf[:payloadSz]
	_, err := io.ReadFull(m.future, payload)

	m.buffered.InitReader(payload)
	m.future = nil

	return m.buffered.Bytes(), err
}

var bufBack [4]byte

func (m *Message) WriteTo(w io.Writer) (_ int64, err error) {
	var totalN int64

	if mt := m.MsgType(); mt != MSG_TYPE_FIRST {
		n, err := w.Write([]byte{mt})
		totalN += int64(n)
		if err != nil {
			return totalN, err
		}
	}

	// Write message size integer to the stream

	buf := bufBack[:]
	binary.BigEndian.PutUint32(buf, m.Size())
	nMsgSz, err := w.Write(buf)
	totalN += int64(nMsgSz)
	if err != nil {
		return totalN, err
	}

	// Write the actual payload
	var nPayload int64

	if m.future == nil {
		// Fast path for fully buffered messages
		var nPayloadSm int
		nPayloadSm, err = w.Write(m.buffered.Bytes())
		nPayload = int64(nPayloadSm)
	} else {
		// Slow generic path
		nPayload, err = io.Copy(w, m.Payload())
	}

	totalN += nPayload
	return totalN, err
}

func (m *Message) baseInitMessage(msgType byte, size uint32) {
	m.msgType = msgType
	m.sz = size
}

func (m *Message) InitFromBytes(msgType byte, payload []byte) {
	m.baseInitMessage(msgType, uint32(len(payload))+4)
	m.future = nil
	m.buffered.InitReader(payload)
	m.union = &m.buffered
}

func (m *Message) InitPromise(msgType byte, size uint32,
	buffered []byte, r io.Reader) {
	m.baseInitMessage(msgType, size)
	m.buffered.InitReader(buffered)

	remaining := int64(size - 4 - uint32(len(buffered)))
	m.future = io.LimitReader(r, remaining)

	m.union = io.MultiReader(&m.buffered, m.future)
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/rcrowley/go-metrics"

	"time"
)

// CounterByMode - container for mode counters, registry and flush interval
type CounterByMode struct {
	counterVirtualize, counterCapture, counterModify, counterSynthesize metrics.Counter
	registry                                                            metrics.Registry
	flushInterval                                                       time.Duration
}

// NewModeCounter - returns new counter instance
func NewModeCounter() *CounterByMode {

	registry := metrics.DefaultRegistry

	c := &CounterByMode{
		counterVirtualize: metrics.NewCounter(),
		counterCapture:    metrics.NewCounter(),
		counterModify:     metrics.NewCounter(),
		counterSynthesize: metrics.NewCounter(),
		registry:          registry,
		flushInterval:     5 * time.Second,
	}

	c.registry.GetOrRegister(VirtualizeMode, c.counterVirtualize)
	c.registry.GetOrRegister(CaptureMode, c.counterCapture)
	c.registry.GetOrRegister(ModifyMode, c.counterModify)
	c.registry.GetOrRegister(SynthesizeMode, c.counterSynthesize)

	log.Info("new counter created, registration successful")

	return c
}

func (c *CounterByMode) Count(mode string) {
	if mode == VirtualizeMode {
		c.counterVirtualize.Inc(1)
	} else if mode == CaptureMode {
		c.counterCapture.Inc(1)
	} else if mode == ModifyMode {
		c.counterModify.Inc(1)
	} else if mode == SynthesizeMode {
		c.counterSynthesize.Inc(1)
	}
}

// Init initializes logging
func (c *CounterByMode) Init() {
	for _ = range time.Tick(c.flushInterval) {
		m := c.Flush()
		log.WithFields(log.Fields{"counters": m.Counters}).Info("hoverfly metrics")
	}
}

// HoverflyStats - holds information about various system metrics like requests counts
type HoverflyStats struct {
	Counters    map[string]int64   `json:"counters"`
	Gauges      map[string]int64   `json:"gauges,omitempty"`
	GaugesFloat map[string]float64 `json:"gautesFloat,omitempty"`
}

// Flush gets current metrics from stats registry
func (c *CounterByMode) Flush() (h HoverflyStats) {

	counters := make(map[string]int64)
	gauges := make(map[string]int64)
	gaugesFloat := make(map[string]float64)

	c.registry.Each(func(name string, i interface{}) {
		switch metric := i.(type) {
		case metrics.Counter:
			//log.Info(fmt.Sprintf("%s.count %d", name, metric.Count()))
			counters[name] = metric.Count()
		case metrics.Gauge:
			gauges[name] = metric.Value()
			//fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
		case metrics.GaugeFloat64:
			gaugesFloat[name] = metric.Value()
		}
	})

	h.Counters = counters
	h.Gauges = gauges
	h.GaugesFloat = gaugesFloat
	return
}
<commit_msg>cleanup<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/rcrowley/go-metrics"

	"time"
)

// CounterByMode - container for mode counters, registry and flush interval
type CounterByMode struct {
	counterVirtualize, counterCapture, counterModify, counterSynthesize metrics.Counter
	registry                                                            metrics.Registry
	flushInterval                                                       time.Duration
}

// NewModeCounter - returns new counter instance
func NewModeCounter() *CounterByMode {

	registry := metrics.DefaultRegistry

	c := &CounterByMode{
		counterVirtualize: metrics.NewCounter(),
		counterCapture:    metrics.NewCounter(),
		counterModify:     metrics.NewCounter(),
		counterSynthesize: metrics.NewCounter(),
		registry:          registry,
		flushInterval:     5 * time.Second,
	}

	c.registry.GetOrRegister(VirtualizeMode, c.counterVirtualize)
	c.registry.GetOrRegister(CaptureMode, c.counterCapture)
	c.registry.GetOrRegister(ModifyMode, c.counterModify)
	c.registry.GetOrRegister(SynthesizeMode, c.counterSynthesize)

	log.Debug("new counter created, registration successful")

	return c
}

// Count - counts requests based on mode
func (c *CounterByMode) Count(mode string) {
	if mode == VirtualizeMode {
		c.counterVirtualize.Inc(1)
	} else if mode == CaptureMode {
		c.counterCapture.Inc(1)
	} else if mode == ModifyMode {
		c.counterModify.Inc(1)
	} else if mode == SynthesizeMode {
		c.counterSynthesize.Inc(1)
	}
}

// Init initializes logging
func (c *CounterByMode) Init() {
	for _ = range time.Tick(c.flushInterval) {
		m := c.Flush()
		log.WithFields(log.Fields{"counters": m.Counters}).Info("hoverfly metrics")
	}
}

// HoverflyStats - holds information about various system metrics like requests counts
type HoverflyStats struct {
	Counters    map[string]int64   `json:"counters"`
	Gauges      map[string]int64   `json:"gauges,omitempty"`
	GaugesFloat map[string]float64 `json:"gautesFloat,omitempty"`
}

// Flush gets current metrics from stats registry
func (c *CounterByMode) Flush() (h HoverflyStats) {

	counters := make(map[string]int64)
	gauges := make(map[string]int64)
	gaugesFloat := make(map[string]float64)

	c.registry.Each(func(name string, i interface{}) {
		switch metric := i.(type) {
		case metrics.Counter:
			counters[name] = metric.Count()
		case metrics.Gauge:
			gauges[name] = metric.Value()
		case metrics.GaugeFloat64:
			gaugesFloat[name] = metric.Value()
		}
	})

	h.Counters = counters
	h.Gauges = gauges
	h.GaugesFloat = gaugesFloat
	return
}
<|endoftext|>