<commit_before>// Package route53 implements a DNS provider for solving the DNS-01 challenge
// using AWS Route 53 DNS.
package route53

import (
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/golang/glog"

	"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
	pkgutil "github.com/jetstack/cert-manager/pkg/util"
)

const (
	maxRetries = 5
	route53TTL = 10
)

// DNSProvider implements the util.ChallengeProvider interface
type DNSProvider struct {
	client       *route53.Route53
	hostedZoneID string
}

// customRetryer implements the client.Retryer interface by composing the
// DefaultRetryer. It controls the logic for retrying recoverable request
// errors (e.g. when rate limits are exceeded).
type customRetryer struct {
	client.DefaultRetryer
}

// RetryRules overwrites the DefaultRetryer's method.
// It uses a basic exponential backoff algorithm that returns an initial
// delay of ~400ms with an upper limit of ~30 seconds which should prevent
// causing a high number of consecutive throttling errors.
// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
func (d customRetryer) RetryRules(r *request.Request) time.Duration {
	retryCount := r.RetryCount
	if retryCount > 7 {
		retryCount = 7
	}

	delay := (1 << uint(retryCount)) * (rand.Intn(50) + 200)
	return time.Duration(delay) * time.Millisecond
}

// NewDNSProvider returns a DNSProvider instance configured for the AWS
// Route 53 service using static credentials from its parameters or, if they're
// unset and the 'ambient' option is set, credentials from the environment.
func NewDNSProvider(accessKeyID, secretAccessKey, hostedZoneID, region string, ambient bool) (*DNSProvider, error) {
	if accessKeyID == "" && secretAccessKey == "" {
		if !ambient {
			return nil, fmt.Errorf("unable to construct route53 provider: empty credentials; perhaps you meant to enable ambient credentials?")
		}
	} else if accessKeyID == "" || secretAccessKey == "" {
		// It's always an error to set one of those but not the other
		return nil, fmt.Errorf("unable to construct route53 provider: only one of access and secret key was provided")
	}

	useAmbientCredentials := ambient && (accessKeyID == "" && secretAccessKey == "")

	r := customRetryer{}
	r.NumMaxRetries = maxRetries
	config := request.WithRetryer(aws.NewConfig(), r)
	sessionOpts := session.Options{}

	if useAmbientCredentials {
		glog.V(5).Infof("using ambient credentials")
		// Leaving credentials unset results in a default credential chain being
		// used; this chain is a reasonable default for getting ambient creds.
		// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
	} else {
		glog.V(5).Infof("not using ambient credentials")
		config.WithCredentials(credentials.NewStaticCredentials(accessKeyID, secretAccessKey, ""))
		// also disable 'ambient' region sources
		sessionOpts.SharedConfigState = session.SharedConfigDisable
	}

	// If ambient credentials aren't permitted, always set the region, even if to
	// empty string, to avoid it falling back on the environment.
	if region != "" || !useAmbientCredentials {
		config.WithRegion(region)
	}
	sess, err := session.NewSessionWithOptions(sessionOpts)
	if err != nil {
		return nil, fmt.Errorf("unable to create aws session: %s", err)
	}
	sess.Handlers.Build.PushBack(request.WithAppendUserAgent(pkgutil.CertManagerUserAgent))
	client := route53.New(sess, config)

	return &DNSProvider{
		client:       client,
		hostedZoneID: hostedZoneID,
	}, nil
}

// Timeout returns the timeout and interval to use when checking for DNS
// propagation. Adjusting here to cope with spikes in propagation times.
func (*DNSProvider) Timeout() (timeout, interval time.Duration) {
	return 120 * time.Second, 2 * time.Second
}

// Present creates a TXT record using the specified parameters
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, _ := util.DNS01Record(domain, keyAuth)
	value = `"` + value + `"`
	return r.changeRecord("UPSERT", fqdn, value, route53TTL)
}

// CleanUp removes the TXT record matching the specified parameters
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, value, _ := util.DNS01Record(domain, keyAuth)
	value = `"` + value + `"`
	return r.changeRecord("DELETE", fqdn, value, route53TTL)
}

func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
	hostedZoneID, err := r.getHostedZoneID(fqdn)
	if err != nil {
		return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err)
	}

	recordSet := newTXTRecordSet(fqdn, value, ttl)
	reqParams := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(hostedZoneID),
		ChangeBatch: &route53.ChangeBatch{
			Comment: aws.String("Managed by cert-manager"),
			Changes: []*route53.Change{
				{
					Action:            aws.String(action),
					ResourceRecordSet: recordSet,
				},
			},
		},
	}

	resp, err := r.client.ChangeResourceRecordSets(reqParams)
	if err != nil {
		return fmt.Errorf("Failed to change Route 53 record set: %v", err)
	}

	statusID := resp.ChangeInfo.Id

	return util.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
		reqParams := &route53.GetChangeInput{
			Id: statusID,
		}
		resp, err := r.client.GetChange(reqParams)
		if err != nil {
			return false, fmt.Errorf("Failed to query Route 53 change status: %v", err)
		}
		if *resp.ChangeInfo.Status == route53.ChangeStatusInsync {
			return true, nil
		}
		return false, nil
	})
}

func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
	if r.hostedZoneID != "" {
		return r.hostedZoneID, nil
	}

	authZone, err := util.FindZoneByFqdn(fqdn, util.RecursiveNameservers)
	if err != nil {
		return "", fmt.Errorf("error finding zone from fqdn: %v", err)
	}

	// .DNSName should not have a trailing dot
	reqParams := &route53.ListHostedZonesByNameInput{
		DNSName: aws.String(util.UnFqdn(authZone)),
	}
	resp, err := r.client.ListHostedZonesByName(reqParams)
	if err != nil {
		return "", err
	}

	var hostedZoneID string
	for _, hostedZone := range resp.HostedZones {
		// .Name has a trailing dot
		if !*hostedZone.Config.PrivateZone && *hostedZone.Name == authZone {
			hostedZoneID = *hostedZone.Id
			break
		}
	}

	if len(hostedZoneID) == 0 {
		return "", fmt.Errorf("Zone %s not found in Route 53 for domain %s", authZone, fqdn)
	}

	if strings.HasPrefix(hostedZoneID, "/hostedzone/") {
		hostedZoneID = strings.TrimPrefix(hostedZoneID, "/hostedzone/")
	}

	return hostedZoneID, nil
}

func newTXTRecordSet(fqdn, value string, ttl int) *route53.ResourceRecordSet {
	return &route53.ResourceRecordSet{
		Name: aws.String(fqdn),
		Type: aws.String("TXT"),
		TTL:  aws.Int64(int64(ttl)),
		ResourceRecords: []*route53.ResourceRecord{
			{Value: aws.String(value)},
		},
	}
}
<commit_msg>issuer/route53: fix delete for 'NotExist' errors<commit_after>// Package route53 implements a DNS provider for solving the DNS-01 challenge
// using AWS Route 53 DNS.
package route53

import (
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/golang/glog"

	"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
	pkgutil "github.com/jetstack/cert-manager/pkg/util"
)

const (
	maxRetries = 5
	route53TTL = 10
)

// DNSProvider implements the util.ChallengeProvider interface
type DNSProvider struct {
	client       *route53.Route53
	hostedZoneID string
}

// customRetryer implements the client.Retryer interface by composing the
// DefaultRetryer. It controls the logic for retrying recoverable request
// errors (e.g. when rate limits are exceeded).
type customRetryer struct {
	client.DefaultRetryer
}

// RetryRules overwrites the DefaultRetryer's method.
// It uses a basic exponential backoff algorithm that returns an initial
// delay of ~400ms with an upper limit of ~30 seconds which should prevent
// causing a high number of consecutive throttling errors.
// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
func (d customRetryer) RetryRules(r *request.Request) time.Duration {
	retryCount := r.RetryCount
	if retryCount > 7 {
		retryCount = 7
	}

	delay := (1 << uint(retryCount)) * (rand.Intn(50) + 200)
	return time.Duration(delay) * time.Millisecond
}

// NewDNSProvider returns a DNSProvider instance configured for the AWS
// Route 53 service using static credentials from its parameters or, if they're
// unset and the 'ambient' option is set, credentials from the environment.
func NewDNSProvider(accessKeyID, secretAccessKey, hostedZoneID, region string, ambient bool) (*DNSProvider, error) {
	if accessKeyID == "" && secretAccessKey == "" {
		if !ambient {
			return nil, fmt.Errorf("unable to construct route53 provider: empty credentials; perhaps you meant to enable ambient credentials?")
		}
	} else if accessKeyID == "" || secretAccessKey == "" {
		// It's always an error to set one of those but not the other
		return nil, fmt.Errorf("unable to construct route53 provider: only one of access and secret key was provided")
	}

	useAmbientCredentials := ambient && (accessKeyID == "" && secretAccessKey == "")

	r := customRetryer{}
	r.NumMaxRetries = maxRetries
	config := request.WithRetryer(aws.NewConfig(), r)
	sessionOpts := session.Options{}

	if useAmbientCredentials {
		glog.V(5).Infof("using ambient credentials")
		// Leaving credentials unset results in a default credential chain being
		// used; this chain is a reasonable default for getting ambient creds.
		// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
	} else {
		glog.V(5).Infof("not using ambient credentials")
		config.WithCredentials(credentials.NewStaticCredentials(accessKeyID, secretAccessKey, ""))
		// also disable 'ambient' region sources
		sessionOpts.SharedConfigState = session.SharedConfigDisable
	}

	// If ambient credentials aren't permitted, always set the region, even if to
	// empty string, to avoid it falling back on the environment.
	if region != "" || !useAmbientCredentials {
		config.WithRegion(region)
	}
	sess, err := session.NewSessionWithOptions(sessionOpts)
	if err != nil {
		return nil, fmt.Errorf("unable to create aws session: %s", err)
	}
	sess.Handlers.Build.PushBack(request.WithAppendUserAgent(pkgutil.CertManagerUserAgent))
	client := route53.New(sess, config)

	return &DNSProvider{
		client:       client,
		hostedZoneID: hostedZoneID,
	}, nil
}

// Timeout returns the timeout and interval to use when checking for DNS
// propagation. Adjusting here to cope with spikes in propagation times.
func (*DNSProvider) Timeout() (timeout, interval time.Duration) {
	return 120 * time.Second, 2 * time.Second
}

// Present creates a TXT record using the specified parameters
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, _ := util.DNS01Record(domain, keyAuth)
	value = `"` + value + `"`
	return r.changeRecord(route53.ChangeActionUpsert, fqdn, value, route53TTL)
}

// CleanUp removes the TXT record matching the specified parameters
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, value, _ := util.DNS01Record(domain, keyAuth)
	value = `"` + value + `"`
	return r.changeRecord(route53.ChangeActionDelete, fqdn, value, route53TTL)
}

func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
	hostedZoneID, err := r.getHostedZoneID(fqdn)
	if err != nil {
		return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err)
	}

	recordSet := newTXTRecordSet(fqdn, value, ttl)
	reqParams := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(hostedZoneID),
		ChangeBatch: &route53.ChangeBatch{
			Comment: aws.String("Managed by cert-manager"),
			Changes: []*route53.Change{
				{
					Action:            &action,
					ResourceRecordSet: recordSet,
				},
			},
		},
	}

	resp, err := r.client.ChangeResourceRecordSets(reqParams)
	if err != nil {
		if awserr, ok := err.(awserr.Error); ok {
			if action == route53.ChangeActionDelete && awserr.Code() == route53.ErrCodeInvalidChangeBatch {
				// If we try to delete something and get a 'InvalidChangeBatch' that
				// means it's already deleted, no need to consider it an error.
				return nil
			}
		}
		return fmt.Errorf("Failed to change Route 53 record set: %v", err)

	}

	statusID := resp.ChangeInfo.Id

	return util.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
		reqParams := &route53.GetChangeInput{
			Id: statusID,
		}
		resp, err := r.client.GetChange(reqParams)
		if err != nil {
			return false, fmt.Errorf("Failed to query Route 53 change status: %v", err)
		}
		if *resp.ChangeInfo.Status == route53.ChangeStatusInsync {
			return true, nil
		}
		return false, nil
	})
}

func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
	if r.hostedZoneID != "" {
		return r.hostedZoneID, nil
	}

	authZone, err := util.FindZoneByFqdn(fqdn, util.RecursiveNameservers)
	if err != nil {
		return "", fmt.Errorf("error finding zone from fqdn: %v", err)
	}

	// .DNSName should not have a trailing dot
	reqParams := &route53.ListHostedZonesByNameInput{
		DNSName: aws.String(util.UnFqdn(authZone)),
	}
	resp, err := r.client.ListHostedZonesByName(reqParams)
	if err != nil {
		return "", err
	}

	var hostedZoneID string
	for _, hostedZone := range resp.HostedZones {
		// .Name has a trailing dot
		if !*hostedZone.Config.PrivateZone && *hostedZone.Name == authZone {
			hostedZoneID = *hostedZone.Id
			break
		}
	}

	if len(hostedZoneID) == 0 {
		return "", fmt.Errorf("Zone %s not found in Route 53 for domain %s", authZone, fqdn)
	}

	if strings.HasPrefix(hostedZoneID, "/hostedzone/") {
		hostedZoneID = strings.TrimPrefix(hostedZoneID, "/hostedzone/")
	}

	return hostedZoneID, nil
}

func newTXTRecordSet(fqdn, value string, ttl int) *route53.ResourceRecordSet {
	return &route53.ResourceRecordSet{
		Name: aws.String(fqdn),
		Type: aws.String(route53.RRTypeTxt),
		TTL:  aws.Int64(int64(ttl)),
		ResourceRecords: []*route53.ResourceRecord{
			{Value: aws.String(value)},
		},
	}
}
<|endoftext|>


<commit_before>package queueinformer

import (
	"context"
	"fmt"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
)

// KeyFunc returns a key for the given object and a bool which is true if the key was
// successfully generated and false otherwise.
type KeyFunc func(obj interface{}) (string, bool)

// QueueInformer ties an informer to a queue in order to process events from the informer
// the informer watches objects of interest and adds objects to the queue for processing
// the syncHandler is called for all objects on the queue
type QueueInformer struct {
	metrics.MetricsProvider

	logger   *logrus.Logger
	queue    workqueue.RateLimitingInterface
	informer cache.SharedIndexInformer
	indexer  cache.Indexer
	keyFunc  KeyFunc
	syncer   kubestate.Syncer
}

// Sync invokes all registered sync handlers in the QueueInformer's chain
func (q *QueueInformer) Sync(ctx context.Context, event kubestate.ResourceEvent) error {
	return q.syncer.Sync(ctx, event)
}

// Enqueue adds a key to the queue. If obj is a key already it gets added directly.
// Otherwise, the key is extracted via keyFunc.
func (q *QueueInformer) Enqueue(event kubestate.ResourceEvent) {
	if event == nil {
		// Don't enqueue nil events
		return
	}

	resource := event.Resource()
	if event.Type() == kubestate.ResourceDeleted {
		// Get object from tombstone if possible
		if tombstone, ok := resource.(cache.DeletedFinalStateUnknown); ok {
			resource = tombstone
		}
	} else {
		// Extract key for add and update events
		if key, ok := q.key(resource); ok {
			resource = key
		}
	}

	// Create new resource event and add to queue
	e := kubestate.NewResourceEvent(event.Type(), resource)
	q.logger.WithField("event", e).Trace("enqueuing resource event")
	q.queue.Add(e)
}

// key turns an object into a key for the indexer.
func (q *QueueInformer) key(obj interface{}) (string, bool) {
	return q.keyFunc(obj)
}

// resourceHandlers provides the default implementation for responding to events
// these simply Log the event and add the object's key to the queue for later processing.
func (q *QueueInformer) resourceHandlers(ctx context.Context) *cache.ResourceEventHandlerFuncs {
	return &cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, obj))
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, newObj))
		},
		DeleteFunc: func(obj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceDeleted, obj))
		},
	}
}

// metricHandlers provides the default implementation for handling metrics in response to events.
func (q *QueueInformer) metricHandlers() *cache.ResourceEventHandlerFuncs {
	return &cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on add event")
			}
		},
		DeleteFunc: func(obj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on delete event")
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", newObj).Warn("error handling metrics on update event")
			}
		},
	}
}

func NewQueue(ctx context.Context, options ...Option) (*QueueInformer, error) {
	config := defaultConfig()
	config.apply(options)

	if err := config.validateQueue(); err != nil {
		return nil, err
	}

	queue := &QueueInformer{
		MetricsProvider: config.provider,
		logger:          config.logger,
		queue:           config.queue,
		keyFunc:         config.keyFunc,
		syncer:          config.syncer,
	}

	return queue, nil
}

// NewQueueInformer returns a new QueueInformer configured with options.
func NewQueueInformer(ctx context.Context, options ...Option) (*QueueInformer, error) {
	// Get default config and apply given options
	config := defaultConfig()
	config.apply(options)
	config.complete()

	return newQueueInformerFromConfig(ctx, config)
}

func newQueueInformerFromConfig(ctx context.Context, config *queueInformerConfig) (*QueueInformer, error) {
	if err := config.validateQueueInformer(); err != nil {
		return nil, err
	}

	// Extract config
	queueInformer := &QueueInformer{
		MetricsProvider: config.provider,
		logger:          config.logger,
		queue:           config.queue,
		indexer:         config.indexer,
		informer:        config.informer,
		keyFunc:         config.keyFunc,
		syncer:          config.syncer,
	}

	// Register event handlers for resource and metrics
	if queueInformer.informer != nil {
		queueInformer.informer.AddEventHandler(queueInformer.resourceHandlers(ctx))
		queueInformer.informer.AddEventHandler(queueInformer.metricHandlers())
	}

	return queueInformer, nil
}

// LegacySyncHandler is a deprecated signature for syncing resources.
type LegacySyncHandler func(obj interface{}) error

// ToSyncer returns the Syncer equivalent of the sync handler.
func (l LegacySyncHandler) ToSyncer() kubestate.Syncer {
	return l.ToSyncerWithDelete(nil)
}

// ToSyncerWithDelete returns the Syncer equivalent of the given sync handler and delete function.
func (l LegacySyncHandler) ToSyncerWithDelete(onDelete func(obj interface{})) kubestate.Syncer {
	var syncer kubestate.SyncFunc = func(ctx context.Context, event kubestate.ResourceEvent) error {
		logrus.New().WithField("event", fmt.Sprintf("%+v", event)).Trace("legacy syncer received event")
		switch event.Type() {
		case kubestate.ResourceDeleted:
			if onDelete != nil {
				onDelete(event.Resource())
			}
		case kubestate.ResourceAdded:
			// Added and updated are treated the same
			fallthrough
		case kubestate.ResourceUpdated:
			return l(event.Resource())
		default:
			return errors.Errorf("unexpected resource event type: %s", event.Type())
		}

		return nil
	}

	return syncer
}
<commit_msg>Remove unused trace log from LegacySyncHandler.<commit_after>package queueinformer

import (
	"context"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
)

// KeyFunc returns a key for the given object and a bool which is true if the key was
// successfully generated and false otherwise.
type KeyFunc func(obj interface{}) (string, bool)

// QueueInformer ties an informer to a queue in order to process events from the informer
// the informer watches objects of interest and adds objects to the queue for processing
// the syncHandler is called for all objects on the queue
type QueueInformer struct {
	metrics.MetricsProvider

	logger   *logrus.Logger
	queue    workqueue.RateLimitingInterface
	informer cache.SharedIndexInformer
	indexer  cache.Indexer
	keyFunc  KeyFunc
	syncer   kubestate.Syncer
}

// Sync invokes all registered sync handlers in the QueueInformer's chain
func (q *QueueInformer) Sync(ctx context.Context, event kubestate.ResourceEvent) error {
	return q.syncer.Sync(ctx, event)
}

// Enqueue adds a key to the queue. If obj is a key already it gets added directly.
// Otherwise, the key is extracted via keyFunc.
func (q *QueueInformer) Enqueue(event kubestate.ResourceEvent) {
	if event == nil {
		// Don't enqueue nil events
		return
	}

	resource := event.Resource()
	if event.Type() == kubestate.ResourceDeleted {
		// Get object from tombstone if possible
		if tombstone, ok := resource.(cache.DeletedFinalStateUnknown); ok {
			resource = tombstone
		}
	} else {
		// Extract key for add and update events
		if key, ok := q.key(resource); ok {
			resource = key
		}
	}

	// Create new resource event and add to queue
	e := kubestate.NewResourceEvent(event.Type(), resource)
	q.logger.WithField("event", e).Trace("enqueuing resource event")
	q.queue.Add(e)
}

// key turns an object into a key for the indexer.
func (q *QueueInformer) key(obj interface{}) (string, bool) {
	return q.keyFunc(obj)
}

// resourceHandlers provides the default implementation for responding to events
// these simply Log the event and add the object's key to the queue for later processing.
func (q *QueueInformer) resourceHandlers(ctx context.Context) *cache.ResourceEventHandlerFuncs {
	return &cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, obj))
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, newObj))
		},
		DeleteFunc: func(obj interface{}) {
			q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceDeleted, obj))
		},
	}
}

// metricHandlers provides the default implementation for handling metrics in response to events.
func (q *QueueInformer) metricHandlers() *cache.ResourceEventHandlerFuncs {
	return &cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on add event")
			}
		},
		DeleteFunc: func(obj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on delete event")
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			if err := q.HandleMetrics(); err != nil {
				q.logger.WithError(err).WithField("key", newObj).Warn("error handling metrics on update event")
			}
		},
	}
}

func NewQueue(ctx context.Context, options ...Option) (*QueueInformer, error) {
	config := defaultConfig()
	config.apply(options)

	if err := config.validateQueue(); err != nil {
		return nil, err
	}

	queue := &QueueInformer{
		MetricsProvider: config.provider,
		logger:          config.logger,
		queue:           config.queue,
		keyFunc:         config.keyFunc,
		syncer:          config.syncer,
	}

	return queue, nil
}

// NewQueueInformer returns a new QueueInformer configured with options.
func NewQueueInformer(ctx context.Context, options ...Option) (*QueueInformer, error) {
	// Get default config and apply given options
	config := defaultConfig()
	config.apply(options)
	config.complete()

	return newQueueInformerFromConfig(ctx, config)
}

func newQueueInformerFromConfig(ctx context.Context, config *queueInformerConfig) (*QueueInformer, error) {
	if err := config.validateQueueInformer(); err != nil {
		return nil, err
	}

	// Extract config
	queueInformer := &QueueInformer{
		MetricsProvider: config.provider,
		logger:          config.logger,
		queue:           config.queue,
		indexer:         config.indexer,
		informer:        config.informer,
		keyFunc:         config.keyFunc,
		syncer:          config.syncer,
	}

	// Register event handlers for resource and metrics
	if queueInformer.informer != nil {
		queueInformer.informer.AddEventHandler(queueInformer.resourceHandlers(ctx))
		queueInformer.informer.AddEventHandler(queueInformer.metricHandlers())
	}

	return queueInformer, nil
}

// LegacySyncHandler is a deprecated signature for syncing resources.
type LegacySyncHandler func(obj interface{}) error

// ToSyncer returns the Syncer equivalent of the sync handler.
func (l LegacySyncHandler) ToSyncer() kubestate.Syncer {
	return l.ToSyncerWithDelete(nil)
}

// ToSyncerWithDelete returns the Syncer equivalent of the given sync handler and delete function.
func (l LegacySyncHandler) ToSyncerWithDelete(onDelete func(obj interface{})) kubestate.Syncer {
	var syncer kubestate.SyncFunc = func(ctx context.Context, event kubestate.ResourceEvent) error {
		switch event.Type() {
		case kubestate.ResourceDeleted:
			if onDelete != nil {
				onDelete(event.Resource())
			}
		case kubestate.ResourceAdded:
			// Added and updated are treated the same
			fallthrough
		case kubestate.ResourceUpdated:
			return l(event.Resource())
		default:
			return errors.Errorf("unexpected resource event type: %s", event.Type())
		}

		return nil
	}

	return syncer
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package transformers

import (
	"reflect"
	"testing"

	"github.com/kubernetes-sigs/kustomize/pkg/resmap"
	"github.com/kubernetes-sigs/kustomize/pkg/resource"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestNameReferenceRun(t *testing.T) {
	m := resmap.ResMap{
		resource.NewResId(cmap, "cm1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm1-somehash",
				},
			}),
		resource.NewResId(cmap, "cm2"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm2-somehash",
				},
			}),
		resource.NewResId(secret, "secret1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "Secret",
				"metadata": map[string]interface{}{
					"name": "someprefix-secret1-somehash",
				},
			}),
		resource.NewResId(pvc, "claim1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "PersistentVolumeClaim",
				"metadata": map[string]interface{}{
					"name": "someprefix-claim1",
				},
			}),
		resource.NewResId(deploy, "deploy1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "Deployment",
				"metadata": map[string]interface{}{
					"name": "deploy1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
									"env": []interface{}{
										map[string]interface{}{
											"name": "CM_FOO",
											"valueFrom": map[string]interface{}{
												"configMapKeyRef": map[string]interface{}{
													"name": "cm1",
													"key":  "somekey",
												},
											},
										},
										map[string]interface{}{
											"name": "SECRET_FOO",
											"valueFrom": map[string]interface{}{
												"secretKeyRef": map[string]interface{}{
													"name": "secret1",
													"key":  "somekey",
												},
											},
										},
									},
									"envFrom": []interface{}{
										map[string]interface{}{
											"configMapRef": map[string]interface{}{
												"name": "cm1",
												"key":  "somekey",
											},
										},
										map[string]interface{}{
											"secretRef": map[string]interface{}{
												"name": "secret1",
												"key":  "somekey",
											},
										},
									},
								},
							},
							"imagePullSecrets": []interface{}{
								map[string]interface{}{
									"name": "secret1",
								},
							},
							"volumes": map[string]interface{}{
								"configMap": map[string]interface{}{
									"name": "cm1",
								},
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "cm2",
										},
									},
								},
								"secret": map[string]interface{}{
									"secretName": "secret1",
								},
								"persistentVolumeClaim": map[string]interface{}{
									"claimName": "claim1",
								},
							},
						},
					},
				},
			}),
		resource.NewResId(statefulset, "statefulset1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "StatefulSet",
				"metadata": map[string]interface{}{
					"name": "statefulset1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
								},
							},
							"volumes": map[string]interface{}{
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "cm2",
										},
									},
								},
							},
						},
					},
				},
			}),
	}

	expected := resmap.ResMap{
		resource.NewResId(cmap, "cm1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm1-somehash",
				},
			}),
		resource.NewResId(cmap, "cm2"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm2-somehash",
				},
			}),
		resource.NewResId(secret, "secret1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "Secret",
				"metadata": map[string]interface{}{
					"name": "someprefix-secret1-somehash",
				},
			}),
		resource.NewResId(pvc, "claim1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "PersistentVolumeClaim",
				"metadata": map[string]interface{}{
					"name": "someprefix-claim1",
				},
			}),
		resource.NewResId(deploy, "deploy1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "Deployment",
				"metadata": map[string]interface{}{
					"name": "deploy1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
									"env": []interface{}{
										map[string]interface{}{
											"name": "CM_FOO",
											"valueFrom": map[string]interface{}{
												"configMapKeyRef": map[string]interface{}{
													"name": "someprefix-cm1-somehash",
													"key":  "somekey",
												},
											},
										},
										map[string]interface{}{
											"name": "SECRET_FOO",
											"valueFrom": map[string]interface{}{
												"secretKeyRef": map[string]interface{}{
													"name": "someprefix-secret1-somehash",
													"key":  "somekey",
												},
											},
										},
									},
									"envFrom": []interface{}{
										map[string]interface{}{
											"configMapRef": map[string]interface{}{
												"name": "someprefix-cm1-somehash",
												"key":  "somekey",
											},
										},
										map[string]interface{}{
											"secretRef": map[string]interface{}{
												"name": "someprefix-secret1-somehash",
												"key":  "somekey",
											},
										},
									},
								},
							},
							"imagePullSecrets": []interface{}{
								map[string]interface{}{
									"name": "someprefix-secret1-somehash",
								},
							},
							"volumes": map[string]interface{}{
								"configMap": map[string]interface{}{
									"name": "someprefix-cm1-somehash",
								},
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "someprefix-cm2-somehash",
										},
									},
								},
								"secret": map[string]interface{}{
									"secretName": "someprefix-secret1-somehash",
								},
								"persistentVolumeClaim": map[string]interface{}{
									"claimName": "someprefix-claim1",
								},
							},
						},
					},
				},
			}),
		resource.NewResId(statefulset, "statefulset1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "StatefulSet",
				"metadata": map[string]interface{}{
					"name": "statefulset1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
								},
							},
							"volumes": map[string]interface{}{
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "someprefix-cm2-somehash",
										},
									},
								},
							},
						},
					},
				},
			}),
	}

	nrt, err := NewDefaultingNameReferenceTransformer()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	err = nrt.Transform(m)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !reflect.DeepEqual(m, expected) {
		err = expected.ErrorIfNotEqual(m)
		t.Fatalf("actual doesn't match expected: %v", err)
	}
}

func TestAddNameReferencePathConfigs(t *testing.T) {
	expected := len(defaultNameReferencePathConfigs) + 1

	pathConfigs := []ReferencePathConfig{
		{
			referencedGVK: schema.GroupVersionKind{
				Kind: "KindA",
			},
			pathConfigs: []PathConfig{
				{
					GroupVersionKind: &schema.GroupVersionKind{
						Kind: "KindB",
					},
					Path:               []string{"path", "to", "a", "field"},
					CreateIfNotPresent: false,
				},
			},
		},
	}

	AddNameReferencePathConfigs(pathConfigs)
	if len(defaultNameReferencePathConfigs) != expected {
		t.Fatalf("actual %v doesn't match expected: %v", len(defaultAnnotationsPathConfigs), expected)
	}
}
<commit_msg>fix linter dupl errors<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package transformers

import (
	"reflect"
	"testing"

	"github.com/kubernetes-sigs/kustomize/pkg/resmap"
	"github.com/kubernetes-sigs/kustomize/pkg/resource"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestNameReferenceRun(t *testing.T) {
	m := resmap.ResMap{
		resource.NewResId(cmap, "cm1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm1-somehash",
				},
			}),
		resource.NewResId(cmap, "cm2"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "ConfigMap",
				"metadata": map[string]interface{}{
					"name": "someprefix-cm2-somehash",
				},
			}),
		resource.NewResId(secret, "secret1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "Secret",
				"metadata": map[string]interface{}{
					"name": "someprefix-secret1-somehash",
				},
			}),
		resource.NewResId(pvc, "claim1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"apiVersion": "v1",
				"kind":       "PersistentVolumeClaim",
				"metadata": map[string]interface{}{
					"name": "someprefix-claim1",
				},
			}),
		resource.NewResId(deploy, "deploy1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "Deployment",
				"metadata": map[string]interface{}{
					"name": "deploy1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
									"env": []interface{}{
										map[string]interface{}{
											"name": "CM_FOO",
											"valueFrom": map[string]interface{}{
												"configMapKeyRef": map[string]interface{}{
													"name": "cm1",
													"key":  "somekey",
												},
											},
										},
										map[string]interface{}{
											"name": "SECRET_FOO",
											"valueFrom": map[string]interface{}{
												"secretKeyRef": map[string]interface{}{
													"name": "secret1",
													"key":  "somekey",
												},
											},
										},
									},
									"envFrom": []interface{}{
										map[string]interface{}{
											"configMapRef": map[string]interface{}{
												"name": "cm1",
												"key":  "somekey",
											},
										},
										map[string]interface{}{
											"secretRef": map[string]interface{}{
												"name": "secret1",
												"key":  "somekey",
											},
										},
									},
								},
							},
							"imagePullSecrets": []interface{}{
								map[string]interface{}{
									"name": "secret1",
								},
							},
							"volumes": map[string]interface{}{
								"configMap": map[string]interface{}{
									"name": "cm1",
								},
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "cm2",
										},
									},
								},
								"secret": map[string]interface{}{
									"secretName": "secret1",
								},
								"persistentVolumeClaim": map[string]interface{}{
									"claimName": "claim1",
								},
							},
						},
					},
				},
			}),
		resource.NewResId(statefulset, "statefulset1"): resource.NewResourceFromMap(
			map[string]interface{}{
				"group":      "apps",
				"apiVersion": "v1",
				"kind":       "StatefulSet",
				"metadata": map[string]interface{}{
					"name": "statefulset1",
				},
				"spec": map[string]interface{}{
					"template": map[string]interface{}{
						"spec": map[string]interface{}{
							"containers": []interface{}{
								map[string]interface{}{
									"name":  "nginx",
									"image": "nginx:1.7.9",
								},
							},
							"volumes": map[string]interface{}{
								"projected": map[string]interface{}{
									"sources": map[string]interface{}{
										"configMap": map[string]interface{}{
											"name": "cm2",
										},
									},
								},
							},
						},
					},
				},
			}),
	}

	expected := resmap.ResMap{}
	for k, v := range m {
		expected[k] = v
	}

	expected[resource.NewResId(deploy, "deploy1")] = resource.NewResourceFromMap(
		map[string]interface{}{
			"group":      "apps",
			"apiVersion": "v1",
			"kind":       "Deployment",
			"metadata": map[string]interface{}{
				"name": "deploy1",
			},
			"spec": map[string]interface{}{
				"template": map[string]interface{}{
					"spec": map[string]interface{}{
						"containers": []interface{}{
							map[string]interface{}{
								"name":  "nginx",
								"image": "nginx:1.7.9",
								"env": []interface{}{
									map[string]interface{}{
										"name": "CM_FOO",
										"valueFrom": map[string]interface{}{
											"configMapKeyRef": map[string]interface{}{
												"name": "someprefix-cm1-somehash",
												"key":  "somekey",
											},
										},
									},
									map[string]interface{}{
										"name": "SECRET_FOO",
										"valueFrom": map[string]interface{}{
											"secretKeyRef": map[string]interface{}{
												"name": "someprefix-secret1-somehash",
												"key":  "somekey",
											},
										},
									},
								},
								"envFrom": []interface{}{
									map[string]interface{}{
										"configMapRef": map[string]interface{}{
											"name": "someprefix-cm1-somehash",
											"key":  "somekey",
										},
									},
									map[string]interface{}{
										"secretRef": map[string]interface{}{
											"name": "someprefix-secret1-somehash",
											"key":  "somekey",
										},
									},
								},
							},
						},
						"imagePullSecrets": []interface{}{
							map[string]interface{}{
								"name": "someprefix-secret1-somehash",
							},
						},
						"volumes": map[string]interface{}{
							"configMap": map[string]interface{}{
								"name": "someprefix-cm1-somehash",
							},
							"projected": map[string]interface{}{
								"sources": map[string]interface{}{
									"configMap": map[string]interface{}{
										"name": "someprefix-cm2-somehash",
									},
								},
							},
							"secret": map[string]interface{}{
								"secretName": "someprefix-secret1-somehash",
							},
							"persistentVolumeClaim": map[string]interface{}{
								"claimName": "someprefix-claim1",
							},
						},
					},
				},
			},
		})
	expected[resource.NewResId(statefulset, "statefulset1")] = resource.NewResourceFromMap(
		map[string]interface{}{
			"group":      "apps",
			"apiVersion": "v1",
			"kind":       "StatefulSet",
			"metadata": map[string]interface{}{
				"name": "statefulset1",
			},
			"spec": map[string]interface{}{
				"template": map[string]interface{}{
					"spec": map[string]interface{}{
						"containers": []interface{}{
							map[string]interface{}{
								"name":  "nginx",
								"image": "nginx:1.7.9",
							},
						},
						"volumes": map[string]interface{}{
							"projected": map[string]interface{}{
								"sources": map[string]interface{}{
									"configMap": map[string]interface{}{
										"name": "someprefix-cm2-somehash",
									},
								},
							},
						},
					},
				},
			},
		})

	nrt, err := NewDefaultingNameReferenceTransformer()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	err = nrt.Transform(m)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !reflect.DeepEqual(m, expected) {
		err = expected.ErrorIfNotEqual(m)
		t.Fatalf("actual doesn't match expected: %v", err)
	}
}

func TestAddNameReferencePathConfigs(t *testing.T) {
	expected := len(defaultNameReferencePathConfigs) + 1

	pathConfigs := []ReferencePathConfig{
		{
			referencedGVK: schema.GroupVersionKind{
				Kind: "KindA",
			},
			pathConfigs: []PathConfig{
				{
					GroupVersionKind: &schema.GroupVersionKind{
						Kind: "KindB",
					},
					Path:               []string{"path", "to", "a", "field"},
					CreateIfNotPresent: false,
				},
			},
		},
	}

	AddNameReferencePathConfigs(pathConfigs)
	if len(defaultNameReferencePathConfigs) != expected {
		t.Fatalf("actual %v doesn't match expected: %v", len(defaultAnnotationsPathConfigs), expected)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-sql"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/postgres"
)

type ProviderRepo struct {
	db *postgres.DB
}

func NewProviderRepo(db *postgres.DB) *ProviderRepo {
	return &ProviderRepo{db}
}

func (r *ProviderRepo) Add(data interface{}) error {
	p := data.(*ct.Provider)
	if p.Name == "" {
		return errors.New("controller: name must not be blank")
	}
	if p.URL == "" {
		return errors.New("controler: url must not be blank")
	}
	// TODO: validate url
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	err = tx.QueryRow("INSERT INTO providers (name, url) VALUES ($1, $2) RETURNING provider_id, created_at, updated_at", p.Name, p.URL).Scan(&p.ID, &p.CreatedAt, &p.UpdatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}
	p.ID = postgres.CleanUUID(p.ID)
	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   p.ID,
		ObjectType: ct.EventTypeProvider,
	}, p); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}

func scanProvider(s postgres.Scanner) (*ct.Provider, error) {
	p := &ct.Provider{}
	err := s.Scan(&p.ID, &p.Name, &p.URL, &p.CreatedAt, &p.UpdatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	p.ID = postgres.CleanUUID(p.ID)
	return p, err
}

func (r *ProviderRepo) Get(id string) (interface{}, error) {
	var row postgres.Scanner
	query := "SELECT provider_id, name, url, created_at, updated_at FROM providers WHERE deleted_at IS NULL AND "
	if idPattern.MatchString(id) {
		row = r.db.QueryRow(query+"(provider_id = $1 OR name = $2) LIMIT 1", id, id)
	} else {
		row = r.db.QueryRow(query+"name = $1", id)
	}
	return scanProvider(row)
}

func (r *ProviderRepo) List() (interface{}, error) {
	rows, err := r.db.Query("SELECT provider_id, name, url, created_at, updated_at FROM providers WHERE deleted_at IS NULL ORDER BY created_at DESC")
	if err != nil {
		return nil, err
	}
	providers := []*ct.Provider{}
	for rows.Next() {
		provider, err := scanProvider(rows)
		if err != nil {
			rows.Close()
			return nil, err
		}
		providers = append(providers, provider)
	}
	return providers, rows.Err()
}
<commit_msg>controller: Fix typo controler -> controller<commit_after>package main

import (
	"errors"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-sql"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/postgres"
)

type ProviderRepo struct {
	db *postgres.DB
}

func NewProviderRepo(db *postgres.DB) *ProviderRepo {
	return &ProviderRepo{db}
}

func (r *ProviderRepo) Add(data interface{}) error {
	p := data.(*ct.Provider)
	if p.Name == "" {
		return errors.New("controller: name must not be blank")
	}
	if p.URL == "" {
		return errors.New("controller: url must not be blank")
	}
	// TODO: validate url
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	err = tx.QueryRow("INSERT INTO providers (name, url) VALUES ($1, $2) RETURNING provider_id, created_at, updated_at", p.Name, p.URL).Scan(&p.ID, &p.CreatedAt, &p.UpdatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}
	p.ID = postgres.CleanUUID(p.ID)
	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   p.ID,
		ObjectType: ct.EventTypeProvider,
	}, p); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}

func scanProvider(s postgres.Scanner) (*ct.Provider, error) {
	p := &ct.Provider{}
	err := s.Scan(&p.ID, &p.Name, &p.URL, &p.CreatedAt, &p.UpdatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	p.ID = postgres.CleanUUID(p.ID)
	return p, err
}

func (r *ProviderRepo) Get(id string) (interface{}, error) {
	var row postgres.Scanner
	query := "SELECT provider_id, name, url, created_at, updated_at FROM providers WHERE deleted_at IS NULL AND "
	if idPattern.MatchString(id) {
		row = r.db.QueryRow(query+"(provider_id = $1 OR name = $2) LIMIT 1", id, id)
	} else {
		row = r.db.QueryRow(query+"name = $1", id)
	}
	return scanProvider(row)
}

func (r *ProviderRepo) List() (interface{}, error) {
	rows, err := r.db.Query("SELECT provider_id, name, url, created_at, updated_at FROM providers WHERE deleted_at IS NULL ORDER BY created_at DESC")
	if err != nil {
		return nil, err
	}
	providers := []*ct.Provider{}
	for rows.Next() {
		provider, err := scanProvider(rows)
		if err != nil {
			rows.Close()
			return nil, err
		}
		providers = append(providers, provider)
	}
	return providers, rows.Err()
}
<|endoftext|>


<commit_before>package omniscient

import (
	"errors"
	"fmt"
	"net/http"

	"gopkg.in/labstack/echo.v1"
	"gopkg.in/labstack/echo.v1/middleware"

	el "github.com/deoxxa/echo-logrus"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	defaultNoteRepository NoteRepository
	defaultHealth         *Health

	revision string
)

func init() {
	dnr, err := NewRedisNoteRepository()
	if err != nil {
		panic(fmt.Sprintf("unable to create default note repository: %v", err))
	}

	defaultNoteRepository = dnr

	health, err := NewHealth()
	if err != nil {
		panic(fmt.Sprintf("unable to create default health check service: %v", err))
	}

	defaultHealth = health
}

// App is the application.
type App struct {
	Mux      *echo.Echo
	noteRepo NoteRepository
	health   *Health
}

// AppOption is an option for configuring App.
type AppOption func(*App) error

// NewApp creates an instance of App.
func NewApp(opts ...AppOption) (*App, error) {
	e := echo.New()

	a := &App{
		Mux:      e,
		noteRepo: defaultNoteRepository,
		health:   defaultHealth,
	}

	for _, opt := range opts {
		if err := opt(a); err != nil {
			return nil, err
		}
	}

	if err := initMetrics(); err != nil {
		return nil, err
	}

	// middleware
	e.Use(el.New())
	e.Use(HitCounter())
	e.Use(middleware.Recover())

	// routes
	e.Post("/notes", a.createNote())
	e.Get("/notes", a.retrieveNotes())
	e.Get("/notes/:id", a.retrieveNote())
	e.Put("/notes/:id", a.updateNote())
	e.Delete("/notes/:id", a.deleteNote())

	e.Get("/healthz", a.healthz())
	e.Get("/app/info", a.appInfo())

	e.Get("/metrics", prometheus.Handler())

	if a.health == nil {
		return nil, errors.New("no health checker")
	}

	err := a.health.Start()
	if err != nil {
		return nil, fmt.Errorf("unable to start health checker: %v", err)
	}

	return a, nil
}

// AppNoteRepository sets the note repository for App.
func AppNoteRepository(nr NoteRepository) AppOption {
	return func(a *App) error {
		a.noteRepo = nr
		return nil
	}
}

// AppHealth sets the app health checker option.
func AppHealth(h *Health) AppOption {
	return func(a *App) error {
		a.health = h
		return nil
	}
}

type createNoteReq struct {
	Content string `json:"content"`
}

type updateNoteReq struct {
	Content string `json:"content"`
}

func (a *App) createNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		cnr := &createNoteReq{}
		if err := c.Bind(cnr); err != nil {
			return err
		}

		note, err := a.noteRepo.Create(cnr.Content)
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to create note",
			}
			return c.JSON(http.StatusInternalServerError, msg)
		}

		return c.JSON(http.StatusCreated, note)
	}
}

func (a *App) retrieveNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		id := c.Param("id")
		note, err := a.noteRepo.Retrieve(id)
		if err != nil {
			msg := map[string]interface{}{
				"error": "note not found",
			}
			return c.JSON(http.StatusNotFound, msg)
		}

		return c.JSON(http.StatusOK, note)
	}
}

func (a *App) retrieveNotes() echo.HandlerFunc {
	return func(c *echo.Context) error {
		notes, err := a.noteRepo.List()
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to retrieve notes",
			}
			return c.JSON(http.StatusInternalServerError, msg)
		}

		return c.JSON(http.StatusOK, notes)
	}
}

func (a *App) updateNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		id := c.Param("id")

		cnr := &createNoteReq{}
		if err := c.Bind(cnr); err != nil {
			return err
		}

		note, err := a.noteRepo.Update(id, cnr.Content)
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to update note",
			}
			return c.JSON(http.StatusNotFound, msg)
		}

		return c.JSON(http.StatusOK, note)
	}
}

func (a *App) deleteNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		id := c.Param("id")

		err := a.noteRepo.Delete(id)
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to delete note",
			}
			return c.JSON(http.StatusBadRequest, msg)
		}

		return c.NoContent(http.StatusNoContent)
	}
}

func (a *App) healthz() echo.HandlerFunc {
	return func(c *echo.Context) error {
		if a.health.IsOK() {
			return c.String(http.StatusOK, "OK")
		}

		return c.NoContent(http.StatusInternalServerError)
	}
}

type appInfo struct {
	Revision string `json:"revision"`
}

func (a *App) appInfo() echo.HandlerFunc {
	return func(c *echo.Context) error {
		ai := appInfo{
			Revision: revision,
		}
		if ai.Revision == "" {
			ai.Revision = "dev"
		}

		return c.JSON(http.StatusOK, ai)
	}
}
<commit_msg>remove delete action<commit_after>package omniscient

import (
	"errors"
	"fmt"
	"net/http"

	"gopkg.in/labstack/echo.v1"
	"gopkg.in/labstack/echo.v1/middleware"

	el "github.com/deoxxa/echo-logrus"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	defaultNoteRepository NoteRepository
	defaultHealth         *Health

	revision string
)

func init() {
	dnr, err := NewRedisNoteRepository()
	if err != nil {
		panic(fmt.Sprintf("unable to create default note repository: %v", err))
	}

	defaultNoteRepository = dnr

	health, err := NewHealth()
	if err != nil {
		panic(fmt.Sprintf("unable to create default health check service: %v", err))
	}

	defaultHealth = health
}

// App is the application.
type App struct {
	Mux      *echo.Echo
	noteRepo NoteRepository
	health   *Health
}

// AppOption is an option for configuring App.
type AppOption func(*App) error

// NewApp creates an instance of App.
func NewApp(opts ...AppOption) (*App, error) {
	e := echo.New()

	a := &App{
		Mux:      e,
		noteRepo: defaultNoteRepository,
		health:   defaultHealth,
	}

	for _, opt := range opts {
		if err := opt(a); err != nil {
			return nil, err
		}
	}

	if err := initMetrics(); err != nil {
		return nil, err
	}

	// middleware
	e.Use(el.New())
	e.Use(HitCounter())
	e.Use(middleware.Recover())

	// routes
	e.Post("/notes", a.createNote())
	e.Get("/notes", a.retrieveNotes())
	e.Get("/notes/:id", a.retrieveNote())
	e.Put("/notes/:id", a.updateNote())
	// e.Delete("/notes/:id", a.deleteNote())

	e.Get("/healthz", a.healthz())
	e.Get("/app/info", a.appInfo())

	e.Get("/metrics", prometheus.Handler())

	if a.health == nil {
		return nil, errors.New("no health checker")
	}

	err := a.health.Start()
	if err != nil {
		return nil, fmt.Errorf("unable to start health checker: %v", err)
	}

	return a, nil
}

// AppNoteRepository sets the note repository for App.
func AppNoteRepository(nr NoteRepository) AppOption {
	return func(a *App) error {
		a.noteRepo = nr
		return nil
	}
}

// AppHealth sets the app health checker option.
func AppHealth(h *Health) AppOption {
	return func(a *App) error {
		a.health = h
		return nil
	}
}

type createNoteReq struct {
	Content string `json:"content"`
}

type updateNoteReq struct {
	Content string `json:"content"`
}

func (a *App) createNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		cnr := &createNoteReq{}
		if err := c.Bind(cnr); err != nil {
			return err
		}

		note, err := a.noteRepo.Create(cnr.Content)
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to create note",
			}
			return c.JSON(http.StatusInternalServerError, msg)
		}

		return c.JSON(http.StatusCreated, note)
	}
}

func (a *App) retrieveNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		id := c.Param("id")
		note, err := a.noteRepo.Retrieve(id)
		if err != nil {
			msg := map[string]interface{}{
				"error": "note not found",
			}
			return c.JSON(http.StatusNotFound, msg)
		}

		return c.JSON(http.StatusOK, note)
	}
}

func (a *App) retrieveNotes() echo.HandlerFunc {
	return func(c *echo.Context) error {
		notes, err := a.noteRepo.List()
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to retrieve notes",
			}
			return c.JSON(http.StatusInternalServerError, msg)
		}

		return c.JSON(http.StatusOK, notes)
	}
}

func (a *App) updateNote() echo.HandlerFunc {
	return func(c *echo.Context) error {
		id := c.Param("id")

		cnr := &createNoteReq{}
		if err := c.Bind(cnr); err != nil {
			return err
		}

		note, err := a.noteRepo.Update(id, cnr.Content)
		if err != nil {
			msg := map[string]interface{}{
				"error": "unable to update note",
			}
			return c.JSON(http.StatusNotFound, msg)
		}

		return c.JSON(http.StatusOK, note)
	}
}

// func (a *App) deleteNote() echo.HandlerFunc {
// 	return func(c *echo.Context) error {
// 		id := c.Param("id")

// 		err := a.noteRepo.Delete(id)
// 		if err != nil {
// 			msg := map[string]interface{}{
// 				"error": "unable to delete note",
// 			}
// 			return c.JSON(http.StatusBadRequest, msg)
// 		}

// 		return c.NoContent(http.StatusNoContent)
// 	}
// }

func (a *App) healthz() echo.HandlerFunc {
	return func(c *echo.Context) error {
		if a.health.IsOK() {
			return c.String(http.StatusOK, "OK")
		}

		return c.NoContent(http.StatusInternalServerError)
	}
}

type appInfo struct {
	Revision string `json:"revision"`
}

func (a *App) appInfo() echo.HandlerFunc {
	return func(c *echo.Context) error {
		ai := appInfo{
			Revision: revision,
		}
		if ai.Revision == "" {
			ai.Revision = "dev"
		}

		return c.JSON(http.StatusOK, ai)
	}
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"sync"
	"time"

	rps "veyron/examples/rockpaperscissors"

	"veyron2/rt"
	"veyron2/vlog"
)

type Counter struct {
	value int64
	// TODO(rthellend): Figure out why sync/atomic doesn't work properly on armv6l.
	lock sync.Mutex
}

func (c *Counter) Add(delta int64) int64 {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.value += delta
	return c.value
}

func (c *Counter) Value() int64 {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.value
}

// FindJudge returns a random rock-paper-scissors judge from the mount table.
func FindJudge() (string, error) {
	judges, err := findAll("judge")
	if err != nil {
		return "", err
	}
	if len(judges) > 0 {
		return judges[rand.Intn(len(judges))], nil
	}
	return "", errors.New("no judges")
}

// FindPlayer returns a random rock-paper-scissors player from the mount table.
func FindPlayer() (string, error) {
	players, err := findAll("player")
	if err != nil {
		return "", err
	}
	if len(players) > 0 {
		return players[rand.Intn(len(players))], nil
	}
	return "", errors.New("no players")
}

// FindScoreKeepers returns all the rock-paper-scissors score keepers from the
// mount table.
func FindScoreKeepers() ([]string, error) {
	sKeepers, err := findAll("scorekeeper")
	if err != nil {
		return nil, err
	}
	return sKeepers, nil
}

func findAll(t string) ([]string, error) {
	start := time.Now()
	ns := rt.R().Namespace()
	c, err := ns.Glob(rt.R().TODOContext(), "rps/"+t+"/*")
	if err != nil {
		vlog.Infof("mt.Glob failed: %v", err)
		return nil, err
	}
	var servers []string
	for e := range c {
		servers = append(servers, e.Name)
	}
	vlog.VI(1).Infof("findAll(%q) elapsed: %s", t, time.Now().Sub(start))
	return servers, nil
}

func FormatScoreCard(score rps.ScoreCard) string {
	buf := bytes.NewBufferString("")
	var gameType string
	switch score.Opts.GameType {
	case rps.Classic:
		gameType = "Classic"
	case rps.LizardSpock:
		gameType = "LizardSpock"
	default:
		gameType = "Unknown"
	}
	fmt.Fprintf(buf, "Game Type: %s\n", gameType)
	fmt.Fprintf(buf, "Number of rounds: %d\n", score.Opts.NumRounds)
	fmt.Fprintf(buf, "Judge: %s\n", score.Judge)
	fmt.Fprintf(buf, "Player 1: %s\n", score.Players[0])
	fmt.Fprintf(buf, "Player 2: %s\n", score.Players[1])
	for i, r := range score.Rounds {
		roundOffset := time.Duration(r.StartTimeNS - score.StartTimeNS)
		roundTime := time.Duration(r.EndTimeNS - r.StartTimeNS)
		fmt.Fprintf(buf, "Round %2d: Player 1 played %-10q. Player 2 played %-10q. Winner: %d %-28s [%-10s/%-10s]\n",
			i+1, r.Moves[0], r.Moves[1], r.Winner, r.Comment, roundOffset, roundTime)
	}
	fmt.Fprintf(buf, "Winner: %d\n", score.Winner)
	fmt.Fprintf(buf, "Time: %s\n", time.Duration(score.EndTimeNS-score.StartTimeNS))
	return buf.String()
}
<commit_msg>veyron/examples/rockpaperscissors/common: Remove TODO.<commit_after>package common

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"sync"
	"time"

	rps "veyron/examples/rockpaperscissors"

	"veyron2/rt"
	"veyron2/vlog"
)

type Counter struct {
	value int64
	lock  sync.Mutex
}

func (c *Counter) Add(delta int64) int64 {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.value += delta
	return c.value
}

func (c *Counter) Value() int64 {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.value
}

// FindJudge returns a random rock-paper-scissors judge from the mount table.
func FindJudge() (string, error) {
	judges, err := findAll("judge")
	if err != nil {
		return "", err
	}
	if len(judges) > 0 {
		return judges[rand.Intn(len(judges))], nil
	}
	return "", errors.New("no judges")
}

// FindPlayer returns a random rock-paper-scissors player from the mount table.
func FindPlayer() (string, error) {
	players, err := findAll("player")
	if err != nil {
		return "", err
	}
	if len(players) > 0 {
		return players[rand.Intn(len(players))], nil
	}
	return "", errors.New("no players")
}

// FindScoreKeepers returns all the rock-paper-scissors score keepers from the
// mount table.
func FindScoreKeepers() ([]string, error) {
	sKeepers, err := findAll("scorekeeper")
	if err != nil {
		return nil, err
	}
	return sKeepers, nil
}

func findAll(t string) ([]string, error) {
	start := time.Now()
	ns := rt.R().Namespace()
	c, err := ns.Glob(rt.R().TODOContext(), "rps/"+t+"/*")
	if err != nil {
		vlog.Infof("mt.Glob failed: %v", err)
		return nil, err
	}
	var servers []string
	for e := range c {
		servers = append(servers, e.Name)
	}
	vlog.VI(1).Infof("findAll(%q) elapsed: %s", t, time.Now().Sub(start))
	return servers, nil
}

func FormatScoreCard(score rps.ScoreCard) string {
	buf := bytes.NewBufferString("")
	var gameType string
	switch score.Opts.GameType {
	case rps.Classic:
		gameType = "Classic"
	case rps.LizardSpock:
		gameType = "LizardSpock"
	default:
		gameType = "Unknown"
	}
	fmt.Fprintf(buf, "Game Type: %s\n", gameType)
	fmt.Fprintf(buf, "Number of rounds: %d\n", score.Opts.NumRounds)
	fmt.Fprintf(buf, "Judge: %s\n", score.Judge)
	fmt.Fprintf(buf, "Player 1: %s\n", score.Players[0])
	fmt.Fprintf(buf, "Player 2: %s\n", score.Players[1])
	for i, r := range score.Rounds {
		roundOffset := time.Duration(r.StartTimeNS - score.StartTimeNS)
		roundTime := time.Duration(r.EndTimeNS - r.StartTimeNS)
		fmt.Fprintf(buf, "Round %2d: Player 1 played %-10q. Player 2 played %-10q. Winner: %d %-28s [%-10s/%-10s]\n",
			i+1, r.Moves[0], r.Moves[1], r.Winner, r.Comment, roundOffset, roundTime)
	}
	fmt.Fprintf(buf, "Winner: %d\n", score.Winner)
	fmt.Fprintf(buf, "Time: %s\n", time.Duration(score.EndTimeNS-score.StartTimeNS))
	return buf.String()
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/astaxie/beego"
	"solo-ci/models"
	"solo-ci/utils"
	"fmt"
	"github.com/astaxie/beego/orm"
	"io/ioutil"
	"encoding/json"
)

type ProjectController struct {
	beego.Controller
}

func (obj *ProjectController) Add() {
	project := new(models.Project)
	if err := obj.ParseForm(project); err != nil {
		obj.Data["json"] = utils.GetClientErrRender()
	} else {
		id, err := project.Add()
		if err != nil {
			obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
		} else {
			obj.Data["json"] = utils.GetSuccessRender(map[string]string{"project_id":id})
		}
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Delete() {
	projectId := obj.Ctx.Input.Param(":project_id")
	fmt.Println(projectId)
	project := &models.Project{ProjectId:projectId}
	err := project.Delete()
	if err != nil {
		obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
	} else {
		obj.Data["json"] = utils.GetSuccessRender(nil)
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Update() {
	projectId := obj.Ctx.Input.Param(":project_id")
	project := &models.Project{ProjectId:projectId}
	if err := obj.ParseForm(project); err != nil {
		obj.Data["json"] = utils.GetClientErrRender()
	} else {
		err := project.Update()
		if err != nil {
			obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
		} else {
			obj.Data["json"] = utils.GetSuccessRender(nil)
		}
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Get() {
	projectId := obj.Ctx.Input.Param(":project_id")
	project := &models.Project{ProjectId:projectId}
	err := project.Get()
	if err != nil {
		obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
	} else {
		obj.Data["json"] = utils.GetSuccessRender(project)
	}
	obj.ServeJSON()
}

func (obj *ProjectController) GetList() {
	page, _ := obj.GetInt("page", 0)
	pageSize, _ := obj.GetInt("pageSize", 20)
	obj.Data["json"] = utils.GetSuccessRender(models.GetList(page, pageSize))
	obj.ServeJSON()
}

func (obj *ProjectController) WebHook() {
	//告诉git 接受成功
	obj.Data["json"] = utils.GetSuccessRender(nil)
	obj.ServeJSON()
	//执行脚本
	project := new(models.Project)
	project.ProjectId = obj.Ctx.Input.Param(":project_id")
	o := orm.NewOrm()
	if err := o.Read(project, "project_id"); err != nil && project.Name == "" {
		beego.Info("This object", project.ProjectId, "not exist")
		return
	}
	switch project.Type {
	case "gitlab":
		gitlabHook := new(models.GitlabHook)
		bodyMsg, _ := ioutil.ReadAll(obj.Ctx.Request.Body)
		beego.Info(string(bodyMsg))
		json.Unmarshal(bodyMsg, gitlabHook)
		if gitlabHook.Ref != "refs/heads/" + project.Branch {
			beego.Info("Branch not same")
			return
		}
		if project.SecretToken != "" && obj.Ctx.Request.Header.Get("X-Gitlab-Token") != project.SecretToken {
			beego.Info(project.ProjectId, "Secret token error")
			return
		}
		go models.NewBuild(project)
	case "github":
		githubHook := new(models.GithubHook)
		bodyMsg, _ := ioutil.ReadAll(obj.Ctx.Request.Body)
		beego.Info(string(bodyMsg))
		json.Unmarshal(bodyMsg, githubHook)
		if githubHook.Ref != "refs/heads/" + project.Branch {
			beego.Info("Branch not same")
			return
		}
		if project.SecretToken != "" && obj.Ctx.Request.Header.Get("X-Hub-Signature") != project.SecretToken {
			beego.Info(project.ProjectId, "Secret token error")
			return
		}
		go models.NewBuild(project)
	default:
		beego.Info("Don't have this type")
	}
}<commit_msg>修改hook<commit_after>package controllers

import (
	"github.com/astaxie/beego"
	"solo-ci/models"
	"solo-ci/utils"
	"fmt"
	"github.com/astaxie/beego/orm"
	"io/ioutil"
	"encoding/json"
)

type ProjectController struct {
	beego.Controller
}

func (obj *ProjectController) Add() {
	project := new(models.Project)
	if err := obj.ParseForm(project); err != nil {
		obj.Data["json"] = utils.GetClientErrRender()
	} else {
		id, err := project.Add()
		if err != nil {
			obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
		} else {
			obj.Data["json"] = utils.GetSuccessRender(map[string]string{"project_id":id})
		}
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Delete() {
	projectId := obj.Ctx.Input.Param(":project_id")
	fmt.Println(projectId)
	project := &models.Project{ProjectId:projectId}
	err := project.Delete()
	if err != nil {
		obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
	} else {
		obj.Data["json"] = utils.GetSuccessRender(nil)
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Update() {
	projectId := obj.Ctx.Input.Param(":project_id")
	project := &models.Project{ProjectId:projectId}
	if err := obj.ParseForm(project); err != nil {
		obj.Data["json"] = utils.GetClientErrRender()
	} else {
		err := project.Update()
		if err != nil {
			obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
		} else {
			obj.Data["json"] = utils.GetSuccessRender(nil)
		}
	}
	obj.ServeJSON()
}

func (obj *ProjectController) Get() {
	projectId := obj.Ctx.Input.Param(":project_id")
	project := &models.Project{ProjectId:projectId}
	err := project.Get()
	if err != nil {
		obj.Data["json"] = utils.GetErrorRender(err.Error(), 400)
	} else {
		obj.Data["json"] = utils.GetSuccessRender(project)
	}
	obj.ServeJSON()
}

func (obj *ProjectController) GetList() {
	page, _ := obj.GetInt("page", 0)
	pageSize, _ := obj.GetInt("pageSize", 20)
	obj.Data["json"] = utils.GetSuccessRender(models.GetList(page, pageSize))
	obj.ServeJSON()
}

func (obj *ProjectController) WebHook() {
	//告诉git 接受成功
	obj.Data["json"] = utils.GetSuccessRender(nil)
	obj.ServeJSON()
	//执行脚本
	project := new(models.Project)
	project.ProjectId = obj.Ctx.Input.Param(":project_id")
	o := orm.NewOrm()
	if err := o.Read(project, "project_id"); err != nil && project.Name == "" {
		beego.Info("This object", project.ProjectId, "not exist")
		return
	}
	switch project.Type {
	case "gitlab":
		gitlabHook := new(models.GitlabHook)
		bodyMsg, _ := ioutil.ReadAll(obj.Ctx.Request.Body)
		json.Unmarshal(bodyMsg, gitlabHook)
		if gitlabHook.Ref != "refs/heads/" + project.Branch {
			beego.Info("Branch not same")
			return
		}
		if project.SecretToken != "" && obj.Ctx.Request.Header.Get("X-Gitlab-Token") != project.SecretToken {
			beego.Info(project.ProjectId, "Secret token error")
			return
		}
		go models.NewBuild(project)
	case "github":
		githubHook := new(models.GithubHook)
		bodyMsg, _ := ioutil.ReadAll(obj.Ctx.Request.Body)
		json.Unmarshal(bodyMsg, githubHook)
		beego.Info(githubHook.Ref)
		if githubHook.Ref != "refs/heads/" + project.Branch {
			beego.Info("Branch not same")
			return
		}
		if project.SecretToken != "" && obj.Ctx.Request.Header.Get("X-Hub-Signature") != project.SecretToken {
			beego.Info(project.ProjectId, "Secret token error")
			return
		}
		go models.NewBuild(project)
	default:
		beego.Info("Don't have this type")
	}
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"packaging/math"
)

func main() {
	xs := []float64{1, 2, 3, 4}
	avg := math.Average(xs)
	fmt.Println(avg)
}
<commit_msg>Add alias in packaging example<commit_after>package main

import (
	"fmt"
	m "packaging/math"
)

func main() {
	xs := []float64{1, 2, 3, 4}
	avg := m.Average(xs)
	fmt.Println(avg)
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "fmt"
    "log"
    "net"
    "strings"
    //"io/ioutil"
    "mob/proto"
    //"encoding/gob"
    //"github.com/tcolgate/mp3"
    //"time"
    "github.com/cenkalti/rpc2"
)

// IP -> array of songs
var peerMap map[string][]string
// TODO: var liveMap map[string]time.Time
var songQueue []string

var currSong string
var currentlyplaying bool  // Is a song playing on clients

// TODO: when all clients in peerMap make rpc to say that they are done with the song
// notify the next set of seeders to begin seeding
func main() {
    peerMap   = make(map[string][]string)
    songQueue = make([]string, 0)
    currentlyplaying = false

    srv := rpc2.NewServer()

    srv.Handle("join", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        // TODO: set ip to be from client object?
        peerMap[args.Ip] = args.List
        //fmt.Println("Handling join ...")
        return nil
    })

    srv.Handle("list", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        reply.Res = getSongList()
        fmt.Println(getSongList())
        //fmt.Println("Handling list ...")
        return nil
    })

    srv.Handle("play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
      fmt.Println("Got request to play " + args.Arg)
        for _, song := range getSongList() {
            if args.Arg == song {
                fmt.Println("Enqueued " + song)
                songQueue = append(songQueue, args.Arg)
                break
            }
        }

        // TODO: if no song is playing currently, reply to clients with the song to start seeding

        return nil
    })

    srv.Handle("leave", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        delete(peerMap, args.Ip)
        return nil
    })

    srv.Handle("peers", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        //fmt.Println("Handling peers ...")
        keys := make([]string, 0, len(peerMap))
        for k := range peerMap {
            keys = append(keys, k)
        }
        reply.Res = keys
        return nil
    })

    srv.Handle("ping", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        //fmt.Println("Handling ping from " + args.Ip)

        /*if currSong == "" && len(songQueue) > 0 {
            nextSong := songQueue[0]
            for _, song := range peerMap[args.Ip] {
                if song == nextSong {
                    currSong  = nextSong
                    songQueue = append(songQueue[:0], songQueue[1:]...)
                    reply.Res = song
                    break
                }
            }
        }*/

        // If no song is currently playing and there is a song ready to be seeded
        // TODO make currentlyplaying a global boolean and toggle it on and off in tracker's
        // play and done handlers respectively
        if !currentlyplaying && len(songQueue) > 0 {

          nextSong := songQueue[0]
          for _, song := range peerMap[args.Ip] {
              if song == nextSong {
                  currSong  = nextSong
                  fmt.Println("Contacting seeders to seed to peers ...")
                  client.Call("seedToPeers", proto.SeedToPeersPacket{currSong}, nil)
                  reply.Res = song
                  return nil
              }
          }
            fmt.Println("Contacting peers to begin listening for seeders ...")
          // Song not found, this peer needs to listen for seeders
          client.Call("listenForSeeders", proto.ListenForSeedersPacket{}, nil)
        }
        // TODO update livemap
        return nil
    })

    srv.Handle("done", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        // TODO: rpc for client to say song is done playing
        // currSong = ""
        return nil
    })

    ln, err := net.Listen("tcp", ":" + os.Args[1])
    if err != nil {
        log.Println(err)
    }

    fmt.Println("mob tracker listening on port: " + os.Args[1] + " ...")

    for {
        srv.Accept(ln)
    }
}

// TODO: maybe return unique song list
func getSongList() ([]string) {
    var songs []string

    keys := make([]string, 0, len(peerMap))
    for k := range peerMap {
        keys = append(keys, k)
    }

    for i := 0; i < len(keys); i++ {
        songs = append(songs, peerMap[keys[i]]...)
    }

    return songs
}
<commit_msg>comment out pkg<commit_after>package main

import (
    "os"
    "fmt"
    "log"
    "net"
    //"strings"
    //"io/ioutil"
    "mob/proto"
    //"encoding/gob"
    //"github.com/tcolgate/mp3"
    //"time"
    "github.com/cenkalti/rpc2"
)

// IP -> array of songs
var peerMap map[string][]string
// TODO: var liveMap map[string]time.Time
var songQueue []string

var currSong string
var currentlyplaying bool  // Is a song playing on clients

// TODO: when all clients in peerMap make rpc to say that they are done with the song
// notify the next set of seeders to begin seeding
func main() {
    peerMap   = make(map[string][]string)
    songQueue = make([]string, 0)
    currentlyplaying = false

    srv := rpc2.NewServer()

    srv.Handle("join", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        // TODO: set ip to be from client object?
        peerMap[args.Ip] = args.List
        //fmt.Println("Handling join ...")
        return nil
    })

    srv.Handle("list", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        reply.Res = getSongList()
        fmt.Println(getSongList())
        //fmt.Println("Handling list ...")
        return nil
    })

    srv.Handle("play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
      fmt.Println("Got request to play " + args.Arg)
        for _, song := range getSongList() {
            if args.Arg == song {
                fmt.Println("Enqueued " + song)
                songQueue = append(songQueue, args.Arg)
                break
            }
        }

        // TODO: if no song is playing currently, reply to clients with the song to start seeding

        return nil
    })

    srv.Handle("leave", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        delete(peerMap, args.Ip)
        return nil
    })

    srv.Handle("peers", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        //fmt.Println("Handling peers ...")
        keys := make([]string, 0, len(peerMap))
        for k := range peerMap {
            keys = append(keys, k)
        }
        reply.Res = keys
        return nil
    })

    srv.Handle("ping", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        //fmt.Println("Handling ping from " + args.Ip)

        /*if currSong == "" && len(songQueue) > 0 {
            nextSong := songQueue[0]
            for _, song := range peerMap[args.Ip] {
                if song == nextSong {
                    currSong  = nextSong
                    songQueue = append(songQueue[:0], songQueue[1:]...)
                    reply.Res = song
                    break
                }
            }
        }*/

        // If no song is currently playing and there is a song ready to be seeded
        // TODO make currentlyplaying a global boolean and toggle it on and off in tracker's
        // play and done handlers respectively
        if !currentlyplaying && len(songQueue) > 0 {

          nextSong := songQueue[0]
          for _, song := range peerMap[args.Ip] {
              if song == nextSong {
                  currSong  = nextSong
                  fmt.Println("Contacting seeders to seed to peers ...")
                  client.Call("seedToPeers", proto.SeedToPeersPacket{currSong}, nil)
                  reply.Res = song
                  return nil
              }
          }
            fmt.Println("Contacting peers to begin listening for seeders ...")
          // Song not found, this peer needs to listen for seeders
          client.Call("listenForSeeders", proto.ListenForSeedersPacket{}, nil)
        }
        // TODO update livemap
        return nil
    })

    srv.Handle("done", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        // TODO: rpc for client to say song is done playing
        // currSong = ""
        return nil
    })

    ln, err := net.Listen("tcp", ":" + os.Args[1])
    if err != nil {
        log.Println(err)
    }

    fmt.Println("mob tracker listening on port: " + os.Args[1] + " ...")

    for {
        srv.Accept(ln)
    }
}

// TODO: maybe return unique song list
func getSongList() ([]string) {
    var songs []string

    keys := make([]string, 0, len(peerMap))
    for k := range peerMap {
        keys = append(keys, k)
    }

    for i := 0; i < len(keys); i++ {
        songs = append(songs, peerMap[keys[i]]...)
    }

    return songs
}
<|endoftext|>