<commit_before>package kubernetes

import (
	"fmt"
	"regexp"

	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"

	"github.com/rusenask/keel/types"
	"github.com/rusenask/keel/util/version"

	log "github.com/Sirupsen/logrus"
)

const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// Provider - kubernetes provider for auto update
type Provider struct {
	// cfg    *rest.Config
	// client *kubernetes.Clientset

	implementer Implementer

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer) (*Provider, error) {
	return &Provider{
		// cfg:    cfg,
		// client: client,
		implementer: implementer,
		events:      make(chan *types.Event, 100),
		stop:        make(chan struct{}),
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*v1beta1.Deployment, err error) {
	impacted, err := p.impactedDeployments(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(impacted) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no impacted deployments found for this event")
		return
	}

	updated, err = p.updateDeployments(impacted)

	return
}

func (p *Provider) updateDeployments(deployments []*v1beta1.Deployment) (updated []*v1beta1.Deployment, err error) {
	for _, deployment := range deployments {
		// _, err := p.client.Extensions().Deployments(deployment.Namespace).Update(deployment)
		err := p.implementer.Update(deployment)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  deployment.Namespace,
				"deployment": deployment.Name,
			}).Error("provider.kubernetes: got error while update deployment")
			continue
		}
		updated = append(updated, deployment)
	}

	return
}

// getDeployment - helper function to get specific deployment
func (p *Provider) getDeployment(namespace, name string) (*v1beta1.Deployment, error) {
	// dep := p.client.Extensions().Deployments(namespace)
	// return dep.Get(name, meta_v1.GetOptions{})
	return p.implementer.Deployment(namespace, name)
}

// gets impacted deployments by changed repository
func (p *Provider) impactedDeployments(repo *types.Repository) ([]*v1beta1.Deployment, error) {
	newVersion, err := version.GetVersion(repo.Tag)
	if err != nil {
		return nil, fmt.Errorf("failed to parse version from repository tag, error: %s", err)
	}

	deploymentLists, err := p.deployments()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.kubernetes: failed to get deployment lists")
		return nil, err
	}

	impacted := []*v1beta1.Deployment{}

	for _, deploymentList := range deploymentLists {
		for _, deployment := range deploymentList.Items {
			labels := deployment.GetLabels()
			policyStr, ok := labels[types.KeelPolicyLabel]
			// if no policy is set - skipping this deployment
			if !ok {
				continue
			}
			policy := types.ParsePolicy(policyStr)

			log.WithFields(log.Fields{
				"labels":    labels,
				"name":      deployment.Name,
				"namespace": deployment.Namespace,
				"policy":    policy,
			}).Info("provider.kubernetes: keel policy found, checking deployment...")

			for idx, c := range deployment.Spec.Template.Spec.Containers {
				// Remove version if any
				containerImageName := versionreg.ReplaceAllString(c.Image, "")

				log.WithFields(log.Fields{
					"name":              deployment.Name,
					"namespace":         deployment.Namespace,
					"parsed_image_name": containerImageName,
					"target_image_name": repo.Name,
					"target_tag":        repo.Tag,
					"policy":            policy,
					"image":             c.Image,
				}).Info("provider.kubernetes: checking image")

				if containerImageName != repo.Name {
					continue
				}

				currentVersion, err := version.GetVersionFromImageName(c.Image)
				if err != nil {
					log.WithFields(log.Fields{
						"error":       err,
						"image_name":  c.Image,
						"keel_policy": policy,
					}).Error("provider.kubernetes: failed to get image version, is it tagged as semver?")
					continue
				}

				log.WithFields(log.Fields{
					"labels":          labels,
					"name":            deployment.Name,
					"namespace":       deployment.Namespace,
					"image":           c.Image,
					"current_version": currentVersion.String(),
					"policy":          policy,
				}).Info("provider.kubernetes: current image version")

				shouldUpdate, err := version.ShouldUpdate(currentVersion, newVersion, policy)
				if err != nil {
					log.WithFields(log.Fields{
						"error":           err,
						"new_version":     newVersion.String(),
						"current_version": currentVersion.String(),
						"keel_policy":     policy,
					}).Error("provider.kubernetes: got error while checking whether deployment should be updated")
					continue
				}

				log.WithFields(log.Fields{
					"labels":          labels,
					"name":            deployment.Name,
					"namespace":       deployment.Namespace,
					"image":           c.Image,
					"current_version": currentVersion.String(),
					"new_version":     newVersion.String(),
					"policy":          policy,
					"should_update":   shouldUpdate,
				}).Info("provider.kubernetes: checked version, deciding whether to update")

				if shouldUpdate {
					// updating image
					c.Image = fmt.Sprintf("%s:%s", containerImageName, newVersion.String())
					deployment.Spec.Template.Spec.Containers[idx] = c
					impacted = append(impacted, &deployment)
					log.WithFields(log.Fields{
						"parsed_image":     containerImageName,
						"raw_image_name":   c.Image,
						"target_image":     repo.Name,
						"target_image_tag": repo.Tag,
						"policy":           policy,
					}).Info("provider.kubernetes: impacted deployment container found")
				}
			}
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	// namespaces := p.client.Namespaces()
	// return namespaces.List(meta_v1.ListOptions{})
	return p.implementer.Namespaces()
}

// deployments - gets all deployments
func (p *Provider) deployments() ([]*v1beta1.DeploymentList, error) {
	// namespaces := p.client.Namespaces()
	deployments := []*v1beta1.DeploymentList{}

	n, err := p.namespaces()
	if err != nil {
		return nil, err
	}

	for _, n := range n.Items {
		// dep := p.client.Extensions().Deployments(n.GetName())
		// l, err := dep.List(meta_v1.ListOptions{})
		l, err := p.implementer.Deployments(n.GetName())
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"namespace": n.GetName(),
			}).Error("provider.kubernetes: failed to list deployments")
			continue
		}
		deployments = append(deployments, l)
	}

	return deployments, nil
}
<commit_msg>cleanup<commit_after>package kubernetes

import (
	"fmt"
	"regexp"

	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"

	"github.com/rusenask/keel/types"
	"github.com/rusenask/keel/util/version"

	log "github.com/Sirupsen/logrus"
)

const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// Provider - kubernetes provider for auto update
type Provider struct {
	implementer Implementer

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer) (*Provider, error) {
	return &Provider{
		implementer: implementer,
		events:      make(chan *types.Event, 100),
		stop:        make(chan struct{}),
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*v1beta1.Deployment, err error) {
	impacted, err := p.impactedDeployments(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(impacted) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no impacted deployments found for this event")
		return
	}

	updated, err = p.updateDeployments(impacted)

	return
}

func (p *Provider) updateDeployments(deployments []*v1beta1.Deployment) (updated []*v1beta1.Deployment, err error) {
	for _, deployment := range deployments {
		// _, err := p.client.Extensions().Deployments(deployment.Namespace).Update(deployment)
		err := p.implementer.Update(deployment)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  deployment.Namespace,
				"deployment": deployment.Name,
			}).Error("provider.kubernetes: got error while update deployment")
			continue
		}
		updated = append(updated, deployment)
	}

	return
}

// getDeployment - helper function to get specific deployment
func (p *Provider) getDeployment(namespace, name string) (*v1beta1.Deployment, error) {
	// dep := p.client.Extensions().Deployments(namespace)
	// return dep.Get(name, meta_v1.GetOptions{})
	return p.implementer.Deployment(namespace, name)
}

// gets impacted deployments by changed repository
func (p *Provider) impactedDeployments(repo *types.Repository) ([]*v1beta1.Deployment, error) {
	newVersion, err := version.GetVersion(repo.Tag)
	if err != nil {
		return nil, fmt.Errorf("failed to parse version from repository tag, error: %s", err)
	}

	deploymentLists, err := p.deployments()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.kubernetes: failed to get deployment lists")
		return nil, err
	}

	impacted := []*v1beta1.Deployment{}

	for _, deploymentList := range deploymentLists {
		for _, deployment := range deploymentList.Items {
			labels := deployment.GetLabels()
			policyStr, ok := labels[types.KeelPolicyLabel]
			// if no policy is set - skipping this deployment
			if !ok {
				continue
			}
			policy := types.ParsePolicy(policyStr)

			log.WithFields(log.Fields{
				"labels":    labels,
				"name":      deployment.Name,
				"namespace": deployment.Namespace,
				"policy":    policy,
			}).Info("provider.kubernetes: keel policy found, checking deployment...")

			for idx, c := range deployment.Spec.Template.Spec.Containers {
				// Remove version if any
				containerImageName := versionreg.ReplaceAllString(c.Image, "")

				log.WithFields(log.Fields{
					"name":              deployment.Name,
					"namespace":         deployment.Namespace,
					"parsed_image_name": containerImageName,
					"target_image_name": repo.Name,
					"target_tag":        repo.Tag,
					"policy":            policy,
					"image":             c.Image,
				}).Info("provider.kubernetes: checking image")

				if containerImageName != repo.Name {
					continue
				}

				currentVersion, err := version.GetVersionFromImageName(c.Image)
				if err != nil {
					log.WithFields(log.Fields{
						"error":       err,
						"image_name":  c.Image,
						"keel_policy": policy,
					}).Error("provider.kubernetes: failed to get image version, is it tagged as semver?")
					continue
				}

				log.WithFields(log.Fields{
					"labels":          labels,
					"name":            deployment.Name,
					"namespace":       deployment.Namespace,
					"image":           c.Image,
					"current_version": currentVersion.String(),
					"policy":          policy,
				}).Info("provider.kubernetes: current image version")

				shouldUpdate, err := version.ShouldUpdate(currentVersion, newVersion, policy)
				if err != nil {
					log.WithFields(log.Fields{
						"error":           err,
						"new_version":     newVersion.String(),
						"current_version": currentVersion.String(),
						"keel_policy":     policy,
					}).Error("provider.kubernetes: got error while checking whether deployment should be updated")
					continue
				}

				log.WithFields(log.Fields{
					"labels":          labels,
					"name":            deployment.Name,
					"namespace":       deployment.Namespace,
					"image":           c.Image,
					"current_version": currentVersion.String(),
					"new_version":     newVersion.String(),
					"policy":          policy,
					"should_update":   shouldUpdate,
				}).Info("provider.kubernetes: checked version, deciding whether to update")

				if shouldUpdate {
					// updating image
					c.Image = fmt.Sprintf("%s:%s", containerImageName, newVersion.String())
					deployment.Spec.Template.Spec.Containers[idx] = c
					impacted = append(impacted, &deployment)
					log.WithFields(log.Fields{
						"parsed_image":     containerImageName,
						"raw_image_name":   c.Image,
						"target_image":     repo.Name,
						"target_image_tag": repo.Tag,
						"policy":           policy,
					}).Info("provider.kubernetes: impacted deployment container found")
				}
			}
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	return p.implementer.Namespaces()
}

// deployments - gets all deployments
func (p *Provider) deployments() ([]*v1beta1.DeploymentList, error) {
	deployments := []*v1beta1.DeploymentList{}

	n, err := p.namespaces()
	if err != nil {
		return nil, err
	}

	for _, n := range n.Items {
		l, err := p.implementer.Deployments(n.GetName())
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"namespace": n.GetName(),
			}).Error("provider.kubernetes: failed to list deployments")
			continue
		}
		deployments = append(deployments, l)
	}

	return deployments, nil
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/rusenask/cron"

	"k8s.io/api/core/v1"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/internal/k8s"
	"github.com/keel-hq/keel/internal/policy"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/policies"

	log "github.com/sirupsen/logrus"
)

var kubernetesVersionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_versioned_updates_total",
		Help: "How many versioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

var kubernetesUnversionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_unversioned_updates_total",
		Help: "How many unversioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

func init() {
	prometheus.MustRegister(kubernetesVersionedUpdatesCounter)
	prometheus.MustRegister(kubernetesUnversionedUpdatesCounter)
}

// ProviderName - provider name
const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// GenericResourceCache an interface for generic resource cache.
type GenericResourceCache interface {
	// Values returns a copy of the contents of the cache.
	// The slice and its contents should be treated as read-only.
	Values() []*k8s.GenericResource

	// Register registers ch to receive a value when Notify is called.
	Register(chan int, int)
}

// UpdatePlan - deployment update plan
type UpdatePlan struct {
	// Updated deployment version
	// Deployment v1beta1.Deployment
	Resource *k8s.GenericResource

	// Current (last seen cluster version)
	CurrentVersion string
	// New version that's already in the deployment
	NewVersion string
}

func (p *UpdatePlan) String() string {
	if p.Resource != nil {
		return fmt.Sprintf("%s %s->%s", p.Resource.Identifier, p.CurrentVersion, p.NewVersion)
	}
	return "empty plan"
}

// Provider - kubernetes provider for auto update
type Provider struct {
	implementer Implementer

	sender notification.Sender

	approvalManager approvals.Manager

	cache GenericResourceCache

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer, sender notification.Sender, approvalManager approvals.Manager, cache GenericResourceCache) (*Provider, error) {
	return &Provider{
		implementer:     implementer,
		cache:           cache,
		approvalManager: approvalManager,
		events:          make(chan *types.Event, 100),
		stop:            make(chan struct{}),
		sender:          sender,
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

// TrackedImages returns a list of tracked images.
func (p *Provider) TrackedImages() ([]*types.TrackedImage, error) {
	var trackedImages []*types.TrackedImage

	for _, gr := range p.cache.Values() {
		labels := gr.GetLabels()

		// ignoring unlabelled deployments
		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			continue
		}

		annotations := gr.GetAnnotations()
		schedule, ok := annotations[types.KeelPollScheduleAnnotation]
		if ok {
			_, err := cron.Parse(schedule)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"schedule":  schedule,
					"name":      gr.Name,
					"namespace": gr.Namespace,
				}).Error("provider.kubernetes: failed to parse poll schedule, setting default schedule")
				schedule = types.KeelPollDefaultSchedule
			}
		} else {
			schedule = types.KeelPollDefaultSchedule
		}

		// trigger type, we only care for "poll" type triggers
		trigger := policies.GetTriggerPolicy(labels)
		secrets := gr.GetImagePullSecrets()
		images := gr.GetImages()
		for _, img := range images {
			ref, err := image.Parse(img)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"image":     img,
					"namespace": gr.Namespace,
					"name":      gr.Name,
				}).Error("provider.kubernetes: failed to parse image")
				continue
			}
			trackedImages = append(trackedImages, &types.TrackedImage{
				Image:        ref,
				PollSchedule: schedule,
				Trigger:      trigger,
				Provider:     ProviderName,
				Namespace:    gr.Namespace,
				Secrets:      secrets,
			})
		}
	}

	return trackedImages, nil
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
				"tag":        event.Repository.Tag,
				"registry":   event.Repository.Host,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*k8s.GenericResource, err error) {
	plans, err := p.createUpdatePlans(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(plans) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no plans for deployment updates found for this event")
		return
	}

	approvedPlans := p.checkForApprovals(event, plans)

	return p.updateDeployments(approvedPlans)
}

func (p *Provider) updateDeployments(plans []*UpdatePlan) (updated []*k8s.GenericResource, err error) {
	for _, plan := range plans {
		resource := plan.Resource

		annotations := resource.GetAnnotations()

		notificationChannels := types.ParseEventNotificationChannels(annotations)

		p.sender.Send(types.EventNotification{
			Name:      "preparing to update resource",
			Message:   fmt.Sprintf("Preparing to update %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", ")),
			CreatedAt: time.Now(),
			Type:      types.NotificationPreDeploymentUpdate,
			Level:     types.LevelDebug,
			Channels:  notificationChannels,
		})

		var err error

		timestamp := time.Now().Format(time.RFC3339)
		annotations["kubernetes.io/change-cause"] = fmt.Sprintf("keel automated update, version %s -> %s [%s]", plan.CurrentVersion, plan.NewVersion, timestamp)

		resource.SetAnnotations(annotations)

		err = p.implementer.Update(resource)
		kubernetesVersionedUpdatesCounter.With(prometheus.Labels{"kubernetes": fmt.Sprintf("%s/%s", resource.Namespace, resource.Name)}).Inc()
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  resource.Namespace,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"update":     fmt.Sprintf("%s->%s", plan.CurrentVersion, plan.NewVersion),
			}).Error("provider.kubernetes: got error while updating resource")

			p.sender.Send(types.EventNotification{
				Name:      "update resource",
				Message:   fmt.Sprintf("%s %s/%s update %s->%s failed, error: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, err),
				CreatedAt: time.Now(),
				Type:      types.NotificationDeploymentUpdate,
				Level:     types.LevelError,
				Channels:  notificationChannels,
			})

			continue
		}

		err = p.updateComplete(plan)
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"name":      resource.Name,
				"kind":      resource.Kind(),
				"namespace": resource.Namespace,
			}).Warn("provider.kubernetes: got error while resetting approvals counter after successful update")
		}

		var msg string
		releaseNotes := types.ParseReleaseNotesURL(resource.GetAnnotations())
		if releaseNotes != "" {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s). Release notes: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "), releaseNotes)
		} else {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "))
		}

		p.sender.Send(types.EventNotification{
			Name:      "update resource",
			Message:   msg,
			CreatedAt: time.Now(),
			Type:      types.NotificationDeploymentUpdate,
			Level:     types.LevelSuccess,
			Channels:  notificationChannels,
		})

		log.WithFields(log.Fields{
			"name":      resource.Name,
			"kind":      resource.Kind(),
			"previous":  plan.CurrentVersion,
			"new":       plan.NewVersion,
			"namespace": resource.Namespace,
		}).Info("provider.kubernetes: resource updated")
		updated = append(updated, resource)
	}

	return
}

func getDesiredImage(delta map[string]string, currentImage string) (string, error) {
	currentRef, err := image.Parse(currentImage)
	if err != nil {
		return "", err
	}
	for repository, tag := range delta {
		if repository == currentRef.Repository() {
			ref, err := image.Parse(repository)
			if err != nil {
				return "", err
			}

			// updating image
			if ref.Registry() == image.DefaultRegistryHostname {
				return fmt.Sprintf("%s:%s", ref.ShortName(), tag), nil
			}
			return fmt.Sprintf("%s:%s", ref.Repository(), tag), nil
		}
	}
	return "", fmt.Errorf("image %s not found in deltas", currentImage)
}

// createUpdatePlans - impacted deployments by changed repository
func (p *Provider) createUpdatePlans(repo *types.Repository) ([]*UpdatePlan, error) {
	impacted := []*UpdatePlan{}

	for _, resource := range p.cache.Values() {

		labels := resource.GetLabels()

		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			log.Debugf("no policy defined, skipping: %s, labels: %s", resource.Identifier, labels)
			continue
		}

		updated, shouldUpdateDeployment, err := checkForUpdate(plc, repo, resource)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"namespace":  resource.Namespace,
			}).Error("provider.kubernetes: got error while checking versioned resource")
			continue
		}

		if shouldUpdateDeployment {
			impacted = append(impacted, updated)
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	return p.implementer.Namespaces()
}
<commit_msg>passing in annotations<commit_after>package kubernetes

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/rusenask/cron"

	"k8s.io/api/core/v1"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/internal/k8s"
	"github.com/keel-hq/keel/internal/policy"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/policies"

	log "github.com/sirupsen/logrus"
)

var kubernetesVersionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_versioned_updates_total",
		Help: "How many versioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

var kubernetesUnversionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_unversioned_updates_total",
		Help: "How many unversioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

func init() {
	prometheus.MustRegister(kubernetesVersionedUpdatesCounter)
	prometheus.MustRegister(kubernetesUnversionedUpdatesCounter)
}

// ProviderName - provider name
const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// GenericResourceCache an interface for generic resource cache.
type GenericResourceCache interface {
	// Values returns a copy of the contents of the cache.
	// The slice and its contents should be treated as read-only.
	Values() []*k8s.GenericResource

	// Register registers ch to receive a value when Notify is called.
	Register(chan int, int)
}

// UpdatePlan - deployment update plan
type UpdatePlan struct {
	// Updated deployment version
	// Deployment v1beta1.Deployment
	Resource *k8s.GenericResource

	// Current (last seen cluster version)
	CurrentVersion string
	// New version that's already in the deployment
	NewVersion string
}

func (p *UpdatePlan) String() string {
	if p.Resource != nil {
		return fmt.Sprintf("%s %s->%s", p.Resource.Identifier, p.CurrentVersion, p.NewVersion)
	}
	return "empty plan"
}

// Provider - kubernetes provider for auto update
type Provider struct {
	implementer Implementer

	sender notification.Sender

	approvalManager approvals.Manager

	cache GenericResourceCache

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer, sender notification.Sender, approvalManager approvals.Manager, cache GenericResourceCache) (*Provider, error) {
	return &Provider{
		implementer:     implementer,
		cache:           cache,
		approvalManager: approvalManager,
		events:          make(chan *types.Event, 100),
		stop:            make(chan struct{}),
		sender:          sender,
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

// TrackedImages returns a list of tracked images.
func (p *Provider) TrackedImages() ([]*types.TrackedImage, error) {
	var trackedImages []*types.TrackedImage

	for _, gr := range p.cache.Values() {
		labels := gr.GetLabels()
		annotations := gr.GetAnnotations()

		// ignoring unlabelled deployments
		plc := policy.GetPolicyFromLabelsOrAnnotations(labels, annotations)
		if plc.Type() == policy.PolicyTypeNone {
			continue
		}

		schedule, ok := annotations[types.KeelPollScheduleAnnotation]
		if ok {
			_, err := cron.Parse(schedule)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"schedule":  schedule,
					"name":      gr.Name,
					"namespace": gr.Namespace,
				}).Error("provider.kubernetes: failed to parse poll schedule, setting default schedule")
				schedule = types.KeelPollDefaultSchedule
			}
		} else {
			schedule = types.KeelPollDefaultSchedule
		}

		// trigger type, we only care for "poll" type triggers
		trigger := policies.GetTriggerPolicy(labels, annotations)
		secrets := gr.GetImagePullSecrets()
		images := gr.GetImages()
		for _, img := range images {
			ref, err := image.Parse(img)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"image":     img,
					"namespace": gr.Namespace,
					"name":      gr.Name,
				}).Error("provider.kubernetes: failed to parse image")
				continue
			}
			trackedImages = append(trackedImages, &types.TrackedImage{
				Image:        ref,
				PollSchedule: schedule,
				Trigger:      trigger,
				Provider:     ProviderName,
				Namespace:    gr.Namespace,
				Secrets:      secrets,
			})
		}
	}

	return trackedImages, nil
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
				"tag":        event.Repository.Tag,
				"registry":   event.Repository.Host,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*k8s.GenericResource, err error) {
	plans, err := p.createUpdatePlans(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(plans) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no plans for deployment updates found for this event")
		return
	}

	approvedPlans := p.checkForApprovals(event, plans)

	return p.updateDeployments(approvedPlans)
}

func (p *Provider) updateDeployments(plans []*UpdatePlan) (updated []*k8s.GenericResource, err error) {
	for _, plan := range plans {
		resource := plan.Resource

		annotations := resource.GetAnnotations()

		notificationChannels := types.ParseEventNotificationChannels(annotations)

		p.sender.Send(types.EventNotification{
			Name:      "preparing to update resource",
			Message:   fmt.Sprintf("Preparing to update %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", ")),
			CreatedAt: time.Now(),
			Type:      types.NotificationPreDeploymentUpdate,
			Level:     types.LevelDebug,
			Channels:  notificationChannels,
		})

		var err error

		timestamp := time.Now().Format(time.RFC3339)
		annotations["kubernetes.io/change-cause"] = fmt.Sprintf("keel automated update, version %s -> %s [%s]", plan.CurrentVersion, plan.NewVersion, timestamp)

		resource.SetAnnotations(annotations)

		err = p.implementer.Update(resource)
		kubernetesVersionedUpdatesCounter.With(prometheus.Labels{"kubernetes": fmt.Sprintf("%s/%s", resource.Namespace, resource.Name)}).Inc()
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  resource.Namespace,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"update":     fmt.Sprintf("%s->%s", plan.CurrentVersion, plan.NewVersion),
			}).Error("provider.kubernetes: got error while updating resource")

			p.sender.Send(types.EventNotification{
				Name:      "update resource",
				Message:   fmt.Sprintf("%s %s/%s update %s->%s failed, error: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, err),
				CreatedAt: time.Now(),
				Type:      types.NotificationDeploymentUpdate,
				Level:     types.LevelError,
				Channels:  notificationChannels,
			})

			continue
		}

		err = p.updateComplete(plan)
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"name":      resource.Name,
				"kind":      resource.Kind(),
				"namespace": resource.Namespace,
			}).Warn("provider.kubernetes: got error while resetting approvals counter after successful update")
		}

		var msg string
		releaseNotes := types.ParseReleaseNotesURL(resource.GetAnnotations())
		if releaseNotes != "" {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s). Release notes: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "), releaseNotes)
		} else {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "))
		}

		p.sender.Send(types.EventNotification{
			Name:      "update resource",
			Message:   msg,
			CreatedAt: time.Now(),
			Type:      types.NotificationDeploymentUpdate,
			Level:     types.LevelSuccess,
			Channels:  notificationChannels,
		})

		log.WithFields(log.Fields{
			"name":      resource.Name,
			"kind":      resource.Kind(),
			"previous":  plan.CurrentVersion,
			"new":       plan.NewVersion,
			"namespace": resource.Namespace,
		}).Info("provider.kubernetes: resource updated")
		updated = append(updated, resource)
	}

	return
}

func getDesiredImage(delta map[string]string, currentImage string) (string, error) {
	currentRef, err := image.Parse(currentImage)
	if err != nil {
		return "", err
	}
	for repository, tag := range delta {
		if repository == currentRef.Repository() {
			ref, err := image.Parse(repository)
			if err != nil {
				return "", err
			}

			// updating image
			if ref.Registry() == image.DefaultRegistryHostname {
				return fmt.Sprintf("%s:%s", ref.ShortName(), tag), nil
			}
			return fmt.Sprintf("%s:%s", ref.Repository(), tag), nil
		}
	}
	return "", fmt.Errorf("image %s not found in deltas", currentImage)
}

// createUpdatePlans - impacted deployments by changed repository
func (p *Provider) createUpdatePlans(repo *types.Repository) ([]*UpdatePlan, error) {
	impacted := []*UpdatePlan{}

	for _, resource := range p.cache.Values() {

		labels := resource.GetLabels()
		annotations := resource.GetAnnotations()

		plc := policy.GetPolicyFromLabelsOrAnnotations(labels, annotations)
		if plc.Type() == policy.PolicyTypeNone {
			log.Debugf("no policy defined, skipping: %s, labels: %s, annotations: %s", resource.Identifier, labels, annotations)
			continue
		}

		updated, shouldUpdateDeployment, err := checkForUpdate(plc, repo, resource)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"namespace":  resource.Namespace,
			}).Error("provider.kubernetes: got error while checking versioned resource")
			continue
		}

		if shouldUpdateDeployment {
			impacted = append(impacted, updated)
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	return p.implementer.Namespaces()
}
<|endoftext|>


<commit_before>package bme280

import "io"
import "time"

type BME280 struct {
	i2c   io.ReadWriter
	calib struct {
		temp struct {
			T1 uint16
			T2 int16
			T3 int16
		}
		press struct {
			P1 uint16
			P2 int16
			P3 int16
			P4 int16
			P5 int16
			P6 int16
			P7 int16
			P8 int16
			P9 int16
		}
		hum struct {
			H1 uint8
			H2 int16
			H3 uint8
			H4 int16
			H5 int16
			H6 int8
		}
	}
	raw [8]byte
}

type Envdata struct {
	Temp  float64 `json:"temp"`
	Press float64 `json:"press"`
	Hum   float64 `json:"hum"`
}

func (bme *BME280) read(reg byte, data []byte) (int, error) {
	// first we have to write register adress
	_, err := bme.i2c.Write([]byte{reg})
	if err != nil {
		return 0, err
	}
	// now we can read the data
	return bme.i2c.Read(data)
}

func (bme *BME280) write(reg byte, data []byte) (int, error) {
	var tdata []byte
	tdata = append(tdata, reg)
	tdata = append(tdata, data...)

	return bme.i2c.Write(tdata)
}

func (bme *BME280) bootFinished() (err error) {
	var x [1]byte
	for x[0] != 0x60 && err == nil {
		_, err = bme.read(REG_id, x[:])
		time.Sleep(50 * time.Millisecond)
	}
	return err
}

func (bme *BME280) readCalibdata() (err error) {
	// read calibration data
	var calib1 [26]byte
	var calib2 [16]byte
	_, err = bme.read(REG_calib00, calib1[:])
	if err != nil {
		return err
	}
	_, err = bme.read(REG_calib26, calib2[:])
	if err != nil {
		return err
	}

	type tmpt struct {
		idata []byte
		odata interface{}
	}
	tconvert := []tmpt{
		{calib1[0:6], &bme.calib.temp},
		{calib1[6:24], &bme.calib.press},
		{calib1[25:], &bme.calib.hum.H1},
		{calib2[0:2], &bme.calib.hum.H2},
		{calib2[2:3], &bme.calib.hum.H3},
		{calib2[6:], &bme.calib.hum.H6}}

	for _, value := range tconvert {
		err = convert(value.idata, value.odata)
		if err != nil {
			return err
		}
	}

	// H4 and H5 are a little bit tricky alligned.
	bme.calib.hum.H4 = int16(calib2[3])<<4 | int16(calib2[4]&0x0F)
	bme.calib.hum.H5 = int16(calib2[5])<<4 | int16(calib2[4]&0xF0)>>4

	return err
}

func (bme *BME280) initialize() (err error) {
	// wait for finished initialisation
	err = bme.bootFinished()
	if err != nil {
		return err
	}
	// get calibrationdata
	err = bme.readCalibdata()
	if err != nil {
		return err
	}
	// initialize bme
	_, err = bme.write(REG_ctrl_hum, []byte{OPT_hum_oversampling_x1})
	if err != nil {
		return err
	}
	_, err = bme.write(REG_ctrl_meas, []byte{OPT_temp_oversampling_x1 |
		OPT_press_oversampling_x1 |
		OPT_mode_normal})
	if err != nil {
		return err
	}
	_, err = bme.write(REG_config, []byte{OPT_config_standbytime_1000})

	return err
}

// latch all data in
func (bme *BME280) readRaw() (err error) {
	_, err = bme.read(REG_press_msb, bme.raw[:])
	return err
}

// calculate enviroment data
func (bme *BME280) Readenv() (env Envdata, err error) {
	err = bme.readRaw()
	traw := int32(bme.raw[3])<<12 | int32(bme.raw[4])<<4 | int32(bme.raw[5])>>4
	praw := int32(bme.raw[0])<<12 | int32(bme.raw[1])<<4 | int32(bme.raw[2])>>4
	hraw := int32(bme.raw[6])<<8 | int32(bme.raw[7])

	t, tfine := bme.temp(traw)
	p := bme.press(praw, tfine)
	h := bme.hum(hraw, tfine)

	env.Temp = t
	env.Press = p / 100
	env.Hum = h
	return env, err
}

func (bme *BME280) temp(raw int32) (float64, int32) {
	calt := bme.calib.temp
	var v1, v2, t float64
	var tfine int32
	v1 = (float64(raw)/16384.0 - float64(calt.T1)/1024.0) *
		float64(calt.T2)
	v2 = (float64(raw)/131072.0 - float64(calt.T1)/8192.0) *
		(float64(raw)/131072.0 - float64(calt.T1)/8192.0) *
		float64(calt.T3)
	tfine = int32(v1 + v2)
	t = (v1 + v2) / 5120.0
	return t, tfine
}

func (bme *BME280) press(raw int32, tfine int32) float64 {
	calp := bme.calib.press
	var v1, v2, p float64
	v1 = float64(tfine)/2.0 - 64000.0
	v2 = v1 * v1 * (float64(calp.P6) / 32768.0)
	v2 = v2 + v1*(float64(calp.P5)*2.0)
	v2 = v2/4.0 + (float64(calp.P4) * 65536.0)
	v1 = (float64(calp.P3)*v1*v1/524288.0 + float64(calp.P2)*v1) / 524288.0
	v1 = (1.0 + v1/32768.0) * float64(calp.P1)
	if v1 == 0 {
		return 0
	}
	p = 1048576.0 - float64(raw)
	p = (p - v2/4096.0) * 6250.0 / v1
	v1 = float64(calp.P9) * p * p / 2147483648.0
	v2 = p * float64(calp.P8) / 32768.0
	p = p + (v1+v2+float64(calp.P7))/16.0
	return p
}

func (bme *BME280) hum(raw int32, tfine int32) float64 {
	calh := bme.calib.hum
	var h float64
	h = float64(tfine) - 76800.0
	h = (float64(raw) - float64(calh.H4)*64.0 +
		float64(calh.H5)/16384.0*h) * float64(calh.H2) /
		65536.0 * (1.0 + float64(calh.H6)/67108864.0*h*
		(1.0+float64(calh.H3)/67108864.0*h))
	h = h * (1.0 - float64(calh.H1)*h/524288.0)

	if h > 100.0 {
		h = 100.0
	} else if h < 0.0 {
		h = 0.0
	}
	return h
}

// NewI2CDriver initializes the bme280 device to use the i2c-bus for communication.
// It is expecting the i2c bus as a ReadWriter-Interface.
func NewI2CDriver(i2c io.ReadWriter) (*BME280, error) {
	bme := BME280{
		i2c: i2c,
	}

	return &bme, bme.initialize()
}
<commit_msg>this comment somehow missed a go fmt<commit_after>package bme280

import "io"
import "time"

type BME280 struct {
	i2c   io.ReadWriter
	calib struct {
		temp struct {
			T1 uint16
			T2 int16
			T3 int16
		}
		press struct {
			P1 uint16
			P2 int16
			P3 int16
			P4 int16
			P5 int16
			P6 int16
			P7 int16
			P8 int16
			P9 int16
		}
		hum struct {
			H1 uint8
			H2 int16
			H3 uint8
			H4 int16
			H5 int16
			H6 int8
		}
	}
	raw [8]byte
}

type Envdata struct {
	Temp  float64 `json:"temp"`
	Press float64 `json:"press"`
	Hum   float64 `json:"hum"`
}

func (bme *BME280) read(reg byte, data []byte) (int, error) {
	// first we have to write register adress
	_, err := bme.i2c.Write([]byte{reg})
	if err != nil {
		return 0, err
	}
	// now we can read the data
	return bme.i2c.Read(data)
}

func (bme *BME280) write(reg byte, data []byte) (int, error) {
	var tdata []byte
	tdata = append(tdata, reg)
	tdata = append(tdata, data...)

	return bme.i2c.Write(tdata)
}

func (bme *BME280) bootFinished() (err error) {
	var x [1]byte
	for x[0] != 0x60 && err == nil {
		_, err = bme.read(REG_id, x[:])
		time.Sleep(50 * time.Millisecond)
	}
	return err
}

func (bme *BME280) readCalibdata() (err error) {
	// read calibration data
	var calib1 [26]byte
	var calib2 [16]byte
	_, err = bme.read(REG_calib00, calib1[:])
	if err != nil {
		return err
	}
	_, err = bme.read(REG_calib26, calib2[:])
	if err != nil {
		return err
	}

	type tmpt struct {
		idata []byte
		odata interface{}
	}
	tconvert := []tmpt{
		{calib1[0:6], &bme.calib.temp},
		{calib1[6:24], &bme.calib.press},
		{calib1[25:], &bme.calib.hum.H1},
		{calib2[0:2], &bme.calib.hum.H2},
		{calib2[2:3], &bme.calib.hum.H3},
		{calib2[6:], &bme.calib.hum.H6}}

	for _, value := range tconvert {
		err = convert(value.idata, value.odata)
		if err != nil {
			return err
		}
	}

	// H4 and H5 are a little bit tricky alligned.
	bme.calib.hum.H4 = int16(calib2[3])<<4 | int16(calib2[4]&0x0F)
	bme.calib.hum.H5 = int16(calib2[5])<<4 | int16(calib2[4]&0xF0)>>4

	return err
}

func (bme *BME280) initialize() (err error) {
	// wait for finished initialisation
	err = bme.bootFinished()
	if err != nil {
		return err
	}
	// get calibrationdata
	err = bme.readCalibdata()
	if err != nil {
		return err
	}
	// initialize bme
	_, err = bme.write(REG_ctrl_hum, []byte{OPT_hum_oversampling_x1})
	if err != nil {
		return err
	}
	_, err = bme.write(REG_ctrl_meas, []byte{OPT_temp_oversampling_x1 |
		OPT_press_oversampling_x1 |
		OPT_mode_normal})
	if err != nil {
		return err
	}
	_, err = bme.write(REG_config, []byte{OPT_config_standbytime_1000})

	return err
}

// latch all data in
func (bme *BME280) readRaw() (err error) {
	_, err = bme.read(REG_press_msb, bme.raw[:])
	return err
}

// calculate enviroment data
func (bme *BME280) Readenv() (env Envdata, err error) {
	err = bme.readRaw()
	traw := int32(bme.raw[3])<<12 | int32(bme.raw[4])<<4 | int32(bme.raw[5])>>4
	praw := int32(bme.raw[0])<<12 | int32(bme.raw[1])<<4 | int32(bme.raw[2])>>4
	hraw := int32(bme.raw[6])<<8 | int32(bme.raw[7])

	t, tfine := bme.temp(traw)
	p := bme.press(praw, tfine)
	h := bme.hum(hraw, tfine)

	env.Temp = t
	env.Press = p / 100
	env.Hum = h
	return env, err
}

func (bme *BME280) temp(raw int32) (float64, int32) {
	calt := bme.calib.temp
	var v1, v2, t float64
	var tfine int32
	v1 = (float64(raw)/16384.0 - float64(calt.T1)/1024.0) *
		float64(calt.T2)
	v2 = (float64(raw)/131072.0 - float64(calt.T1)/8192.0) *
		(float64(raw)/131072.0 - float64(calt.T1)/8192.0) *
		float64(calt.T3)
	tfine = int32(v1 + v2)
	t = (v1 + v2) / 5120.0
	return t, tfine
}

func (bme *BME280) press(raw int32, tfine int32) float64 {
	calp := bme.calib.press
	var v1, v2, p float64
	v1 = float64(tfine)/2.0 - 64000.0
	v2 = v1 * v1 * (float64(calp.P6) / 32768.0)
	v2 = v2 + v1*(float64(calp.P5)*2.0)
	v2 = v2/4.0 + (float64(calp.P4) * 65536.0)
	v1 = (float64(calp.P3)*v1*v1/524288.0 + float64(calp.P2)*v1) / 524288.0
	v1 = (1.0 + v1/32768.0) * float64(calp.P1)
	if v1 == 0 {
		return 0
	}
	p = 1048576.0 - float64(raw)
	p = (p - v2/4096.0) * 6250.0 / v1
	v1 = float64(calp.P9) * p * p / 2147483648.0
	v2 = p * float64(calp.P8) / 32768.0
	p = p + (v1+v2+float64(calp.P7))/16.0
	return p
}

func (bme *BME280) hum(raw int32, tfine int32) float64 {
	calh := bme.calib.hum
	var h float64
	h = float64(tfine) - 76800.0
	h = (float64(raw) - float64(calh.H4)*64.0 +
		float64(calh.H5)/16384.0*h) * float64(calh.H2) /
		65536.0 * (1.0 + float64(calh.H6)/67108864.0*h*
		(1.0+float64(calh.H3)/67108864.0*h))
	h = h * (1.0 - float64(calh.H1)*h/524288.0)

	if h > 100.0 {
		h = 100.0
	} else if h < 0.0 {
		h = 0.0
	}
	return h
}

// NewI2CDriver initializes the bme280 device to use the i2c-bus for communication.
// It is expecting the i2c bus as a ReadWriter-Interface.
func NewI2CDriver(i2c io.ReadWriter) (*BME280, error) {
	bme := BME280{
		i2c: i2c,
	}

	return &bme, bme.initialize()
}
<|endoftext|>


<commit_before>package libnetwork

import (
	"os"
	"path/filepath"

	"github.com/docker/docker/pkg/etchosts"
	"github.com/docker/libnetwork/sandbox"
	"github.com/docker/libnetwork/types"
)

// Endpoint represents a logical connection between a network and a sandbox.
type Endpoint interface {
	// A system generated id for this endpoint.
	ID() string

	// Name returns the name of this endpoint.
	Name() string

	// Network returns the name of the network to which this endpoint is attached.
	Network() string

	// Join creates a new sandbox for the given container ID and populates the
	// network resources allocated for the endpoint and joins the sandbox to
	// the endpoint. It returns the sandbox key to the caller
	Join(containerID string, options ...JoinOption) (*ContainerData, error)

	// Leave removes the sandbox associated with  container ID and detaches
	// the network resources populated in the sandbox
	Leave(containerID string, options ...LeaveOption) error

	// SandboxInfo returns the sandbox information for this endpoint.
	SandboxInfo() *sandbox.Info

	// Delete and detaches this endpoint from the network.
	Delete() error
}

// ContainerData is a set of data returned when a container joins an endpoint.
type ContainerData struct {
	SandboxKey string
	HostsPath  string
}

// JoinOption is a option setter function type used to pass varios options to
// endpoint Join method. The various setter functions of type JoinOption are
// provided by libnetwork, they look like JoinOption[...](...)
type JoinOption func(ep *endpoint)

// LeaveOption is a option setter function type used to pass varios options to
// endpoint Leave method. The various setter functions of type LeaveOption are
// provided by libnetwork, they look like LeaveOptionXXXX(...)
type LeaveOption func(ep *endpoint)

type containerConfig struct {
	Hostname   string
	Domainname string
	generic    map[string]interface{}
}

type containerInfo struct {
	ID     string
	Config containerConfig
	Data   ContainerData
}

type endpoint struct {
	name        string
	id          types.UUID
	network     *network
	sandboxInfo *sandbox.Info
	sandBox     sandbox.Sandbox
	container   *containerInfo
	generic     map[string]interface{}
	context     map[string]interface{}
}

const prefix = "/var/lib/docker/network/files"

func (ep *endpoint) ID() string {
	return string(ep.id)
}

func (ep *endpoint) Name() string {
	return ep.name
}

func (ep *endpoint) Network() string {
	return ep.network.name
}

func (ep *endpoint) SandboxInfo() *sandbox.Info {
	if ep.sandboxInfo == nil {
		return nil
	}
	return ep.sandboxInfo.GetCopy()
}

// EndpointOption is a option setter function type used to pass various options to
// CreateEndpoint method. The various setter functions of type EndpointOption are
// provided by libnetwork, they look like EndpointOptionXXXX(...)
type EndpointOption func(ep *endpoint)

// EndpointOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
	return func(ep *endpoint) {
		ep.generic = generic
	}
}

func (ep *endpoint) processOptions(options ...EndpointOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

func createBasePath(dir string) error {
	err := os.MkdirAll(dir, 0644)
	if err != nil && !os.IsExist(err) {
		return err
	}

	return nil
}

func createHostsFile(path string) error {
	var f *os.File

	dir, _ := filepath.Split(path)
	err := createBasePath(dir)
	if err != nil {
		return err
	}

	f, err = os.Create(path)
	if err == nil {
		f.Close()
	}

	return err
}

func (ep *endpoint) Join(containerID string, options ...JoinOption) (*ContainerData, error) {
	var err error

	if containerID == "" {
		return nil, InvalidContainerIDError(containerID)
	}

	if ep.container != nil {
		return nil, ErrInvalidJoin
	}

	ep.container = &containerInfo{}
	defer func() {
		if err != nil {
			ep.container = nil
		}
	}()

	ep.processJoinOptions(options...)

	ep.container.Data.HostsPath = prefix + "/" + containerID + "/hosts"
	err = createHostsFile(ep.container.Data.HostsPath)
	if err != nil {
		return nil, err
	}

	err = ep.buildHostsFiles()
	if err != nil {
		return nil, err
	}

	sboxKey := sandbox.GenerateKey(containerID)
	sb, err := ep.network.ctrlr.sandboxAdd(sboxKey)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			ep.network.ctrlr.sandboxRm(sboxKey)
		}
	}()

	n := ep.network
	err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.Config.generic)
	if err != nil {
		return nil, err
	}

	sinfo := ep.SandboxInfo()
	if sinfo != nil {
		for _, i := range sinfo.Interfaces {
			err = sb.AddInterface(i)
			if err != nil {
				return nil, err
			}
		}

		err = sb.SetGateway(sinfo.Gateway)
		if err != nil {
			return nil, err
		}

		err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
		if err != nil {
			return nil, err
		}
	}

	ep.container.ID = containerID
	ep.container.Data.SandboxKey = sb.Key()

	cData := ep.container.Data
	return &cData, nil
}

func (ep *endpoint) Leave(containerID string, options ...LeaveOption) error {
	if ep.container == nil || ep.container.ID == "" ||
		containerID == "" || ep.container.ID != containerID {
		return InvalidContainerIDError(containerID)
	}

	n := ep.network
	ep.processLeaveOptions(options...)

	err := n.driver.Leave(n.id, ep.id, ep.context)
	if err != nil {
		return err
	}

	ep.network.ctrlr.sandboxRm(ep.container.Data.SandboxKey)
	ep.container = nil
	ep.context = nil
	return nil
}

func (ep *endpoint) Delete() error {
	var err error

	n := ep.network
	n.Lock()
	_, ok := n.endpoints[ep.id]
	if !ok {
		n.Unlock()
		return &UnknownEndpointError{name: ep.name, id: string(ep.id)}
	}

	delete(n.endpoints, ep.id)
	n.Unlock()
	defer func() {
		if err != nil {
			n.Lock()
			n.endpoints[ep.id] = ep
			n.Unlock()
		}
	}()

	err = n.driver.DeleteEndpoint(n.id, ep.id)
	return err
}

func (ep *endpoint) buildHostsFiles() error {
	var extraContent []etchosts.Record

	name := ep.container.Config.Hostname
	if ep.container.Config.Domainname != "" {
		name = name + "." + ep.container.Config.Domainname
	}

	IP := ""
	if ep.sandboxInfo != nil && ep.sandboxInfo.Interfaces[0] != nil &&
		ep.sandboxInfo.Interfaces[0].Address != nil {
		IP = ep.sandboxInfo.Interfaces[0].Address.IP.String()
	}

	return etchosts.Build(ep.container.Data.HostsPath, IP, ep.container.Config.Hostname,
		ep.container.Config.Domainname, extraContent)
}

// JoinOptionHostname function returns an option setter for hostname option to
// be passed to endpoint Join method.
func JoinOptionHostname(name string) JoinOption {
	return func(ep *endpoint) {
		ep.container.Config.Hostname = name
	}
}

// JoinOptionDomainname function returns an option setter for domainname option to
// be passed to endpoint Join method.
func JoinOptionDomainname(name string) JoinOption {
	return func(ep *endpoint) {
		ep.container.Config.Domainname = name
	}
}

// JoinOptionGeneric function returns an option setter for Generic configuration
// that is not managed by libNetwork but can be used by the Drivers during the call to
// endpoint join method. Container Labels are a good example.
func JoinOptionGeneric(generic map[string]interface{}) JoinOption {
	return func(ep *endpoint) {
		ep.container.Config.generic = generic
	}
}

func (ep *endpoint) processJoinOptions(options ...JoinOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

// LeaveOptionGeneric function returns an option setter for Generic configuration
// that is not managed by libNetwork but can be used by the Drivers during the call to
// endpoint leave method. Container Labels are a good example.
func LeaveOptionGeneric(context map[string]interface{}) JoinOption {
	return func(ep *endpoint) {
		ep.context = context
	}
}

func (ep *endpoint) processLeaveOptions(options ...LeaveOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}
<commit_msg>Params of non-exported struct should be non-exported<commit_after>package libnetwork

import (
	"os"
	"path/filepath"

	"github.com/docker/docker/pkg/etchosts"
	"github.com/docker/libnetwork/sandbox"
	"github.com/docker/libnetwork/types"
)

// Endpoint represents a logical connection between a network and a sandbox.
type Endpoint interface {
	// A system generated id for this endpoint.
	ID() string

	// Name returns the name of this endpoint.
	Name() string

	// Network returns the name of the network to which this endpoint is attached.
	Network() string

	// Join creates a new sandbox for the given container ID and populates the
	// network resources allocated for the endpoint and joins the sandbox to
	// the endpoint. It returns the sandbox key to the caller
	Join(containerID string, options ...JoinOption) (*ContainerData, error)

	// Leave removes the sandbox associated with  container ID and detaches
	// the network resources populated in the sandbox
	Leave(containerID string, options ...LeaveOption) error

	// SandboxInfo returns the sandbox information for this endpoint.
	SandboxInfo() *sandbox.Info

	// Delete and detaches this endpoint from the network.
	Delete() error
}

// ContainerData is a set of data returned when a container joins an endpoint.
type ContainerData struct {
	SandboxKey string
	HostsPath  string
}

// JoinOption is a option setter function type used to pass varios options to
// endpoint Join method. The various setter functions of type JoinOption are
// provided by libnetwork, they look like JoinOption[...](...)
type JoinOption func(ep *endpoint)

// LeaveOption is a option setter function type used to pass varios options to
// endpoint Leave method. The various setter functions of type LeaveOption are
// provided by libnetwork, they look like LeaveOptionXXXX(...)
type LeaveOption func(ep *endpoint)

type containerConfig struct {
	hostName   string
	domainName string
	generic    map[string]interface{}
}

type containerInfo struct {
	id     string
	config containerConfig
	data   ContainerData
}

type endpoint struct {
	name        string
	id          types.UUID
	network     *network
	sandboxInfo *sandbox.Info
	sandBox     sandbox.Sandbox
	container   *containerInfo
	generic     map[string]interface{}
	context     map[string]interface{}
}

const prefix = "/var/lib/docker/network/files"

func (ep *endpoint) ID() string {
	return string(ep.id)
}

func (ep *endpoint) Name() string {
	return ep.name
}

func (ep *endpoint) Network() string {
	return ep.network.name
}

func (ep *endpoint) SandboxInfo() *sandbox.Info {
	if ep.sandboxInfo == nil {
		return nil
	}
	return ep.sandboxInfo.GetCopy()
}

// EndpointOption is a option setter function type used to pass various options to
// CreateEndpoint method. The various setter functions of type EndpointOption are
// provided by libnetwork, they look like EndpointOptionXXXX(...)
type EndpointOption func(ep *endpoint)

// EndpointOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
	return func(ep *endpoint) {
		ep.generic = generic
	}
}

func (ep *endpoint) processOptions(options ...EndpointOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

func createBasePath(dir string) error {
	err := os.MkdirAll(dir, 0644)
	if err != nil && !os.IsExist(err) {
		return err
	}

	return nil
}

func createHostsFile(path string) error {
	var f *os.File

	dir, _ := filepath.Split(path)
	err := createBasePath(dir)
	if err != nil {
		return err
	}

	f, err = os.Create(path)
	if err == nil {
		f.Close()
	}

	return err
}

func (ep *endpoint) Join(containerID string, options ...JoinOption) (*ContainerData, error) {
	var err error

	if containerID == "" {
		return nil, InvalidContainerIDError(containerID)
	}

	if ep.container != nil {
		return nil, ErrInvalidJoin
	}

	ep.container = &containerInfo{}
	defer func() {
		if err != nil {
			ep.container = nil
		}
	}()

	ep.processJoinOptions(options...)

	ep.container.data.HostsPath = prefix + "/" + containerID + "/hosts"
	err = createHostsFile(ep.container.data.HostsPath)
	if err != nil {
		return nil, err
	}

	err = ep.buildHostsFiles()
	if err != nil {
		return nil, err
	}

	sboxKey := sandbox.GenerateKey(containerID)
	sb, err := ep.network.ctrlr.sandboxAdd(sboxKey)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			ep.network.ctrlr.sandboxRm(sboxKey)
		}
	}()

	n := ep.network
	err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.Config.generic)
	if err != nil {
		return nil, err
	}

	sinfo := ep.SandboxInfo()
	if sinfo != nil {
		for _, i := range sinfo.Interfaces {
			err = sb.AddInterface(i)
			if err != nil {
				return nil, err
			}
		}

		err = sb.SetGateway(sinfo.Gateway)
		if err != nil {
			return nil, err
		}

		err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
		if err != nil {
			return nil, err
		}
	}

	ep.container.id = containerID
	ep.container.data.SandboxKey = sb.Key()

	cData := ep.container.data
	return &cData, nil
}

func (ep *endpoint) Leave(containerID string, options ...LeaveOption) error {
	if ep.container == nil || ep.container.id == "" ||
		containerID == "" || ep.container.id != containerID {
		return InvalidContainerIDError(containerID)
	}

	ep.processLeaveOptions(options...)

	n := ep.network
	err := n.driver.Leave(n.id, ep.id, ep.context)
	if err != nil {
		return err
	}

	ep.network.ctrlr.sandboxRm(ep.container.data.SandboxKey)
	ep.container = nil
	ep.context = nil
	return nil
}

func (ep *endpoint) Delete() error {
	var err error

	n := ep.network
	n.Lock()
	_, ok := n.endpoints[ep.id]
	if !ok {
		n.Unlock()
		return &UnknownEndpointError{name: ep.name, id: string(ep.id)}
	}

	delete(n.endpoints, ep.id)
	n.Unlock()
	defer func() {
		if err != nil {
			n.Lock()
			n.endpoints[ep.id] = ep
			n.Unlock()
		}
	}()

	err = n.driver.DeleteEndpoint(n.id, ep.id)
	return err
}

func (ep *endpoint) buildHostsFiles() error {
	var extraContent []etchosts.Record

	name := ep.container.config.hostName
	if ep.container.config.domainName != "" {
		name = name + "." + ep.container.config.domainName
	}

	IP := ""
	if ep.sandboxInfo != nil && ep.sandboxInfo.Interfaces[0] != nil &&
		ep.sandboxInfo.Interfaces[0].Address != nil {
		IP = ep.sandboxInfo.Interfaces[0].Address.IP.String()
	}

	return etchosts.Build(ep.container.data.HostsPath, IP, ep.container.config.hostName,
		ep.container.config.domainName, extraContent)
}

// JoinOptionHostname function returns an option setter for hostname option to
// be passed to endpoint Join method.
func JoinOptionHostname(name string) JoinOption {
	return func(ep *endpoint) {
		ep.container.config.hostName = name
	}
}

// JoinOptionDomainname function returns an option setter for domainname option to
// be passed to endpoint Join method.
func JoinOptionDomainname(name string) JoinOption {
	return func(ep *endpoint) {
		ep.container.config.domainName = name
	}
}

// JoinOptionGeneric function returns an option setter for Generic configuration
// that is not managed by libNetwork but can be used by the Drivers during the call to
// endpoint join method. Container Labels are a good example.
func JoinOptionGeneric(generic map[string]interface{}) JoinOption {
	return func(ep *endpoint) {
		ep.container.Config.generic = generic
	}
}

func (ep *endpoint) processJoinOptions(options ...JoinOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

// LeaveOptionGeneric function returns an option setter for Generic configuration
// that is not managed by libNetwork but can be used by the Drivers during the call to
// endpoint leave method. Container Labels are a good example.
func LeaveOptionGeneric(context map[string]interface{}) JoinOption {
	return func(ep *endpoint) {
		ep.context = context
	}
}

func (ep *endpoint) processLeaveOptions(options ...LeaveOption) {
	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}
<|endoftext|>


<commit_before>package httpclient

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"io"
	"net"
	xhttp "net/http"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/quan-xie/tuba/backoff"
	"github.com/quan-xie/tuba/retry"
	"github.com/quan-xie/tuba/util/xtime"
)

const (
	minRead               = 16 * 1024 // 16kb
	defaultRetryCount int = 0
)

type Config struct {
	Dial            xtime.Duration
	Timeout         xtime.Duration
	KeepAlive       xtime.Duration
	BackoffInterval xtime.Duration // Interval is second
	retryCount      int
}

type HttpClient struct {
	conf       *Config
	client     *xhttp.Client
	dialer     *net.Dialer
	transport  *xhttp.Transport
	retryCount int
	retrier    retry.Retriable
}

// NewHTTPClient returns a new instance of httpClient
func NewHTTPClient(c *Config) *HttpClient {
	dialer := &net.Dialer{
		Timeout:   time.Duration(c.Dial),
		KeepAlive: time.Duration(c.KeepAlive),
	}
	transport := &xhttp.Transport{
		DialContext:     dialer.DialContext,
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	bo := backoff.NewConstantBackoff(c.BackoffInterval)
	return &HttpClient{
		conf: c,
		client: &xhttp.Client{
			Transport: transport,
		},
		retryCount: defaultRetryCount,
		retrier:    retry.NewRetrier(bo),
	}
}

// SetRetryCount sets the retry count for the httpClient
func (c *HttpClient) SetRetryCount(count int) {
	c.retryCount = count
}

// SetRetryCount sets the retry count for the httpClient
func (c *HttpClient) SetRetrier(retrier retry.Retriable) {
	c.retrier = retrier
}

// Get makes a HTTP GET request to provided URL with context passed in
func (c *HttpClient) Get(ctx context.Context, url string, headers xhttp.Header, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodGet, url, nil)
	if err != nil {
		return errors.Wrap(err, "GET - request creation failed")
	}

	request.Header = headers

	return c.Do(ctx, request, res)
}

// Post makes a HTTP POST request to provided URL with context passed in
func (c *HttpClient) Post(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPost, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "POST - request creation failed")
	}
	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Put makes a HTTP PUT request to provided URL with context passed in
func (c *HttpClient) Put(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPut, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "PUT - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Patch makes a HTTP PATCH request to provided URL with context passed in
func (c *HttpClient) Patch(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPatch, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "PATCH - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Delete makes a HTTP DELETE request to provided URL with context passed in
func (c *HttpClient) Delete(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodDelete, url, nil)
	if err != nil {
		return errors.Wrap(err, "DELETE - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Do makes an HTTP request with the native `http.Do` interface and context passed in
func (c *HttpClient) Do(ctx context.Context, req *xhttp.Request, res interface{}) (err error) {
	for i := 0; i <= c.retryCount; i++ {
		if err = c.request(ctx, req, res); err != nil {
			backoffTime := c.retrier.NextInterval(i)
			time.Sleep(backoffTime)
			continue
		}
		break
	}
	return
}

func (c *HttpClient) request(ctx context.Context, req *xhttp.Request, res interface{}) (err error) {
	var (
		response *xhttp.Response
		bs       []byte
		cancel   func()
	)
	ctx, cancel = context.WithTimeout(ctx, time.Duration(c.conf.Timeout))
	defer cancel()
	response, err = c.client.Do(req.WithContext(ctx))
	if err != nil {
		select {
		case <-ctx.Done():
			err = ctx.Err()
		}
		return
	}
	defer response.Body.Close()
	if response.StatusCode >= xhttp.StatusInternalServerError {
		err = errors.Wrap(err, "")
		return
	}
	if bs, err = readAll(response.Body, minRead); err != nil {
		return
	}
	err = json.Unmarshal(bs, &res)
	return
}

func reqBody(contentType string, param interface{}) (body io.Reader) {
	var err error
	if contentType == MIMEPOSTForm {
		enc, ok := param.(string)
		if ok {
			body = strings.NewReader(enc)
		}
	}
	if contentType == MIMEJSON {
		buff := new(bytes.Buffer)
		err = json.NewEncoder(buff).Encode(param)
		if err != nil {
			return
		}
		body = buff
	}
	return
}

func readAll(r io.Reader, capacity int64) (b []byte, err error) {
	buf := bytes.NewBuffer(make([]byte, 0, capacity))
	// If the buffer overflows, we will get bytes.ErrTooLarge.
	// Return that as an error. Any other panic remains.
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
			err = panicErr
		} else {
			panic(e)
		}
	}()
	_, err = buf.ReadFrom(r)
	return buf.Bytes(), err
}
<commit_msg>modify errors warp<commit_after>package httpclient

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net"
	xhttp "net/http"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/quan-xie/tuba/backoff"
	"github.com/quan-xie/tuba/retry"
	"github.com/quan-xie/tuba/util/xtime"
)

const (
	minRead               = 16 * 1024 // 16kb
	defaultRetryCount int = 0
)

type Config struct {
	Dial            xtime.Duration
	Timeout         xtime.Duration
	KeepAlive       xtime.Duration
	BackoffInterval xtime.Duration // Interval is second
	retryCount      int
}

type HttpClient struct {
	conf       *Config
	client     *xhttp.Client
	dialer     *net.Dialer
	transport  *xhttp.Transport
	retryCount int
	retrier    retry.Retriable
}

// NewHTTPClient returns a new instance of httpClient
func NewHTTPClient(c *Config) *HttpClient {
	dialer := &net.Dialer{
		Timeout:   time.Duration(c.Dial),
		KeepAlive: time.Duration(c.KeepAlive),
	}
	transport := &xhttp.Transport{
		DialContext:     dialer.DialContext,
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	bo := backoff.NewConstantBackoff(c.BackoffInterval)
	return &HttpClient{
		conf: c,
		client: &xhttp.Client{
			Transport: transport,
		},
		retryCount: defaultRetryCount,
		retrier:    retry.NewRetrier(bo),
	}
}

// SetRetryCount sets the retry count for the httpClient
func (c *HttpClient) SetRetryCount(count int) {
	c.retryCount = count
}

// SetRetryCount sets the retry count for the httpClient
func (c *HttpClient) SetRetrier(retrier retry.Retriable) {
	c.retrier = retrier
}

// Get makes a HTTP GET request to provided URL with context passed in
func (c *HttpClient) Get(ctx context.Context, url string, headers xhttp.Header, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodGet, url, nil)
	if err != nil {
		return errors.Wrap(err, "GET - request creation failed")
	}

	request.Header = headers

	return c.Do(ctx, request, res)
}

// Post makes a HTTP POST request to provided URL with context passed in
func (c *HttpClient) Post(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPost, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "POST - request creation failed")
	}
	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Put makes a HTTP PUT request to provided URL with context passed in
func (c *HttpClient) Put(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPut, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "PUT - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Patch makes a HTTP PATCH request to provided URL with context passed in
func (c *HttpClient) Patch(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodPatch, url, reqBody(contentType, param))
	if err != nil {
		return errors.Wrap(err, "PATCH - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Delete makes a HTTP DELETE request to provided URL with context passed in
func (c *HttpClient) Delete(ctx context.Context, url, contentType string, headers xhttp.Header, param, res interface{}) (err error) {
	request, err := xhttp.NewRequest(xhttp.MethodDelete, url, nil)
	if err != nil {
		return errors.Wrap(err, "DELETE - request creation failed")
	}

	if headers == nil {
		headers = make(xhttp.Header)
	}
	headers.Set("Content-Type", contentType)
	request.Header = headers

	return c.Do(ctx, request, res)
}

// Do makes an HTTP request with the native `http.Do` interface and context passed in
func (c *HttpClient) Do(ctx context.Context, req *xhttp.Request, res interface{}) (err error) {
	for i := 0; i <= c.retryCount; i++ {
		if err = c.request(ctx, req, res); err != nil {
			backoffTime := c.retrier.NextInterval(i)
			time.Sleep(backoffTime)
			continue
		}
		break
	}
	return
}

func (c *HttpClient) request(ctx context.Context, req *xhttp.Request, res interface{}) (err error) {
	var (
		response *xhttp.Response
		bs       []byte
		cancel   func()
	)
	ctx, cancel = context.WithTimeout(ctx, time.Duration(c.conf.Timeout))
	defer cancel()
	response, err = c.client.Do(req.WithContext(ctx))
	if err != nil {
		select {
		case <-ctx.Done():
			err = ctx.Err()
		}
		return
	}
	defer response.Body.Close()
	if response.StatusCode >= xhttp.StatusInternalServerError {
		err = errors.Wrap(err, fmt.Sprintf("response.StatusCode %d", response.StatusCode))
		return
	}
	if bs, err = readAll(response.Body, minRead); err != nil {
		return
	}
	err = json.Unmarshal(bs, &res)
	return
}

func reqBody(contentType string, param interface{}) (body io.Reader) {
	var err error
	if contentType == MIMEPOSTForm {
		enc, ok := param.(string)
		if ok {
			body = strings.NewReader(enc)
		}
	}
	if contentType == MIMEJSON {
		buff := new(bytes.Buffer)
		err = json.NewEncoder(buff).Encode(param)
		if err != nil {
			return
		}
		body = buff
	}
	return
}

func readAll(r io.Reader, capacity int64) (b []byte, err error) {
	buf := bytes.NewBuffer(make([]byte, 0, capacity))
	// If the buffer overflows, we will get bytes.ErrTooLarge.
	// Return that as an error. Any other panic remains.
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
			err = panicErr
		} else {
			panic(e)
		}
	}()
	_, err = buf.ReadFrom(r)
	return buf.Bytes(), err
}
<|endoftext|>


<commit_before>// Package spritz provides a pure Go implementation of the Spritz stream cipher
// and hash.
package spritz

import (
	"crypto/cipher"
	"hash"
)

// NewStream returns a new instance of the Spritz cipher using the given key.
func NewStream(key []byte) cipher.Stream {
	var s state
	s.initialize(256)

	// key setup
	s.absorbBytes(key)
	if s.a > 0 {
		s.shuffle()
	}

	return stream{s: &s}
}

// NewHash returns a new instance of the Spritz hash with the given output size.
func NewHash(size int) hash.Hash {
	var s state
	d := digest{size: size, s: &s}
	d.Reset()
	return d
}

type stream struct {
	s *state
}

func (s stream) XORKeyStream(dst, src []byte) {
	for i, v := range src {
		dst[i] = v ^ byte(s.s.drip())
	}
}

type digest struct {
	size int
	s    *state
}

func (d digest) Sum(b []byte) []byte {
	s := *d.s // make a local copy
	s.absorbStop()
	s.absorb([]int{d.size})

	out := make([]byte, d.size)
	s.squeezeBytes(out)

	return append(b, out...)
}

func (d digest) Write(p []byte) (int, error) {
	d.s.absorbBytes(p)
	return len(p), nil
}

func (d digest) Size() int {
	return d.size
}

func (d digest) Reset() {
	d.s.initialize(256)
}

func (digest) BlockSize() int {
	return 1 // single byte
}

type state struct {
	// these are all ints instead of bytes to allow for states > 256
	n                int
	s                []int
	a, i, j, k, w, z int
}

func (s *state) initialize(n int) {
	*s = state{
		s: make([]int, n),
		w: 1,
		n: n,
	}
	for i := range s.s {
		s.s[i] = i
	}
}

func (s *state) update() {
	s.i = (s.i + s.w) % s.n
	y := (s.j + s.s[s.i]) % s.n
	s.j = (s.k + s.s[y]) % s.n
	s.k = (s.i + s.k + s.s[s.j]) % s.n
	t := s.s[s.i]
	s.s[s.i] = s.s[s.j]
	s.s[s.j] = t
}

func (s *state) output() int {
	y1 := (s.z + s.k) % s.n
	x1 := (s.i + s.s[y1]) % s.n
	y2 := (s.j + s.s[x1]) % s.n
	s.z = s.s[y2]
	return s.z
}

func (s *state) crush() {
	for i := 0; i < s.n/2; i++ {
		y := (s.n - 1) - i
		x1 := s.s[i]
		x2 := s.s[y]
		if x1 > x2 {
			s.s[i] = x2
			s.s[y] = x1
		} else {
			s.s[i] = x1
			s.s[y] = x2
		}
	}
}

func (s *state) whip() {
	r := s.n * 2
	for i := 0; i < r; i++ {
		s.update()
	}
	s.w = (s.w + 2) % s.n
}

func (s *state) shuffle() {
	s.whip()
	s.crush()
	s.whip()
	s.crush()
	s.whip()
	s.a = 0
}

func (s *state) absorbStop() {
	if s.a == s.n/2 {
		s.shuffle()
	}
	s.a = (s.a + 1) % s.n
}

func (s *state) absorbNibble(x int) {
	if s.a == s.n/2 {
		s.shuffle()
	}
	y := (s.n/2 + x) % s.n
	t := s.s[s.a]
	s.s[s.a] = s.s[y]
	s.s[y] = t
	s.a = (s.a + 1) % s.n
}

func (s *state) absorbValue(b int) {
	d := s.n / 16
	s.absorbNibble(b % d) // LOW
	s.absorbNibble(b / d) // HIGH
}

func (s *state) absorb(msg []int) {
	for _, v := range msg {
		s.absorbValue(v)
	}
}

func (s *state) absorbBytes(msg []byte) {
	for _, v := range msg {
		s.absorbValue(int(v))
	}
}

func (s *state) drip() int {
	if s.a > 0 {
		s.shuffle()
	}
	s.update()
	return s.output()
}

func (s *state) squeeze(out []int) {
	if s.a > 0 {
		s.shuffle()
	}
	for i := range out {
		out[i] = s.drip()
	}
}

func (s *state) squeezeBytes(out []byte) {
	if s.a > 0 {
		s.shuffle()
	}
	for i := range out {
		out[i] = byte(s.drip())
	}
}
<commit_msg>Don't alloc a slice we don't have to.<commit_after>// Package spritz provides a pure Go implementation of the Spritz stream cipher
// and hash.
package spritz

import (
	"crypto/cipher"
	"hash"
)

// NewStream returns a new instance of the Spritz cipher using the given key.
func NewStream(key []byte) cipher.Stream {
	var s state
	s.initialize(256)

	// key setup
	s.absorbBytes(key)
	if s.a > 0 {
		s.shuffle()
	}

	return stream{s: &s}
}

// NewHash returns a new instance of the Spritz hash with the given output size.
func NewHash(size int) hash.Hash {
	var s state
	d := digest{size: size, s: &s}
	d.Reset()
	return d
}

type stream struct {
	s *state
}

func (s stream) XORKeyStream(dst, src []byte) {
	for i, v := range src {
		dst[i] = v ^ byte(s.s.drip())
	}
}

type digest struct {
	size int
	s    *state
}

func (d digest) Sum(b []byte) []byte {
	s := *d.s // make a local copy
	s.absorbStop()
	s.absorbValue(d.size)

	out := make([]byte, d.size)
	s.squeezeBytes(out)

	return append(b, out...)
}

func (d digest) Write(p []byte) (int, error) {
	d.s.absorbBytes(p)
	return len(p), nil
}

func (d digest) Size() int {
	return d.size
}

func (d digest) Reset() {
	d.s.initialize(256)
}

func (digest) BlockSize() int {
	return 1 // single byte
}

type state struct {
	// these are all ints instead of bytes to allow for states > 256
	n                int
	s                []int
	a, i, j, k, w, z int
}

func (s *state) initialize(n int) {
	*s = state{
		s: make([]int, n),
		w: 1,
		n: n,
	}
	for i := range s.s {
		s.s[i] = i
	}
}

func (s *state) update() {
	s.i = (s.i + s.w) % s.n
	y := (s.j + s.s[s.i]) % s.n
	s.j = (s.k + s.s[y]) % s.n
	s.k = (s.i + s.k + s.s[s.j]) % s.n
	t := s.s[s.i]
	s.s[s.i] = s.s[s.j]
	s.s[s.j] = t
}

func (s *state) output() int {
	y1 := (s.z + s.k) % s.n
	x1 := (s.i + s.s[y1]) % s.n
	y2 := (s.j + s.s[x1]) % s.n
	s.z = s.s[y2]
	return s.z
}

func (s *state) crush() {
	for i := 0; i < s.n/2; i++ {
		y := (s.n - 1) - i
		x1 := s.s[i]
		x2 := s.s[y]
		if x1 > x2 {
			s.s[i] = x2
			s.s[y] = x1
		} else {
			s.s[i] = x1
			s.s[y] = x2
		}
	}
}

func (s *state) whip() {
	r := s.n * 2
	for i := 0; i < r; i++ {
		s.update()
	}
	s.w = (s.w + 2) % s.n
}

func (s *state) shuffle() {
	s.whip()
	s.crush()
	s.whip()
	s.crush()
	s.whip()
	s.a = 0
}

func (s *state) absorbStop() {
	if s.a == s.n/2 {
		s.shuffle()
	}
	s.a = (s.a + 1) % s.n
}

func (s *state) absorbNibble(x int) {
	if s.a == s.n/2 {
		s.shuffle()
	}
	y := (s.n/2 + x) % s.n
	t := s.s[s.a]
	s.s[s.a] = s.s[y]
	s.s[y] = t
	s.a = (s.a + 1) % s.n
}

func (s *state) absorbValue(b int) {
	d := s.n / 16
	s.absorbNibble(b % d) // LOW
	s.absorbNibble(b / d) // HIGH
}

func (s *state) absorb(msg []int) {
	for _, v := range msg {
		s.absorbValue(v)
	}
}

func (s *state) absorbBytes(msg []byte) {
	for _, v := range msg {
		s.absorbValue(int(v))
	}
}

func (s *state) drip() int {
	if s.a > 0 {
		s.shuffle()
	}
	s.update()
	return s.output()
}

func (s *state) squeeze(out []int) {
	if s.a > 0 {
		s.shuffle()
	}
	for i := range out {
		out[i] = s.drip()
	}
}

func (s *state) squeezeBytes(out []byte) {
	if s.a > 0 {
		s.shuffle()
	}
	for i := range out {
		out[i] = byte(s.drip())
	}
}
<|endoftext|>


<commit_before>package spyrun

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sync"
	"time"

	"github.com/naoina/toml"
)

const (
	// SpyRunFile convert to target file.
	SpyRunFile = "\\$SPYRUN_FILE"
)

/**
 * Toml config.
 */
type tomlConfig struct {
	Spyconf struct {
		Sleep string `toml:"sleep"`
	}
	SpyTables map[string]spyTable `toml:"spys"`
}

type spyTable struct {
	File    string `toml:"file"`
	Command string `toml:"command"`
}

/**
 * Spyrun config.
 */
type spyMap map[string][]*spyst

type spyst struct {
	filePath   string
	command    string
	modifyTime time.Time
	mu         *sync.Mutex
}

type spyrun struct {
	conf tomlConfig
	spym spyMap
}

// New Create and return *spyrun.
func New() *spyrun {
	s := new(spyrun)
	s.spym = make(spyMap)
	return s
}

// Run run spyrun.
func Run(tomlpath string) error {
	return New().run(tomlpath)
}

func (s *spyrun) run(tomlpath string) error {
	var err error

	err = s.loadToml(tomlpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse toml ! %s", err.Error())
		os.Exit(1)
	}

	err = s.createSpyMapFromSpyTables()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get spys ! %s", err.Error())
		os.Exit(1)
	}

	ch := make(chan *spyst)
	go s.spyFiles(ch)

	for {
		spyst := <-ch
		log.Printf("[%s] is modified !\n", spyst.filePath)
		go s.executeCommand(spyst)
	}

}

func (s *spyrun) convertSpyVar(file, command string) (string, error) { // {{{
	var err error

	re := regexp.MustCompile(SpyRunFile)

	if matched := re.MatchString(command); matched {
		command = re.ReplaceAllString(command, file)
	}

	return command, err
} // }}}

func (s *spyrun) createSpyMapFromSpyTables() error {
	var err error

	for k, v := range s.conf.SpyTables {
		s.spym[k] = make([]*spyst, 0)
		files, err := filepath.Glob(v.File)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to search glob pattern. %s", v.File)
			os.Exit(1)
		}
		for _, file := range files {
			spyst := new(spyst)
			spyst.filePath = file
			fi, err := os.Stat(file)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to get FileInfo. %s [%s]", file, err.Error())
				os.Exit(1)
			}
			spyst.modifyTime = fi.ModTime()
			spyst.command, err = s.convertSpyVar(file, v.Command)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to convert spy variable. %s", v.Command)
				os.Exit(1)
			}
			spyst.mu = new(sync.Mutex)
			s.spym[k] = append(s.spym[k], spyst)
		}
	}

	return err
}

func (s *spyrun) loadToml(tomlpath string) error { // {{{
	var err error

	if _, err = os.Stat(tomlpath); err != nil {
		fmt.Fprintf(os.Stderr, "%s is not found !", tomlpath)
		os.Exit(1)
	}

	f, err := os.Open(tomlpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open %s", tomlpath)
		os.Exit(1)
	}
	defer f.Close()

	buf, err := ioutil.ReadAll(f)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load %s", tomlpath)
		os.Exit(1)
	}

	err = toml.Unmarshal(buf, &s.conf)

	return err
} // }}}

func (s *spyrun) spyFiles(ch chan *spyst) {
	var err error
	var sleep time.Duration
	if s.conf.Spyconf.Sleep != "" {
		sleep, err = time.ParseDuration(s.conf.Spyconf.Sleep)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse sleep duration. %v", err)
			os.Exit(1)
		}
	} else {
		sleep = time.Duration(100) * time.Millisecond
	}
	log.Println("sleep:", sleep)
	for {
		for _, spysts := range s.spym {
			for _, spyst := range spysts {
				fi, err := os.Stat(spyst.filePath)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Failed to get FileInfo. %s, [%s]", spyst.filePath, err.Error())
					os.Exit(1)
				}
				if fi.ModTime() != spyst.modifyTime {
					spyst.modifyTime = fi.ModTime()
					ch <- spyst
				}
			}
		}
		time.Sleep(sleep)
	}
}

func (s *spyrun) executeCommand(spy *spyst) error {
	var err error
	spy.mu.Lock()
	defer spy.mu.Unlock()
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/c", spy.command)
	} else {
		cmd = exec.Command("sh", "-c", spy.command)
	}
	log.Printf("Execute command. [%s]", spy.command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Println(err)
	}

	return err
}
<commit_msg>Debug output spyfiles.<commit_after>package spyrun

import ( // {{{
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sync"
	"time"

	"github.com/naoina/toml"
) // }}}

const ( // {{{
	// SpyRunFile convert to target file.
	SpyRunFile = "\\$SPYRUN_FILE"
) // }}}

/**
 * Toml config.
 */
type tomlConfig struct { // {{{
	Spyconf struct {
		Sleep string `toml:"sleep"`
	}
	SpyTables map[string]spyTable `toml:"spys"`
} // }}}

type spyTable struct { // {{{
	File    string `toml:"file"`
	Command string `toml:"command"`
} // }}}

/**
 * Spyrun config.
 */
type spyMap map[string][]*spyst

type spyst struct { // {{{
	filePath   string
	command    string
	modifyTime time.Time
	mu         *sync.Mutex
} // }}}

type spyrun struct { // {{{
	conf tomlConfig
	spym spyMap
} // }}}

// New Create and return *spyrun.
func New() *spyrun { // {{{
	s := new(spyrun)
	s.spym = make(spyMap)
	return s
} // }}}

// Run run spyrun.
func Run(tomlpath string) error { // {{{
	return New().run(tomlpath)
} // }}}

func (s *spyrun) run(tomlpath string) error { // {{{
	var err error

	err = s.loadToml(tomlpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse toml ! %s", err.Error())
		os.Exit(1)
	}

	err = s.createSpyMapFromSpyTables()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get spys ! %s", err.Error())
		os.Exit(1)
	}

	ch := make(chan *spyst)
	go s.spyFiles(ch)

	for {
		spyst := <-ch
		log.Printf("[%s] is modified !\n", spyst.filePath)
		go s.executeCommand(spyst)
	}

} // }}}

func (s *spyrun) convertSpyVar(file, command string) (string, error) { // {{{
	var err error

	re := regexp.MustCompile(SpyRunFile)

	if matched := re.MatchString(command); matched {
		command = re.ReplaceAllString(command, file)
	}

	return command, err
} // }}}

func (s *spyrun) createSpyMapFromSpyTables() error { // {{{
	var err error

	for k, v := range s.conf.SpyTables {
		s.spym[k] = make([]*spyst, 0)
		files, err := filepath.Glob(v.File)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to search glob pattern. %s", v.File)
			os.Exit(1)
		}
		for _, file := range files {
			spyst := new(spyst)
			spyst.filePath = file
			fi, err := os.Stat(file)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to get FileInfo. %s [%s]", file, err.Error())
				os.Exit(1)
			}
			spyst.modifyTime = fi.ModTime()
			spyst.command, err = s.convertSpyVar(file, v.Command)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to convert spy variable. %s", v.Command)
				os.Exit(1)
			}
			spyst.mu = new(sync.Mutex)
			log.Printf("%s: {file: [%s], command: [%s]}\n", k, spyst.filePath, spyst.command)
			s.spym[k] = append(s.spym[k], spyst)
		}
	}
	return err
} // }}}

func (s *spyrun) loadToml(tomlpath string) error { // {{{
	var err error

	if _, err = os.Stat(tomlpath); err != nil {
		fmt.Fprintf(os.Stderr, "%s is not found !", tomlpath)
		os.Exit(1)
	}

	f, err := os.Open(tomlpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open %s", tomlpath)
		os.Exit(1)
	}
	defer f.Close()

	buf, err := ioutil.ReadAll(f)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load %s", tomlpath)
		os.Exit(1)
	}

	err = toml.Unmarshal(buf, &s.conf)

	return err
} // }}}

func (s *spyrun) spyFiles(ch chan *spyst) { // {{{
	var err error
	var sleep time.Duration
	if s.conf.Spyconf.Sleep != "" {
		sleep, err = time.ParseDuration(s.conf.Spyconf.Sleep)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse sleep duration. %v", err)
			os.Exit(1)
		}
	} else {
		sleep = time.Duration(100) * time.Millisecond
	}
	log.Println("sleep:", sleep)
	for {
		for _, spysts := range s.spym {
			for _, spyst := range spysts {
				fi, err := os.Stat(spyst.filePath)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Failed to get FileInfo. %s, [%s]", spyst.filePath, err.Error())
					os.Exit(1)
				}
				if fi.ModTime() != spyst.modifyTime {
					spyst.modifyTime = fi.ModTime()
					ch <- spyst
				}
			}
		}
		time.Sleep(sleep)
	}
} // }}}

func (s *spyrun) executeCommand(spy *spyst) error { // {{{
	var err error
	spy.mu.Lock()
	defer spy.mu.Unlock()
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/c", spy.command)
	} else {
		cmd = exec.Command("sh", "-c", spy.command)
	}
	log.Printf("Execute command. [%s]", spy.command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Println(err)
	}

	return err
} // }}}
<|endoftext|>